sidebar.js

453 lines | 16.396 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 key in other ) {
      if ( other.hasOwnProperty(key) ) {
        var entries = other[key];
        if ( !entries ) continue;
        entries.forEach(function ( ann ){
          if ( !ann ) return;
          renderPair(panel, prefixedName(ann.identifier), 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);
        updateGraphInformation();
        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, "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);

    renderPair(panel, "rdfs:comment", property.commentForCurrentLanguage());
    renderPair(panel, "rdfs:label", property.descriptionForCurrentLanguage());

    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 renderAnnotationPairs( panel, annotationObject ){
    annotationObject = annotationObject || {};
    for ( var key in annotationObject ) {
      if ( annotationObject.hasOwnProperty(key) ) {
        var entries = annotationObject[key];
        if ( !entries ) continue;
        entries.forEach(function ( ann ){
          if ( !ann ) return;
          var label = prefixedName(ann.identifier);
          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, "Label", node.labelForCurrentLanguage());
    renderPair(panel, "Type", node.type());

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

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

    renderPair(panel, "rdfs:comment", node.commentForCurrentLanguage());
    renderPair(panel, "rdfs:label", node.descriptionForCurrentLanguage());

    renderAnnotationPairs(panel, node.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 (){
    // get it from graph.options
    var generalMetaObj = graph.options().getGeneralMetaObject();
    var preferredLanguage = graph && graph.language ? graph.language() : null;
    if ( generalMetaObj.hasOwnProperty("title") ) {
      // title has language to it -.-
      if ( typeof generalMetaObj.title === "object" ) {
        d3.select("#title").node().value = languageTools.textInLanguage(generalMetaObj.title, preferredLanguage);
      } else {
        d3.select("#title").node().innerHTML = generalMetaObj.title;
      }
      
    }
    if ( generalMetaObj.hasOwnProperty("iri") ) d3.select("#about").node().innerHTML = generalMetaObj.iri;
    if ( generalMetaObj.hasOwnProperty("iri") ) d3.select("#about").node().href = generalMetaObj.iri;
    if ( generalMetaObj.hasOwnProperty("version") ) d3.select("#version").node().innerHTML = generalMetaObj.version;
    if ( generalMetaObj.hasOwnProperty("author") ) d3.select("#authors").node().innerHTML = generalMetaObj.author;
    // this could also be an object >>
    if ( generalMetaObj.hasOwnProperty("description") ) {
      if ( typeof generalMetaObj.description === "object" ) {
        d3.select("#description").node().innerHTML = languageTools.textInLanguage(generalMetaObj.description, preferredLanguage);
      }
      else {
        d3.select("#description").node().innerHTML = generalMetaObj.description;
      }
    }
  };
  
  
  return sidebar;
};