var _ = require("lodash/core"); var math = require("./util/math")(); var linkCreator = require("./parsing/linkCreator")(); var elementTools = require("./util/elementTools")(); // add some maps for nodes and properties -- used for object generation var nodePrototypeMap = require("./elements/nodes/nodeMap")(); var propertyPrototypeMap = require("./elements/properties/propertyMap")(); module.exports = function ( graphContainerSelector ){ var graph = {}, CARDINALITY_HDISTANCE = 20, CARDINALITY_VDISTANCE = 10, curveFunction = d3.svg.line() .x(function ( d ){ return d.x; }) .y(function ( d ){ return d.y; }) .interpolate("cardinal"), options = require("./options")(), parser = require("./parser")(graph), language = "default", paused = false, // Container for visual elements graphContainer, nodeContainer, labelContainer, cardinalityContainer, linkContainer, // Visual elements nodeElements, initialLoad = true, updateRenderingDuringSimulation = false, labelGroupElements, linkGroups, linkPathElements, cardinalityElements, // Internal data classNodes, labelNodes, links, properties, unfilteredData, // Graph behaviour force, dragBehaviour, zoomFactor = 1.0, centerGraphViewOnLoad = false, transformAnimation = false, graphTranslation = [0, 0], graphUpdateRequired = false, pulseNodeIds = [], nodeArrayForPulse = [], nodeMap = [], locationId = 0, defaultZoom = 1.0, defaultTargetZoom = 0.8, global_dof = -1, touchDevice = false, last_touch_time, originalD3_dblClickFunction = null, originalD3_touchZoomFunction = null, // editing elements deleteGroupElement, addDataPropertyGroupElement, editContainer, draggerLayer = null, draggerObjectsArray = [], delayedHider, nodeFreezer, hoveredNodeElement = null, currentlySelectedNode = null, hoveredPropertyElement = null, draggingStarted = false, frozenDomainForPropertyDragger, frozenRangeForPropertyDragger, eP = 0, // id for new properties eN = 0, // id for new Nodes editMode = true, debugContainer = d3.select("#FPS_Statistics"), finishedLoadingSequence = false, ignoreOtherHoverEvents = false, forceNotZooming = false, now, then, // used for fps computation showFPS = false, seenEditorHint = false, seenFilterWarning = false, showFilterWarning = false, keepDetailsCollapsedOnLoading = true, adjustingGraphSize = false, showReloadButtonAfterLayoutOptimization = false, zoom; //var prefixModule=require("./prefixRepresentationModule")(graph); var NodePrototypeMap = createLowerCasePrototypeMap(nodePrototypeMap); var PropertyPrototypeMap = createLowerCasePrototypeMap(propertyPrototypeMap); var classDragger = require("./classDragger")(graph); var rangeDragger = require("./rangeDragger")(graph); var domainDragger = require("./domainDragger")(graph); var shadowClone = require("./shadowClone")(graph); graph.math = function (){ return math; }; /** --------------------------------------------------------- **/ /** -- getter and setter definitions -- **/ /** --------------------------------------------------------- **/ graph.isEditorMode = function (){ return editMode; }; graph.getGlobalDOF = function (){ return global_dof; }; graph.setGlobalDOF = function ( val ){ global_dof = val; }; graph.updateZoomSliderValueFromOutside = function (){ graph.options().zoomSlider().updateZoomSliderValue(zoomFactor); }; graph.setDefaultZoom = function ( val ){ defaultZoom = val; graph.reset(); graph.options().zoomSlider().updateZoomSliderValue(defaultZoom); }; graph.setTargetZoom = function ( val ){ defaultTargetZoom = val; }; graph.graphOptions = function (){ return options; }; graph.scaleFactor = function (){ return zoomFactor; }; graph.translation = function (){ return graphTranslation; }; // Returns the visible nodes graph.graphNodeElements = function (){ return nodeElements; }; // Returns the visible Label Nodes graph.graphLabelElements = function (){ return labelNodes; }; graph.graphLinkElements = function (){ return links; }; graph.setSliderZoom = function ( val ){ var cx = 0.5 * graph.options().width(); var cy = 0.5 * graph.options().height(); var cp = getWorldPosFromScreen(cx, cy, graphTranslation, zoomFactor); var sP = [cp.x, cp.y, graph.options().height() / zoomFactor]; var eP = [cp.x, cp.y, graph.options().height() / val]; var pos_intp = d3.interpolateZoom(sP, eP); graphContainer.attr("transform", transform(sP, cx, cy)) .transition() .duration(1) .attrTween("transform", function (){ return function ( t ){ return transform(pos_intp(t), cx, cy); }; }) .each("end", function (){ graphContainer.attr("transform", "translate(" + graphTranslation + ")scale(" + zoomFactor + ")"); zoom.translate(graphTranslation); zoom.scale(zoomFactor); graph.options().zoomSlider().updateZoomSliderValue(zoomFactor); }); }; graph.setZoom = function ( value ){ zoom.scale(value); }; graph.setTranslation = function ( translation ){ zoom.translate([translation[0], translation[1]]); }; graph.options = function (){ return options; }; // search functionality graph.getUpdateDictionary = function (){ return parser.getDictionary(); }; graph.language = function ( newLanguage ){ if ( !arguments.length ) return language; // Just update if the language changes if ( language !== newLanguage ) { language = newLanguage || "default"; redrawContent(); recalculatePositions(); graph.options().searchMenu().requestDictionaryUpdate(); graph.resetSearchHighlight(); } return graph; }; /** --------------------------------------------------------- **/ /** graph / rendering related functions **/ /** --------------------------------------------------------- **/ // Initializes the graph. function initializeGraph(){ options.graphContainerSelector(graphContainerSelector); var moved = false; force = d3.layout.force() .on("tick", hiddenRecalculatePositions); dragBehaviour = d3.behavior.drag() .origin(function ( d ){ return d; }) .on("dragstart", function ( d ){ d3.event.sourceEvent.stopPropagation(); // Prevent panning graph.ignoreOtherHoverEvents(true); if ( d.type && d.type() === "Class_dragger" ) { classDragger.mouseButtonPressed = true; clearTimeout(delayedHider); classDragger.selectedViaTouch(true); d.parentNode().locked(true); draggingStarted = true; } else if ( d.type && d.type() === "Range_dragger" ) { graph.ignoreOtherHoverEvents(true); clearTimeout(delayedHider); frozenDomainForPropertyDragger = shadowClone.parentNode().domain(); frozenRangeForPropertyDragger = shadowClone.parentNode().range(); shadowClone.setInitialPosition(); shadowClone.hideClone(false); shadowClone.hideParentProperty(true); shadowClone.updateElement(); deleteGroupElement.classed("hidden", true); addDataPropertyGroupElement.classed("hidden", true); frozenDomainForPropertyDragger.frozen(true); frozenDomainForPropertyDragger.locked(true); frozenRangeForPropertyDragger.frozen(true); frozenRangeForPropertyDragger.locked(true); domainDragger.updateElement(); domainDragger.mouseButtonPressed = true; rangeDragger.updateElement(); rangeDragger.mouseButtonPressed = true; // shadowClone.setPosition(d.x, d.y); } else if ( d.type && d.type() === "Domain_dragger" ) { graph.ignoreOtherHoverEvents(true); clearTimeout(delayedHider); frozenDomainForPropertyDragger = shadowClone.parentNode().domain(); frozenRangeForPropertyDragger = shadowClone.parentNode().range(); shadowClone.setInitialPosition(); shadowClone.hideClone(false); shadowClone.hideParentProperty(true); shadowClone.updateElement(); deleteGroupElement.classed("hidden", true); addDataPropertyGroupElement.classed("hidden", true); frozenDomainForPropertyDragger.frozen(true); frozenDomainForPropertyDragger.locked(true); frozenRangeForPropertyDragger.frozen(true); frozenRangeForPropertyDragger.locked(true); domainDragger.updateElement(); domainDragger.mouseButtonPressed = true; rangeDragger.updateElement(); rangeDragger.mouseButtonPressed = true; } else { d.locked(true); moved = false; } }) .on("drag", function ( d ){ if ( d.type && d.type() === "Class_dragger" ) { clearTimeout(delayedHider); classDragger.setPosition(d3.event.x, d3.event.y); } else if ( d.type && d.type() === "Range_dragger" ) { clearTimeout(delayedHider); rangeDragger.setPosition(d3.event.x, d3.event.y); shadowClone.setPosition(d3.event.x, d3.event.y); domainDragger.updateElementViaRangeDragger(d3.event.x, d3.event.y); } else if ( d.type && d.type() === "Domain_dragger" ) { clearTimeout(delayedHider); domainDragger.setPosition(d3.event.x, d3.event.y); shadowClone.setPositionDomain(d3.event.x, d3.event.y); rangeDragger.updateElementViaDomainDragger(d3.event.x, d3.event.y); } else { d.px = d3.event.x; d.py = d3.event.y; force.resume(); updateHaloRadius(); moved = true; if ( d.renderType && d.renderType() === "round" ) { classDragger.setParentNode(d); } } }) .on("dragend", function ( d ){ graph.ignoreOtherHoverEvents(false); if ( d.type && d.type() === "Class_dragger" ) { var nX = classDragger.x; var nY = classDragger.y; clearTimeout(delayedHider); classDragger.mouseButtonPressed = false; classDragger.selectedViaTouch(false); d.setParentNode(d.parentNode()); var draggerEndPos = [nX, nY]; var targetNode = graph.getTargetNode(draggerEndPos); if ( targetNode ) { createNewObjectProperty(d.parentNode(), targetNode, draggerEndPos); } if ( touchDevice === false ) { editElementHoverOut(); } draggingStarted = false; } else if ( d.type && d.type() === "Range_dragger" ) { graph.ignoreOtherHoverEvents(false); frozenDomainForPropertyDragger.frozen(false); frozenDomainForPropertyDragger.locked(false); frozenRangeForPropertyDragger.frozen(false); frozenRangeForPropertyDragger.locked(false); rangeDragger.mouseButtonPressed = false; domainDragger.mouseButtonPressed = false; domainDragger.updateElement(); rangeDragger.updateElement(); shadowClone.hideClone(true); var rX = rangeDragger.x; var rY = rangeDragger.y; var rangeDraggerEndPos = [rX, rY]; var targetRangeNode = graph.getTargetNode(rangeDraggerEndPos); if ( elementTools.isDatatype(targetRangeNode) === true ) { targetRangeNode = null; console.log("---------------TARGET NODE IS A DATATYPE/ LITERAL ------------"); } if ( targetRangeNode === null ) { d.reDrawEverthing(); shadowClone.hideParentProperty(false); } else { d.updateRange(targetRangeNode); graph.update(); shadowClone.hideParentProperty(false); } } else if ( d.type && d.type() === "Domain_dragger" ) { graph.ignoreOtherHoverEvents(false); frozenDomainForPropertyDragger.frozen(false); frozenDomainForPropertyDragger.locked(false); frozenRangeForPropertyDragger.frozen(false); frozenRangeForPropertyDragger.locked(false); rangeDragger.mouseButtonPressed = false; domainDragger.mouseButtonPressed = false; domainDragger.updateElement(); rangeDragger.updateElement(); shadowClone.hideClone(true); var dX = domainDragger.x; var dY = domainDragger.y; var domainDraggerEndPos = [dX, dY]; var targetDomainNode = graph.getTargetNode(domainDraggerEndPos); if ( elementTools.isDatatype(targetDomainNode) === true ) { targetDomainNode = null; console.log("---------------TARGET NODE IS A DATATYPE/ LITERAL ------------"); } shadowClone.hideClone(true); if ( targetDomainNode === null ) { d.reDrawEverthing(); shadowClone.hideParentProperty(false); } else { d.updateDomain(targetDomainNode); graph.update(); shadowClone.hideParentProperty(false); } } else { d.locked(false); var pnp = graph.options().pickAndPinModule(); if ( pnp.enabled() === true && moved === true ) { if ( d.id ) { // node pnp.handle(d, true); } if ( d.property ) { pnp.handle(d.property(), true); } } } }); // Apply the zooming factor. zoom = d3.behavior.zoom() .duration(150) .scaleExtent([options.minMagnification(), options.maxMagnification()]) .on("zoom", zoomed); draggerObjectsArray.push(classDragger); draggerObjectsArray.push(rangeDragger); draggerObjectsArray.push(domainDragger); draggerObjectsArray.push(shadowClone); force.stop(); } graph.lazyRefresh = function (){ redrawContent(); recalculatePositions(); }; graph.adjustingGraphSize = function ( val ){ adjustingGraphSize = val; }; graph.showReloadButtonAfterLayoutOptimization = function ( show ){ showReloadButtonAfterLayoutOptimization = show; }; function hiddenRecalculatePositions(){ finishedLoadingSequence = false; if ( graph.options().loadingModule().successfullyLoadedOntology() === false ) { force.stop(); d3.select("#progressBarValue").node().innerHTML = ""; graph.updateProgressBarMode(); graph.options().loadingModule().showErrorDetailsMessage(hiddenRecalculatePositions); if ( keepDetailsCollapsedOnLoading && adjustingGraphSize === false ) { graph.options().loadingModule().collapseDetails("hiddenRecalculatePositions"); } return; } if ( updateRenderingDuringSimulation === false ) { var value = 1.0 - 10 * force.alpha(); var percent = parseInt(200 * value) + "%"; graph.options().loadingModule().setPercentValue(percent); d3.select("#progressBarValue").style("width", percent); d3.select("#progressBarValue").node().innerHTML = percent; if ( value > 0.49 ) { updateRenderingDuringSimulation = true; // show graph container; if ( graphContainer ) { graphContainer.style("opacity", "1"); percent = "100%"; d3.select("#progressBarValue").style("width", percent); d3.select("#progressBarValue").node().innerHTML = percent; graph.options().ontologyMenu().append_message_toLastBulletPoint("done"); d3.select("#reloadCachedOntology").classed("hidden", !showReloadButtonAfterLayoutOptimization); if ( showFilterWarning === true && seenFilterWarning === false ) { graph.options().warningModule().showFilterHint(); seenFilterWarning = true; } } if ( initialLoad ) { if ( graph.paused() === false ) force.resume(); // resume force initialLoad = false; } finishedLoadingSequence = true; if ( showFPS === true ) { force.on("tick", recalculatePositionsWithFPS); recalculatePositionsWithFPS(); } else { force.on("tick", recalculatePositions); recalculatePositions(); } if ( centerGraphViewOnLoad === true && force.nodes().length > 0 ) { if ( force.nodes().length < 10 ) graph.forceRelocationEvent(true); // uses dynamic zoomer; else graph.forceRelocationEvent(); centerGraphViewOnLoad = false; // console.log("--------------------------------------") } graph.showEditorHintIfNeeded(); if ( graph.options().loadingModule().missingImportsWarning() === false ) { graph.options().loadingModule().hideLoadingIndicator(); graph.options().ontologyMenu().append_bulletPoint("Successfully loaded ontology"); graph.options().loadingModule().setSuccessful(); } else { graph.options().loadingModule().showWarningDetailsMessage(); graph.options().ontologyMenu().append_bulletPoint("Loaded ontology with warnings"); } } } } graph.showEditorHintIfNeeded = function (){ if ( seenEditorHint === false && editMode === true ) { seenEditorHint = true; graph.options().warningModule().showEditorHint(); } }; graph.setForceTickFunctionWithFPS = function (){ showFPS = true; if ( force && finishedLoadingSequence === true ) { force.on("tick", recalculatePositionsWithFPS); } }; graph.setDefaultForceTickFunction = function (){ showFPS = false; if ( force && finishedLoadingSequence === true ) { force.on("tick", recalculatePositions); } }; function recalculatePositionsWithFPS(){ // compute the fps recalculatePositions(); now = Date.now(); var diff = now - then; var fps = (1000 / (diff)).toFixed(2); debugContainer.node().innerHTML = "FPS: " + fps + "
" + "Nodes: " + force.nodes().length + "
" + "Links: " + force.links().length; then = Date.now(); } function recalculatePositions(){ // Set node positions // add switch for edit mode to make this faster; if ( !editMode ) { nodeElements.attr("transform", function ( node ){ return "translate(" + node.x + "," + node.y + ")"; }); // Set label group positions labelGroupElements.attr("transform", function ( label ){ var position; // force centered positions on single-layered links var link = label.link(); if ( link.layers().length === 1 && !link.loops() ) { var linkDomainIntersection = math.calculateIntersection(link.range(), link.domain(), 0); var linkRangeIntersection = math.calculateIntersection(link.domain(), link.range(), 0); position = math.calculateCenter(linkDomainIntersection, linkRangeIntersection); label.x = position.x; label.y = position.y; } return "translate(" + label.x + "," + label.y + ")"; }); // Set link paths and calculate additional information linkPathElements.attr("d", function ( l ){ if ( l.isLoop() ) { return math.calculateLoopPath(l); } var curvePoint = l.label(); var pathStart = math.calculateIntersection(curvePoint, l.domain(), 1); var pathEnd = math.calculateIntersection(curvePoint, l.range(), 1); return curveFunction([pathStart, curvePoint, pathEnd]); }); // Set cardinality positions cardinalityElements.attr("transform", function ( property ){ var label = property.link().label(), pos = math.calculateIntersection(label, property.range(), CARDINALITY_HDISTANCE), normalV = math.calculateNormalVector(label, property.range(), CARDINALITY_VDISTANCE); return "translate(" + (pos.x + normalV.x) + "," + (pos.y + normalV.y) + ")"; }); updateHaloRadius(); return; } // TODO: this is Editor redraw function // we need to make this faster!! nodeElements.attr("transform", function ( node ){ return "translate(" + node.x + "," + node.y + ")"; }); // Set label group positions labelGroupElements.attr("transform", function ( label ){ var position; // force centered positions on single-layered links var link = label.link(); if ( link.layers().length === 1 && !link.loops() ) { var linkDomainIntersection = math.calculateIntersection(link.range(), link.domain(), 0); var linkRangeIntersection = math.calculateIntersection(link.domain(), link.range(), 0); position = math.calculateCenter(linkDomainIntersection, linkRangeIntersection); label.x = position.x; label.y = position.y; label.linkRangeIntersection = linkRangeIntersection; label.linkDomainIntersection = linkDomainIntersection; if ( link.property().focused() === true || hoveredPropertyElement !== undefined ) { rangeDragger.updateElement(); domainDragger.updateElement(); // shadowClone.setPosition(link.property().range().x,link.property().range().y); // shadowClone.setPositionDomain(link.property().domain().x,link.property().domain().y); } } else { label.linkDomainIntersection = math.calculateIntersection(link.label(), link.domain(), 0); label.linkRangeIntersection = math.calculateIntersection(link.label(), link.range(), 0); if ( link.property().focused() === true || hoveredPropertyElement !== undefined ) { rangeDragger.updateElement(); domainDragger.updateElement(); // shadowClone.setPosition(link.property().range().x,link.property().range().y); // shadowClone.setPositionDomain(link.property().domain().x,link.property().domain().y); } } return "translate(" + label.x + "," + label.y + ")"; }); // Set link paths and calculate additional information linkPathElements.attr("d", function ( l ){ if ( l.isLoop() ) { var ptrAr = math.getLoopPoints(l); l.label().linkRangeIntersection = ptrAr[1]; l.label().linkDomainIntersection = ptrAr[0]; if ( l.property().focused() === true || hoveredPropertyElement !== undefined ) { rangeDragger.updateElement(); domainDragger.updateElement(); } return math.calculateLoopPath(l); } var curvePoint = l.label(); var pathStart = math.calculateIntersection(curvePoint, l.domain(), 1); var pathEnd = math.calculateIntersection(curvePoint, l.range(), 1); l.linkRangeIntersection = pathStart; l.linkDomainIntersection = pathEnd; if ( l.property().focused() === true || hoveredPropertyElement !== undefined ) { domainDragger.updateElement(); rangeDragger.updateElement(); // shadowClone.setPosition(l.property().range().x,l.property().range().y); // shadowClone.setPositionDomain(l.property().domain().x,l.property().domain().y); } return curveFunction([pathStart, curvePoint, pathEnd]); }); // Set cardinality positions cardinalityElements.attr("transform", function ( property ){ var label = property.link().label(), pos = math.calculateIntersection(label, property.range(), CARDINALITY_HDISTANCE), normalV = math.calculateNormalVector(label, property.range(), CARDINALITY_VDISTANCE); return "translate(" + (pos.x + normalV.x) + "," + (pos.y + normalV.y) + ")"; }); if ( hoveredNodeElement ) { setDeleteHoverElementPosition(hoveredNodeElement); setAddDataPropertyHoverElementPosition(hoveredNodeElement); if ( draggingStarted === false ) { classDragger.setParentNode(hoveredNodeElement); } } if ( hoveredPropertyElement ) { setDeleteHoverElementPositionProperty(hoveredPropertyElement); } updateHaloRadius(); } graph.updatePropertyDraggerElements = function ( property ){ if ( property.type() !== "owl:DatatypeProperty" ) { shadowClone.setParentProperty(property); rangeDragger.setParentProperty(property); rangeDragger.hideDragger(false); rangeDragger.addMouseEvents(); domainDragger.setParentProperty(property); domainDragger.hideDragger(false); domainDragger.addMouseEvents(); } else { rangeDragger.hideDragger(true); domainDragger.hideDragger(true); shadowClone.hideClone(true); } }; function addClickEvents(){ function executeModules( selectedElement ){ options.selectionModules().forEach(function ( module ){ module.handle(selectedElement); }); } nodeElements.on("click", function ( clickedNode ){ // manaual double clicker // helper for iphone 6 etc... if ( touchDevice === true && doubletap() === true ) { d3.event.stopPropagation(); if ( editMode === true ) { clickedNode.raiseDoubleClickEdit(defaultIriValue(clickedNode)); } } else { executeModules(clickedNode); } }); nodeElements.on("dblclick", function ( clickedNode ){ d3.event.stopPropagation(); if ( editMode === true ) { clickedNode.raiseDoubleClickEdit(defaultIriValue(clickedNode)); } }); labelGroupElements.selectAll(".label").on("click", function ( clickedProperty ){ executeModules(clickedProperty); // this is for enviroments that do not define dblClick function; if ( touchDevice === true && doubletap() === true ) { d3.event.stopPropagation(); if ( editMode === true ) { clickedProperty.raiseDoubleClickEdit(defaultIriValue(clickedProperty)); } } // currently removed the selection of an element to invoke the dragger // if (editMode===true && clickedProperty.editingTextElement!==true) { // return; // // We say that Datatype properties are not allowed to have domain range draggers // if (clickedProperty.focused() && clickedProperty.type() !== "owl:DatatypeProperty") { // shadowClone.setParentProperty(clickedProperty); // rangeDragger.setParentProperty(clickedProperty); // rangeDragger.hideDragger(false); // rangeDragger.addMouseEvents(); // domainDragger.setParentProperty(clickedProperty); // domainDragger.hideDragger(false); // domainDragger.addMouseEvents(); // // if (clickedProperty.domain()===clickedProperty.range()){ // clickedProperty.labelObject().increasedLoopAngle=true; // recalculatePositions(); // // } // // } else if (clickedProperty.focused() && clickedProperty.type() === "owl:DatatypeProperty") { // shadowClone.setParentProperty(clickedProperty); // rangeDragger.setParentProperty(clickedProperty); // rangeDragger.hideDragger(true); // rangeDragger.addMouseEvents(); // domainDragger.setParentProperty(clickedProperty); // domainDragger.hideDragger(false); // domainDragger.addMouseEvents(); // // } // else { // rangeDragger.hideDragger(true); // domainDragger.hideDragger(true); // if (clickedProperty.domain()===clickedProperty.range()){ // clickedProperty.labelObject().increasedLoopAngle=false; // recalculatePositions(); // // } // } // } }); labelGroupElements.selectAll(".label").on("dblclick", function ( clickedProperty ){ d3.event.stopPropagation(); if ( editMode === true ) { clickedProperty.raiseDoubleClickEdit(defaultIriValue(clickedProperty)); } }); } function defaultIriValue( element ){ // get the iri of that element; if ( graph.options().getGeneralMetaObject().iri ) { var str2Compare = graph.options().getGeneralMetaObject().iri + element.id(); return element.iri() === str2Compare; } return false; } /** Adjusts the containers current scale and position. */ function zoomed(){ if ( forceNotZooming === true ) { zoom.translate(graphTranslation); zoom.scale(zoomFactor); return; } var zoomEventByMWheel = false; if ( d3.event.sourceEvent ) { if ( d3.event.sourceEvent.deltaY ) zoomEventByMWheel = true; } if ( zoomEventByMWheel === false ) { if ( transformAnimation === true ) { return; } zoomFactor = d3.event.scale; graphTranslation = d3.event.translate; graphContainer.attr("transform", "translate(" + graphTranslation + ")scale(" + zoomFactor + ")"); updateHaloRadius(); graph.options().zoomSlider().updateZoomSliderValue(zoomFactor); return; } /** animate the transition **/ zoomFactor = d3.event.scale; graphTranslation = d3.event.translate; graphContainer.transition() .tween("attr.translate", function (){ return function ( t ){ transformAnimation = true; var tr = d3.transform(graphContainer.attr("transform")); graphTranslation[0] = tr.translate[0]; graphTranslation[1] = tr.translate[1]; zoomFactor = tr.scale[0]; updateHaloRadius(); graph.options().zoomSlider().updateZoomSliderValue(zoomFactor); }; }) .each("end", function (){ transformAnimation = false; }) .attr("transform", "translate(" + graphTranslation + ")scale(" + zoomFactor + ")") .ease('linear') .duration(250); }// end of zoomed function function redrawGraph(){ remove(); graphContainer = d3.selectAll(options.graphContainerSelector()) .append("svg") .classed("vowlGraph", true) .attr("width", options.width()) .attr("height", options.height()) .call(zoom) .append("g"); // add touch and double click functions var svgGraph = d3.selectAll(".vowlGraph"); originalD3_dblClickFunction = svgGraph.on("dblclick.zoom"); originalD3_touchZoomFunction = svgGraph.on("touchstart"); svgGraph.on("touchstart", touchzoomed); if ( editMode === true ) { svgGraph.on("dblclick.zoom", graph.modified_dblClickFunction); } else { svgGraph.on("dblclick.zoom", originalD3_dblClickFunction); } } function generateEditElements(){ addDataPropertyGroupElement = editContainer.append('g') .classed("hidden-in-export", true) .classed("hidden", true) .classed("addDataPropertyElement", true) .attr("transform", "translate(" + 0 + "," + 0 + ")"); addDataPropertyGroupElement.append("circle") // .classed("deleteElement", true) .attr("r", 12) .attr("cx", 0) .attr("cy", 0) .append("title").text("Add Datatype Property"); addDataPropertyGroupElement.append("line") // .classed("deleteElementIcon ",true) .attr("x1", -8) .attr("y1", 0) .attr("x2", 8) .attr("y2", 0) .append("title").text("Add Datatype Property"); addDataPropertyGroupElement.append("line") // .classed("deleteElementIcon",true) .attr("x1", 0) .attr("y1", -8) .attr("x2", 0) .attr("y2", 8) .append("title").text("Add Datatype Property"); if ( graph.options().useAccuracyHelper() ) { addDataPropertyGroupElement.append("circle") .attr("r", 15) .attr("cx", -7) .attr("cy", 7) .classed("superHiddenElement", true) .classed("superOpacityElement", !graph.options().showDraggerObject()); } deleteGroupElement = editContainer.append('g') .classed("hidden-in-export", true) .classed("hidden", true) .classed("deleteParentElement", true) .attr("transform", "translate(" + 0 + "," + 0 + ")"); deleteGroupElement.append("circle") .attr("r", 12) .attr("cx", 0) .attr("cy", 0) .append("title").text("Delete This Node"); var crossLen = 5; deleteGroupElement.append("line") .attr("x1", -crossLen) .attr("y1", -crossLen) .attr("x2", crossLen) .attr("y2", crossLen) .append("title").text("Delete This Node"); deleteGroupElement.append("line") .attr("x1", crossLen) .attr("y1", -crossLen) .attr("x2", -crossLen) .attr("y2", crossLen) .append("title").text("Delete This Node"); if ( graph.options().useAccuracyHelper() ) { deleteGroupElement.append("circle") .attr("r", 15) .attr("cx", 7) .attr("cy", -7) .classed("superHiddenElement", true) .classed("superOpacityElement", !graph.options().showDraggerObject()); } } graph.getUnfilteredData = function (){ return unfilteredData; }; graph.getClassDataForTtlExport = function (){ var allNodes = unfilteredData.nodes; var nodeData = []; for ( var i = 0; i < allNodes.length; i++ ) { if ( allNodes[i].type() !== "rdfs:Literal" && allNodes[i].type() !== "rdfs:Datatype" && allNodes[i].type() !== "owl:Thing" ) { nodeData.push(allNodes[i]); } } return nodeData; }; graph.getPropertyDataForTtlExport = function (){ var propertyData = []; var allProperties = unfilteredData.properties; for ( var i = 0; i < allProperties.length; i++ ) { // currently using only the object properties if ( allProperties[i].type() === "owl:ObjectProperty" || allProperties[i].type() === "owl:DatatypeProperty" || allProperties[i].type() === "owl:ObjectProperty" ) { propertyData.push(allProperties[i]); } else { if ( allProperties[i].type() === "rdfs:subClassOf" ) { allProperties[i].baseIri("http://www.w3.org/2000/01/rdf-schema#"); allProperties[i].iri("http://www.w3.org/2000/01/rdf-schema#subClassOf"); } if ( allProperties[i].type() === "owl:disjointWith" ) { allProperties[i].baseIri("http://www.w3.org/2002/07/owl#"); allProperties[i].iri("http://www.w3.org/2002/07/owl#disjointWith"); } } } return propertyData; }; graph.getAxiomsForTtlExport = function (){ var axioms = []; var allProperties = unfilteredData.properties; for ( var i = 0; i < allProperties.length; i++ ) { // currently using only the object properties if ( allProperties[i].type() === "owl:ObjectProperty" || allProperties[i].type() === "owl:DatatypeProperty" || allProperties[i].type() === "owl:ObjectProperty" || allProperties[i].type() === "rdfs:subClassOf" ) { } else { } } return axioms; }; graph.getUnfilteredData = function (){ return unfilteredData; }; graph.getClassDataForTtlExport = function (){ var allNodes = unfilteredData.nodes; var nodeData = []; for ( var i = 0; i < allNodes.length; i++ ) { if ( allNodes[i].type() !== "rdfs:Literal" && allNodes[i].type() !== "rdfs:Datatype" && allNodes[i].type() !== "owl:Thing" ) { nodeData.push(allNodes[i]); } } return nodeData; }; function redrawContent(){ var markerContainer; if ( !graphContainer ) { return; } // Empty the graph container graphContainer.selectAll("*").remove(); // Last container -> elements of this container overlap others linkContainer = graphContainer.append("g").classed("linkContainer", true); cardinalityContainer = graphContainer.append("g").classed("cardinalityContainer", true); labelContainer = graphContainer.append("g").classed("labelContainer", true); nodeContainer = graphContainer.append("g").classed("nodeContainer", true); // adding editing Elements var draggerPathLayer = graphContainer.append("g").classed("linkContainer", true); draggerLayer = graphContainer.append("g").classed("editContainer", true); editContainer = graphContainer.append("g").classed("editContainer", true); draggerPathLayer.classed("hidden-in-export", true); editContainer.classed("hidden-in-export", true); draggerLayer.classed("hidden-in-export", true); // Add an extra container for all markers markerContainer = linkContainer.append("defs"); var drElement = draggerLayer.selectAll(".node") .data(draggerObjectsArray).enter() .append("g") .classed("node", true) .classed("hidden-in-export", true) .attr("id", function ( d ){ return d.id(); }) .call(dragBehaviour); drElement.each(function ( node ){ node.svgRoot(d3.select(this)); node.svgPathLayer(draggerPathLayer); if ( node.type() === "shadowClone" ) { node.drawClone(); node.hideClone(true); } else { node.drawNode(); node.hideDragger(true); } }); generateEditElements(); // Add an extra container for all markers markerContainer = linkContainer.append("defs"); // Draw nodes if ( classNodes === undefined ) classNodes = []; nodeElements = nodeContainer.selectAll(".node") .data(classNodes).enter() .append("g") .classed("node", true) .attr("id", function ( d ){ return d.id(); }) .call(dragBehaviour); nodeElements.each(function ( node ){ node.draw(d3.select(this)); }); if ( labelNodes === undefined ) labelNodes = []; // Draw label groups (property + inverse) labelGroupElements = labelContainer.selectAll(".labelGroup") .data(labelNodes).enter() .append("g") .classed("labelGroup", true) .call(dragBehaviour); labelGroupElements.each(function ( label ){ var success = label.draw(d3.select(this)); label.property().labelObject(label); // Remove empty groups without a label. if ( !success ) { d3.select(this).remove(); } }); // Place subclass label groups on the bottom of all labels labelGroupElements.each(function ( label ){ // the label might be hidden e.g. in compact notation if ( !this.parentNode ) { return; } if ( elementTools.isRdfsSubClassOf(label.property()) ) { var parentNode = this.parentNode; parentNode.insertBefore(this, parentNode.firstChild); } }); if ( properties === undefined ) properties = []; // Draw cardinality elements cardinalityElements = cardinalityContainer.selectAll(".cardinality") .data(properties).enter() .append("g") .classed("cardinality", true); cardinalityElements.each(function ( property ){ var success = property.drawCardinality(d3.select(this)); // Remove empty groups without a label. if ( !success ) { d3.select(this).remove(); } }); // Draw links if ( links === undefined ) links = []; linkGroups = linkContainer.selectAll(".link") .data(links).enter() .append("g") .classed("link", true); linkGroups.each(function ( link ){ link.draw(d3.select(this), markerContainer); }); linkPathElements = linkGroups.selectAll("path"); // Select the path for direct access to receive a better performance addClickEvents(); } function remove(){ if ( graphContainer ) { // Select the parent element because the graph container is a group (e.g. for zooming) d3.select(graphContainer.node().parentNode).remove(); } } initializeGraph(); // << call the initialization function graph.updateCanvasContainerSize = function (){ if ( graphContainer ) { var svgElement = d3.selectAll(".vowlGraph"); svgElement.attr("width", options.width()); svgElement.attr("height", options.height()); graphContainer.attr("transform", "translate(" + graphTranslation + ")scale(" + zoomFactor + ")"); } }; // Loads all settings, removes the old graph (if it exists) and draws a new one. graph.start = function (){ force.stop(); loadGraphData(true); redrawGraph(); graph.update(true); if ( graph.options().loadingModule().successfullyLoadedOntology() === false ) { graph.options().loadingModule().setErrorMode(); } }; // Updates only the style of the graph. graph.updateStyle = function (){ refreshGraphStyle(); if ( graph.options().loadingModule().successfullyLoadedOntology() === false ) { force.stop(); } else { force.start(); } }; graph.reload = function (){ loadGraphData(); graph.update(); }; graph.load = function (){ force.stop(); loadGraphData(); refreshGraphData(); for ( var i = 0; i < labelNodes.length; i++ ) { var label = labelNodes[i]; if ( label.property().x && label.property().y ) { label.x = label.property().x; label.y = label.property().y; // also set the prev position of the label label.px = label.x; label.py = label.y; } } graph.update(); }; graph.fastUpdate = function (){ // fast update function for editor calls; // -- experimental ; quick_refreshGraphData(); updateNodeMap(); force.start(); redrawContent(); graph.updatePulseIds(nodeArrayForPulse); refreshGraphStyle(); updateHaloStyles(); }; graph.getNodeMapForSearch = function (){ return nodeMap; }; function updateNodeMap(){ nodeMap = []; var node; for ( var j = 0; j < force.nodes().length; j++ ) { node = force.nodes()[j]; if ( node.id ) { nodeMap[node.id()] = j; // check for equivalents var eqs = node.equivalents(); if ( eqs.length > 0 ) { for ( var e = 0; e < eqs.length; e++ ) { var eqObject = eqs[e]; nodeMap[eqObject.id()] = j; } } } if ( node.property ) { nodeMap[node.property().id()] = j; var inverse = node.inverse(); if ( inverse ) { nodeMap[inverse.id()] = j; } } } } function updateHaloStyles(){ var haloElement; var halo; var node; for ( var j = 0; j < force.nodes().length; j++ ) { node = force.nodes()[j]; if ( node.id ) { haloElement = node.getHalos(); if ( haloElement ) { halo = haloElement.selectAll(".searchResultA"); halo.classed("searchResultA", false); halo.classed("searchResultB", true); } } if ( node.property ) { haloElement = node.property().getHalos(); if ( haloElement ) { halo = haloElement.selectAll(".searchResultA"); halo.classed("searchResultA", false); halo.classed("searchResultB", true); } } } } // Updates the graphs displayed data and style. graph.update = function ( init ){ var validOntology = graph.options().loadingModule().successfullyLoadedOntology(); if ( validOntology === false && (init && init === true) ) { graph.options().loadingModule().collapseDetails(); return; } if ( validOntology === false ) { return; } keepDetailsCollapsedOnLoading = false; refreshGraphData(); // update node map updateNodeMap(); force.start(); redrawContent(); graph.updatePulseIds(nodeArrayForPulse); refreshGraphStyle(); updateHaloStyles(); }; graph.paused = function ( p ){ if ( !arguments.length ) return paused; paused = p; graph.updateStyle(); return graph; }; // resetting the graph graph.reset = function (){ // window size var w = 0.5 * graph.options().width(); var h = 0.5 * graph.options().height(); // computing initial translation for the graph due tue the dynamic default zoom level var tx = w - defaultZoom * w; var ty = h - defaultZoom * h; zoom.translate([tx, ty]) .scale(defaultZoom); }; graph.zoomOut = function (){ var minMag = options.minMagnification(), maxMag = options.maxMagnification(); var stepSize = (maxMag - minMag) / 10; var val = zoomFactor - stepSize; if ( val < minMag ) val = minMag; var cx = 0.5 * graph.options().width(); var cy = 0.5 * graph.options().height(); var cp = getWorldPosFromScreen(cx, cy, graphTranslation, zoomFactor); var sP = [cp.x, cp.y, graph.options().height() / zoomFactor]; var eP = [cp.x, cp.y, graph.options().height() / val]; var pos_intp = d3.interpolateZoom(sP, eP); graphContainer.attr("transform", transform(sP, cx, cy)) .transition() .duration(250) .attrTween("transform", function (){ return function ( t ){ return transform(pos_intp(t), cx, cy); }; }) .each("end", function (){ graphContainer.attr("transform", "translate(" + graphTranslation + ")scale(" + zoomFactor + ")"); zoom.translate(graphTranslation); zoom.scale(zoomFactor); updateHaloRadius(); options.zoomSlider().updateZoomSliderValue(zoomFactor); }); }; graph.zoomIn = function (){ var minMag = options.minMagnification(), maxMag = options.maxMagnification(); var stepSize = (maxMag - minMag) / 10; var val = zoomFactor + stepSize; if ( val > maxMag ) val = maxMag; var cx = 0.5 * graph.options().width(); var cy = 0.5 * graph.options().height(); var cp = getWorldPosFromScreen(cx, cy, graphTranslation, zoomFactor); var sP = [cp.x, cp.y, graph.options().height() / zoomFactor]; var eP = [cp.x, cp.y, graph.options().height() / val]; var pos_intp = d3.interpolateZoom(sP, eP); graphContainer.attr("transform", transform(sP, cx, cy)) .transition() .duration(250) .attrTween("transform", function (){ return function ( t ){ return transform(pos_intp(t), cx, cy); }; }) .each("end", function (){ graphContainer.attr("transform", "translate(" + graphTranslation + ")scale(" + zoomFactor + ")"); zoom.translate(graphTranslation); zoom.scale(zoomFactor); updateHaloRadius(); options.zoomSlider().updateZoomSliderValue(zoomFactor); }); }; /** --------------------------------------------------------- **/ /** -- data related handling -- **/ /** --------------------------------------------------------- **/ var cachedJsonOBJ = null; graph.clearAllGraphData = function (){ if ( graph.graphNodeElements() && graph.graphNodeElements().length > 0 ) { cachedJsonOBJ = graph.options().exportMenu().createJSON_exportObject(); } else { cachedJsonOBJ = null; } force.stop(); if ( unfilteredData ) { unfilteredData.nodes = []; unfilteredData.properties = []; } }; graph.getCachedJsonObj = function (){ return cachedJsonOBJ; }; // removes data when data could not be loaded graph.clearGraphData = function (){ force.stop(); var sidebar = graph.options().sidebar(); if ( sidebar ) sidebar.clearOntologyInformation(); if ( graphContainer ) redrawGraph(); }; function generateDictionary( data ){ var i; var originalDictionary = []; var nodes = data.nodes; for ( i = 0; i < nodes.length; i++ ) { // check if node has a label if ( nodes[i].labelForCurrentLanguage() !== undefined ) originalDictionary.push(nodes[i]); } var props = data.properties; for ( i = 0; i < props.length; i++ ) { if ( props[i].labelForCurrentLanguage() !== undefined ) originalDictionary.push(props[i]); } parser.setDictionary(originalDictionary); var literFilter = graph.options().literalFilter(); var idsToRemove = literFilter.removedNodes(); var originalDict = parser.getDictionary(); var newDict = []; // go through the dictionary and remove the ids; for ( i = 0; i < originalDict.length; i++ ) { var dictElement = originalDict[i]; var dictElementId; if ( dictElement.property ) dictElementId = dictElement.property().id(); else dictElementId = dictElement.id(); // compare against the removed ids; var addToDictionary = true; for ( var j = 0; j < idsToRemove.length; j++ ) { var currentId = idsToRemove[j]; if ( currentId === dictElementId ) { addToDictionary = false; } } if ( addToDictionary === true ) { newDict.push(dictElement); } } // tell the parser that the dictionary is updated parser.setDictionary(newDict); } graph.updateProgressBarMode = function (){ var loadingModule = graph.options().loadingModule(); var state = loadingModule.getProgressBarMode(); switch ( state ) { case 0: loadingModule.setErrorMode(); break; case 1: loadingModule.setBusyMode(); break; case 2: loadingModule.setPercentMode(); break; default: loadingModule.setPercentMode(); } }; graph.setFilterWarning = function ( val ){ showFilterWarning = val; }; function loadGraphData( init ){ // reset the locate button and previously selected locations and other variables var loadingModule = graph.options().loadingModule(); force.stop(); force.nodes([]); force.links([]); nodeArrayForPulse = []; pulseNodeIds = []; locationId = 0; d3.select("#locateSearchResult").classed("highlighted", false); d3.select("#locateSearchResult").node().title = "Nothing to locate"; graph.clearGraphData(); if ( init ) { force.stop(); return; } showFilterWarning = false; parser.parse(options.data()); unfilteredData = { nodes: parser.nodes(), properties: parser.properties() }; // fixing class and property id counter for the editor eN = unfilteredData.nodes.length + 1; eP = unfilteredData.properties.length + 1; // using the ids of elements if to ensure that loaded elements will not get the same id; for ( var p = 0; p < unfilteredData.properties.length; p++ ) { var currentId = unfilteredData.properties[p].id(); if ( currentId.indexOf('objectProperty') !== -1 ) { // could be ours; var idStr = currentId.split('objectProperty'); if ( idStr[0].length === 0 ) { var idInt = parseInt(idStr[1]); if ( eP < idInt ) { eP = idInt + 1; } } } } // using the ids of elements if to ensure that loaded elements will not get the same id; for ( var n = 0; n < unfilteredData.nodes.length; n++ ) { var currentId_Nodes = unfilteredData.nodes[n].id(); if ( currentId_Nodes.indexOf('Class') !== -1 ) { // could be ours; var idStr_Nodes = currentId_Nodes.split('Class'); if ( idStr_Nodes[0].length === 0 ) { var idInt_Nodes = parseInt(idStr_Nodes[1]); if ( eN < idInt_Nodes ) { eN = idInt_Nodes + 1; } } } } initialLoad = true; graph.options().warningModule().closeFilterHint(); // loading handler updateRenderingDuringSimulation = true; var validOntology = graph.options().loadingModule().successfullyLoadedOntology(); if ( graphContainer && validOntology === true ) { updateRenderingDuringSimulation = false; graph.options().ontologyMenu().append_bulletPoint("Generating visualization ... "); loadingModule.setPercentMode(); if ( unfilteredData.nodes.length > 0 ) { graphContainer.style("opacity", "0"); force.on("tick", hiddenRecalculatePositions); } else { graphContainer.style("opacity", "1"); if ( showFPS === true ) { force.on("tick", recalculatePositionsWithFPS); } else { force.on("tick", recalculatePositions); } } force.start(); } else { force.stop(); graph.options().ontologyMenu().append_bulletPoint("Failed to load ontology"); loadingModule.setErrorMode(); } // update prefixList( // update general MetaOBJECT graph.options().clearMetaObject(); graph.options().clearGeneralMetaObject(); graph.options().editSidebar().clearMetaObjectValue(); if ( options.data() !== undefined ) { var header = options.data().header; if ( header ) { if ( header.iri ) { graph.options().addOrUpdateGeneralObjectEntry("iri", header.iri); } if ( header.title ) { graph.options().addOrUpdateGeneralObjectEntry("title", header.title); } if ( header.author ) { graph.options().addOrUpdateGeneralObjectEntry("author", header.author); } if ( header.version ) { graph.options().addOrUpdateGeneralObjectEntry("version", header.version); } if ( header.description ) { graph.options().addOrUpdateGeneralObjectEntry("description", header.description); } if ( header.prefixList ) { var pL = header.prefixList; for ( var pr in pL ) { if ( pL.hasOwnProperty(pr) ) { var val = pL[pr]; graph.options().addPrefix(pr, val); } } } // get other metadata; if ( header.other ) { var otherObjects = header.other; for ( var name in otherObjects ) { if ( otherObjects.hasOwnProperty(name) ) { var otherObj = otherObjects[name]; if ( otherObj.hasOwnProperty("identifier") && otherObj.hasOwnProperty("value") ) { graph.options().addOrUpdateMetaObjectEntry(otherObj.identfier, otherObj.value); } } } } } } // update more meta OBJECT // Initialize filters with data to replicate consecutive filtering var initializationData = _.clone(unfilteredData); options.filterModules().forEach(function ( module ){ initializationData = filterFunction(module, initializationData, true); }); // generate dictionary here ; generateDictionary(unfilteredData); parser.parseSettings(); graphUpdateRequired = parser.settingsImported(); centerGraphViewOnLoad = true; if ( parser.settingsImportGraphZoomAndTranslation() === true ) { centerGraphViewOnLoad = false; } graph.options().searchMenu().requestDictionaryUpdate(); graph.options().editSidebar().updateGeneralOntologyInfo(); graph.options().editSidebar().updatePrefixUi(); graph.options().editSidebar().updateElementWidth(); } graph.handleOnLoadingError = function (){ force.stop(); graph.clearGraphData(); graph.options().ontologyMenu().append_bulletPoint("Failed to load ontology"); d3.select("#progressBarValue").node().innherHTML = ""; d3.select("#progressBarValue").classed("busyProgressBar", false); graph.options().loadingModule().setErrorMode(); graph.options().loadingModule().showErrorDetailsMessage(); }; function quick_refreshGraphData(){ links = linkCreator.createLinks(properties); labelNodes = links.map(function ( link ){ return link.label(); }); storeLinksOnNodes(classNodes, links); setForceLayoutData(classNodes, labelNodes, links); } //Applies the data of the graph options object and parses it. The graph is not redrawn. function refreshGraphData(){ var shouldExecuteEmptyFilter = options.literalFilter().enabled(); graph.executeEmptyLiteralFilter(); options.literalFilter().enabled(shouldExecuteEmptyFilter); var preprocessedData = _.clone(unfilteredData); // Filter the data options.filterModules().forEach(function ( module ){ preprocessedData = filterFunction(module, preprocessedData); }); options.focuserModule().handle(undefined, true); classNodes = preprocessedData.nodes; properties = preprocessedData.properties; links = linkCreator.createLinks(properties); labelNodes = links.map(function ( link ){ return link.label(); }); storeLinksOnNodes(classNodes, links); setForceLayoutData(classNodes, labelNodes, links); // for (var i = 0; i < classNodes.length; i++) { // if (classNodes[i].setRectangularRepresentation) // classNodes[i].setRectangularRepresentation(graph.options().rectangularRepresentation()); // } } function filterFunction( module, data, initializing ){ links = linkCreator.createLinks(data.properties); storeLinksOnNodes(data.nodes, links); if ( initializing ) { if ( module.initialize ) { module.initialize(data.nodes, data.properties); } } module.filter(data.nodes, data.properties); return { nodes: module.filteredNodes(), properties: module.filteredProperties() }; } /** --------------------------------------------------------- **/ /** -- force-layout related functions -- **/ /** --------------------------------------------------------- **/ function storeLinksOnNodes( nodes, links ){ for ( var i = 0, nodesLength = nodes.length; i < nodesLength; i++ ) { var node = nodes[i], connectedLinks = []; // look for properties where this node is the domain or range for ( var j = 0, linksLength = links.length; j < linksLength; j++ ) { var link = links[j]; if ( link.domain() === node || link.range() === node ) { connectedLinks.push(link); } } node.links(connectedLinks); } } function setForceLayoutData( classNodes, labelNodes, links ){ var d3Links = []; links.forEach(function ( link ){ d3Links = d3Links.concat(link.linkParts()); }); var d3Nodes = [].concat(classNodes).concat(labelNodes); setPositionOfOldLabelsOnNewLabels(force.nodes(), labelNodes); force.nodes(d3Nodes) .links(d3Links); } // The label nodes are positioned randomly, because they are created from scratch if the data changes and lose // their position information. With this hack the position of old labels is copied to the new labels. function setPositionOfOldLabelsOnNewLabels( oldLabelNodes, labelNodes ){ labelNodes.forEach(function ( labelNode ){ for ( var i = 0; i < oldLabelNodes.length; i++ ) { var oldNode = oldLabelNodes[i]; if ( oldNode.equals(labelNode) ) { labelNode.x = oldNode.x; labelNode.y = oldNode.y; labelNode.px = oldNode.px; labelNode.py = oldNode.py; break; } } }); } // Applies all options that don't change the graph data. function refreshGraphStyle(){ zoom = zoom.scaleExtent([options.minMagnification(), options.maxMagnification()]); if ( graphContainer ) { zoom.event(graphContainer); } force.charge(function ( element ){ var charge = options.charge(); if ( elementTools.isLabel(element) ) { charge *= 0.8; } return charge; }) .size([options.width(), options.height()]) .linkDistance(calculateLinkPartDistance) .gravity(options.gravity()) .linkStrength(options.linkStrength()); // Flexibility of links force.nodes().forEach(function ( n ){ n.frozen(paused); }); } function calculateLinkPartDistance( linkPart ){ var link = linkPart.link(); if ( link.isLoop() ) { return options.loopDistance(); } // divide by 2 to receive the length for a single link part var linkPartDistance = getVisibleLinkDistance(link) / 2; linkPartDistance += linkPart.domain().actualRadius(); linkPartDistance += linkPart.range().actualRadius(); return linkPartDistance; } function getVisibleLinkDistance( link ){ if ( elementTools.isDatatype(link.domain()) || elementTools.isDatatype(link.range()) ) { return options.datatypeDistance(); } else { return options.classDistance(); } } /** --------------------------------------------------------- **/ /** -- animation functions for the nodes -- **/ /** --------------------------------------------------------- **/ graph.animateDynamicLabelWidth = function (){ var wantedWidth = options.dynamicLabelWidth(); var i; for ( i = 0; i < classNodes.length; i++ ) { var nodeElement = classNodes[i]; if ( elementTools.isDatatype(nodeElement) ) { nodeElement.animateDynamicLabelWidth(wantedWidth); } } for ( i = 0; i < properties.length; i++ ) { properties[i].animateDynamicLabelWidth(wantedWidth); } }; /** --------------------------------------------------------- **/ /** -- halo and localization functions -- **/ /** --------------------------------------------------------- **/ function updateHaloRadius(){ if ( pulseNodeIds && pulseNodeIds.length > 0 ) { var forceNodes = force.nodes(); for ( var i = 0; i < pulseNodeIds.length; i++ ) { var node = forceNodes[pulseNodeIds[i]]; if ( node ) { if ( node.property ) { // match search strings with property label if ( node.property().inverse ) { var searchString = graph.options().searchMenu().getSearchString().toLowerCase(); var name = node.property().labelForCurrentLanguage().toLowerCase(); if ( name === searchString ) computeDistanceToCenter(node); else { node.property().removeHalo(); if ( node.property().inverse() ) { if ( !node.property().inverse().getHalos() ) node.property().inverse().drawHalo(); computeDistanceToCenter(node, true); } if ( node.property().equivalents() ) { var eq = node.property().equivalents(); for ( var e = 0; e < eq.length; e++ ) { if ( !eq[e].getHalos() ) eq[e].drawHalo(); } if ( !node.property().getHalos() ) node.property().drawHalo(); computeDistanceToCenter(node, false); } } } } computeDistanceToCenter(node); } } } } function getScreenCoords( x, y, translate, scale ){ var xn = translate[0] + x * scale; var yn = translate[1] + y * scale; return { x: xn, y: yn }; } function getClickedScreenCoords( x, y, translate, scale ){ var xn = (x - translate[0]) / scale; var yn = (y - translate[1]) / scale; return { x: xn, y: yn }; } function computeDistanceToCenter( node, inverse ){ var container = node; var w = graph.options().width(); var h = graph.options().height(); var posXY = getScreenCoords(node.x, node.y, graphTranslation, zoomFactor); var highlightOfInv = false; if ( inverse && inverse === true ) { highlightOfInv = true; posXY = getScreenCoords(node.x, node.y + 20, graphTranslation, zoomFactor); } var x = posXY.x; var y = posXY.y; var nodeIsRect = false; var halo; var roundHalo; var rectHalo; var borderPoint_x = 0; var borderPoint_y = 0; var defaultRadius; var offset = 15; var radius; if ( node.property && highlightOfInv === true ) { if ( node.property().inverse() ) { rectHalo = node.property().inverse().getHalos().select("rect"); } else { if ( node.property().getHalos() ) rectHalo = node.property().getHalos().select("rect"); else { node.property().drawHalo(); rectHalo = node.property().getHalos().select("rect"); } } rectHalo.classed("hidden", true); if ( node.property().inverse() ) { if ( node.property().inverse().getHalos() ) { roundHalo = node.property().inverse().getHalos().select("circle"); } } else { roundHalo = node.property().getHalos().select("circle"); } if ( roundHalo.node() === null ) { radius = node.property().inverse().width() + 15; roundHalo = node.property().inverse().getHalos().append("circle") .classed("searchResultB", true) .classed("searchResultA", false) .attr("r", radius + 15); } halo = roundHalo; // swap the halo to be round nodeIsRect = true; container = node.property().inverse(); } if ( node.id ) { if ( !node.getHalos() ) return; // something went wrong before halo = node.getHalos().select("rect"); if ( halo.node() === null ) { // this is a round node nodeIsRect = false; roundHalo = node.getHalos().select("circle"); defaultRadius = node.actualRadius(); roundHalo.attr("r", defaultRadius + offset); halo = roundHalo; } else { // this is a rect node nodeIsRect = true; rectHalo = node.getHalos().select("rect"); rectHalo.classed("hidden", true); roundHalo = node.getHalos().select("circle"); if ( roundHalo.node() === null ) { radius = node.width(); roundHalo = node.getHalos().append("circle") .classed("searchResultB", true) .classed("searchResultA", false) .attr("r", radius + offset); } halo = roundHalo; } } if ( node.property && !inverse ) { if ( !node.property().getHalos() ) return; // something went wrong before rectHalo = node.property().getHalos().select("rect"); rectHalo.classed("hidden", true); roundHalo = node.property().getHalos().select("circle"); if ( roundHalo.node() === null ) { radius = node.property().width(); roundHalo = node.property().getHalos().append("circle") .classed("searchResultB", true) .classed("searchResultA", false) .attr("r", radius + 15); } halo = roundHalo; // swap the halo to be round nodeIsRect = true; container = node.property(); } if ( x < 0 || x > w || y < 0 || y > h ) { // node outside viewport; // check for quadrant and get the correct boarder point (intersection with viewport) if ( x < 0 && y < 0 ) { borderPoint_x = 0; borderPoint_y = 0; } else if ( x > 0 && x < w && y < 0 ) { borderPoint_x = x; borderPoint_y = 0; } else if ( x > w && y < 0 ) { borderPoint_x = w; borderPoint_y = 0; } else if ( x > w && y > 0 && y < h ) { borderPoint_x = w; borderPoint_y = y; } else if ( x > w && y > h ) { borderPoint_x = w; borderPoint_y = h; } else if ( x > 0 && x < w && y > h ) { borderPoint_x = x; borderPoint_y = h; } else if ( x < 0 && y > h ) { borderPoint_x = 0; borderPoint_y = h; } else if ( x < 0 && y > 0 && y < h ) { borderPoint_x = 0; borderPoint_y = y; } // kill all pulses of nodes that are outside the viewport container.getHalos().select("rect").classed("searchResultA", false); container.getHalos().select("circle").classed("searchResultA", false); container.getHalos().select("rect").classed("searchResultB", true); container.getHalos().select("circle").classed("searchResultB", true); halo.classed("hidden", false); // compute in pixel coordinates length of difference vector var borderRadius_x = borderPoint_x - x; var borderRadius_y = borderPoint_y - y; var len = borderRadius_x * borderRadius_x + borderRadius_y * borderRadius_y; len = Math.sqrt(len); var normedX = borderRadius_x / len; var normedY = borderRadius_y / len; len = len + 20; // add 20 px; // re-normalized vector var newVectorX = normedX * len + x; var newVectorY = normedY * len + y; // compute world coordinates of this point var wX = (newVectorX - graphTranslation[0]) / zoomFactor; var wY = (newVectorY - graphTranslation[1]) / zoomFactor; // compute distance in world coordinates var dx = wX - node.x; var dy = wY - node.y; if ( highlightOfInv === true ) dy = wY - node.y - 20; if ( highlightOfInv === false && node.property && node.property().inverse() ) dy = wY - node.y + 20; var newRadius = Math.sqrt(dx * dx + dy * dy); halo = container.getHalos().select("circle"); // sanity checks and setting new halo radius if ( !nodeIsRect ) { defaultRadius = node.actualRadius() + offset; if ( newRadius < defaultRadius ) { newRadius = defaultRadius; } halo.attr("r", newRadius); } else { defaultRadius = 0.5 * container.width(); if ( newRadius < defaultRadius ) newRadius = defaultRadius; halo.attr("r", newRadius); } } else { // node is in viewport , render original; // reset the halo to original radius defaultRadius = node.actualRadius() + 15; if ( !nodeIsRect ) { halo.attr("r", defaultRadius); } else { // this is rectangular node render as such halo = container.getHalos().select("rect"); halo.classed("hidden", false); //halo.classed("searchResultB", true); //halo.classed("searchResultA", false); var aCircHalo = container.getHalos().select("circle"); aCircHalo.classed("hidden", true); container.getHalos().select("rect").classed("hidden", false); container.getHalos().select("circle").classed("hidden", true); } } } function transform( p, cx, cy ){ // one iteration step for the locate target animation zoomFactor = graph.options().height() / p[2]; graphTranslation = [(cx - p[0] * zoomFactor), (cy - p[1] * zoomFactor)]; updateHaloRadius(); // update the values in case the user wants to break the animation zoom.translate(graphTranslation); zoom.scale(zoomFactor); graph.options().zoomSlider().updateZoomSliderValue(zoomFactor); return "translate(" + graphTranslation[0] + "," + graphTranslation[1] + ")scale(" + zoomFactor + ")"; } graph.zoomToElementInGraph = function ( element ){ targetLocationZoom(element); }; graph.updateHaloRadius = function ( element ){ computeDistanceToCenter(element); }; function targetLocationZoom( target ){ // store the original information var cx = 0.5 * graph.options().width(); var cy = 0.5 * graph.options().height(); var cp = getWorldPosFromScreen(cx, cy, graphTranslation, zoomFactor); var sP = [cp.x, cp.y, graph.options().height() / zoomFactor]; var zoomLevel = Math.max(defaultZoom + 0.5 * defaultZoom, defaultTargetZoom); var eP = [target.x, target.y, graph.options().height() / zoomLevel]; var pos_intp = d3.interpolateZoom(sP, eP); var lenAnimation = pos_intp.duration; if ( lenAnimation > 2500 ) { lenAnimation = 2500; } graphContainer.attr("transform", transform(sP, cx, cy)) .transition() .duration(lenAnimation) .attrTween("transform", function (){ return function ( t ){ return transform(pos_intp(t), cx, cy); }; }) .each("end", function (){ graphContainer.attr("transform", "translate(" + graphTranslation + ")scale(" + zoomFactor + ")"); zoom.translate(graphTranslation); zoom.scale(zoomFactor); updateHaloRadius(); }); } function getWorldPosFromScreen( x, y, translate, scale ){ var temp = scale[0], xn, yn; if ( temp ) { xn = (x - translate[0]) / temp; yn = (y - translate[1]) / temp; } else { xn = (x - translate[0]) / scale; yn = (y - translate[1]) / scale; } return { x: xn, y: yn }; } graph.locateSearchResult = function (){ if ( pulseNodeIds && pulseNodeIds.length > 0 ) { // move the center of the viewport to this location if ( transformAnimation === true ) return; // << prevents incrementing the location id if we are in an animation var node = force.nodes()[pulseNodeIds[locationId]]; locationId++; locationId = locationId % pulseNodeIds.length; if ( node.id ) node.foreground(); if ( node.property ) node.property().foreground(); targetLocationZoom(node); } }; graph.resetSearchHighlight = function (){ // get all nodes (handle also already filtered nodes ) pulseNodeIds = []; nodeArrayForPulse = []; // clear from stored nodes var nodes = unfilteredData.nodes; var props = unfilteredData.properties; var j; for ( j = 0; j < nodes.length; j++ ) { var node = nodes[j]; if ( node.removeHalo ) node.removeHalo(); } for ( j = 0; j < props.length; j++ ) { var prop = props[j]; if ( prop.removeHalo ) prop.removeHalo(); } }; graph.updatePulseIds = function ( nodeIdArray ){ pulseNodeIds = []; for ( var i = 0; i < nodeIdArray.length; i++ ) { var selectedId = nodeIdArray[i]; var forceId = nodeMap[selectedId]; if ( forceId !== undefined ) { var le_node = force.nodes()[forceId]; if ( le_node.id ) { if ( pulseNodeIds.indexOf(forceId) === -1 ) { pulseNodeIds.push(forceId); } } if ( le_node.property ) { if ( pulseNodeIds.indexOf(forceId) === -1 ) { pulseNodeIds.push(forceId); } } } } locationId = 0; if ( pulseNodeIds.length > 0 ) { d3.select("#locateSearchResult").classed("highlighted", true); d3.select("#locateSearchResult").node().title = "Locate search term"; } else { d3.select("#locateSearchResult").classed("highlighted", false); d3.select("#locateSearchResult").node().title = "Nothing to locate"; } }; graph.highLightNodes = function ( nodeIdArray ){ if ( nodeIdArray.length === 0 ) { return; // nothing to highlight } pulseNodeIds = []; nodeArrayForPulse = nodeIdArray; var missedIds = []; // identify the force id to highlight for ( var i = 0; i < nodeIdArray.length; i++ ) { var selectedId = nodeIdArray[i]; var forceId = nodeMap[selectedId]; if ( forceId !== undefined ) { var le_node = force.nodes()[forceId]; if ( le_node.id ) { if ( pulseNodeIds.indexOf(forceId) === -1 ) { pulseNodeIds.push(forceId); le_node.foreground(); le_node.drawHalo(); } } if ( le_node.property ) { if ( pulseNodeIds.indexOf(forceId) === -1 ) { pulseNodeIds.push(forceId); le_node.property().foreground(); le_node.property().drawHalo(); } } } else { missedIds.push(selectedId); } } if ( missedIds.length === nodeIdArray.length ) { } // store the highlight on the missed nodes; var s_nodes = unfilteredData.nodes; var s_props = unfilteredData.properties; for ( i = 0; i < missedIds.length; i++ ) { var missedId = missedIds[i]; // search for this in the nodes; for ( var n = 0; n < s_nodes.length; n++ ) { var nodeId = s_nodes[n].id(); if ( nodeId === missedId ) { s_nodes[n].drawHalo(); } } for ( var p = 0; p < s_props.length; p++ ) { var propId = s_props[p].id(); if ( propId === missedId ) { s_props[p].drawHalo(); } } } if ( missedIds.length === nodeIdArray.length ) { d3.select("#locateSearchResult").classed("highlighted", false); } else { d3.select("#locateSearchResult").classed("highlighted", true); } locationId = 0; updateHaloRadius(); }; graph.hideHalos = function (){ var haloElements = d3.selectAll(".searchResultA,.searchResultB"); haloElements.classed("hidden", true); return haloElements; }; function nodeInViewport( node, property ){ var w = graph.options().width(); var h = graph.options().height(); var posXY = getScreenCoords(node.x, node.y, graphTranslation, zoomFactor); var x = posXY.x; var y = posXY.y; var retVal = !(x < 0 || x > w || y < 0 || y > h); return retVal; } graph.getBoundingBoxForTex = function (){ var halos = graph.hideHalos(); var bbox = graphContainer.node().getBoundingClientRect(); halos.classed("hidden", false); var w = graph.options().width(); var h = graph.options().height(); // get the graph coordinates var topLeft = getWorldPosFromScreen(0, 0, graphTranslation, zoomFactor); var botRight = getWorldPosFromScreen(w, h, graphTranslation, zoomFactor); var t_topLeft = getWorldPosFromScreen(bbox.left, bbox.top, graphTranslation, zoomFactor); var t_botRight = getWorldPosFromScreen(bbox.right, bbox.bottom, graphTranslation, zoomFactor); // tighten up the bounding box; var tX = Math.max(t_topLeft.x, topLeft.x); var tY = Math.max(t_topLeft.y, topLeft.y); var bX = Math.min(t_botRight.x, botRight.x); var bY = Math.min(t_botRight.y, botRight.y); // tighten further; var allForceNodes = force.nodes(); var numNodes = allForceNodes.length; var visibleNodes = []; var bbx; var contentBBox = { tx: 1000000000000, ty: 1000000000000, bx: -1000000000000, by: -1000000000000 }; for ( var i = 0; i < numNodes; i++ ) { var node = allForceNodes[i]; if ( node ) { if ( node.property ) { if ( nodeInViewport(node, true) ) { if ( node.property().labelElement() === undefined ) continue; bbx = node.property().labelElement().node().getBoundingClientRect(); if ( bbx ) { contentBBox.tx = Math.min(contentBBox.tx, bbx.left); contentBBox.bx = Math.max(contentBBox.bx, bbx.right); contentBBox.ty = Math.min(contentBBox.ty, bbx.top); contentBBox.by = Math.max(contentBBox.by, bbx.bottom); } } } else { if ( nodeInViewport(node, false) ) { bbx = node.nodeElement().node().getBoundingClientRect(); if ( bbx ) { contentBBox.tx = Math.min(contentBBox.tx, bbx.left); contentBBox.bx = Math.max(contentBBox.bx, bbx.right); contentBBox.ty = Math.min(contentBBox.ty, bbx.top); contentBBox.by = Math.max(contentBBox.by, bbx.bottom); } } } } } var tt_topLeft = getWorldPosFromScreen(contentBBox.tx, contentBBox.ty, graphTranslation, zoomFactor); var tt_botRight = getWorldPosFromScreen(contentBBox.bx, contentBBox.by, graphTranslation, zoomFactor); tX = Math.max(tX, tt_topLeft.x); tY = Math.max(tY, tt_topLeft.y); bX = Math.min(bX, tt_botRight.x); bY = Math.min(bY, tt_botRight.y); // y axis flip for tex return [tX, -tY, bX, -bY]; }; var updateTargetElement = function (){ var bbox = graphContainer.node().getBoundingClientRect(); // get the graph coordinates var bboxOffset = 50; // default radius of a node; var topLeft = getWorldPosFromScreen(bbox.left, bbox.top, graphTranslation, zoomFactor); var botRight = getWorldPosFromScreen(bbox.right, bbox.bottom, graphTranslation, zoomFactor); var w = graph.options().width(); if ( graph.options().leftSidebar().isSidebarVisible() === true ) w -= 200; var h = graph.options().height(); topLeft.x += bboxOffset; topLeft.y -= bboxOffset; botRight.x -= bboxOffset; botRight.y += bboxOffset; var g_w = botRight.x - topLeft.x; var g_h = botRight.y - topLeft.y; // endpoint position calculations var posX = 0.5 * (topLeft.x + botRight.x); var posY = 0.5 * (topLeft.y + botRight.y); var cx = 0.5 * w, cy = 0.5 * h; if ( graph.options().leftSidebar().isSidebarVisible() === true ) cx += 200; var cp = getWorldPosFromScreen(cx, cy, graphTranslation, zoomFactor); // zoom factor calculations and fail safes; var newZoomFactor = 1.0; // fail save if graph and window are squares //get the smaller one var a = w / g_w; var b = h / g_h; if ( a < b ) newZoomFactor = a; else newZoomFactor = b; // fail saves if ( newZoomFactor > zoom.scaleExtent()[1] ) { newZoomFactor = zoom.scaleExtent()[1]; } if ( newZoomFactor < zoom.scaleExtent()[0] ) { newZoomFactor = zoom.scaleExtent()[0]; } // apply Zooming var sP = [cp.x, cp.y, h / zoomFactor]; var eP = [posX, posY, h / newZoomFactor]; var pos_intp = d3.interpolateZoom(sP, eP); return [pos_intp, cx, cy]; }; graph.forceRelocationEvent = function ( dynamic ){ // we need to kill the halo to determine the bounding box; var halos = graph.hideHalos(); var bbox = graphContainer.node().getBoundingClientRect(); halos.classed("hidden", false); // get the graph coordinates var bboxOffset = 50; // default radius of a node; var topLeft = getWorldPosFromScreen(bbox.left, bbox.top, graphTranslation, zoomFactor); var botRight = getWorldPosFromScreen(bbox.right, bbox.bottom, graphTranslation, zoomFactor); var w = graph.options().width(); if ( graph.options().leftSidebar().isSidebarVisible() === true ) w -= 200; var h = graph.options().height(); topLeft.x += bboxOffset; topLeft.y -= bboxOffset; botRight.x -= bboxOffset; botRight.y += bboxOffset; var g_w = botRight.x - topLeft.x; var g_h = botRight.y - topLeft.y; // endpoint position calculations var posX = 0.5 * (topLeft.x + botRight.x); var posY = 0.5 * (topLeft.y + botRight.y); var cx = 0.5 * w, cy = 0.5 * h; if ( graph.options().leftSidebar().isSidebarVisible() === true ) cx += 200; var cp = getWorldPosFromScreen(cx, cy, graphTranslation, zoomFactor); // zoom factor calculations and fail safes; var newZoomFactor = 1.0; // fail save if graph and window are squares //get the smaller one var a = w / g_w; var b = h / g_h; if ( a < b ) newZoomFactor = a; else newZoomFactor = b; // fail saves if ( newZoomFactor > zoom.scaleExtent()[1] ) { newZoomFactor = zoom.scaleExtent()[1]; } if ( newZoomFactor < zoom.scaleExtent()[0] ) { newZoomFactor = zoom.scaleExtent()[0]; } // apply Zooming var sP = [cp.x, cp.y, h / zoomFactor]; var eP = [posX, posY, h / newZoomFactor]; var pos_intp = d3.interpolateZoom(sP, eP); var lenAnimation = pos_intp.duration; if ( lenAnimation > 2500 ) { lenAnimation = 2500; } graphContainer.attr("transform", transform(sP, cx, cy)) .transition() .duration(lenAnimation) .attrTween("transform", function (){ return function ( t ){ if ( dynamic ) { var param = updateTargetElement(); var nV = param[0](t); return transform(nV, cx, cy); } return transform(pos_intp(t), cx, cy); }; }) .each("end", function (){ if ( dynamic ) { return; } graphContainer.attr("transform", "translate(" + graphTranslation + ")scale(" + zoomFactor + ")"); zoom.translate(graphTranslation); zoom.scale(zoomFactor); graph.options().zoomSlider().updateZoomSliderValue(zoomFactor); }); }; graph.isADraggerActive = function (){ if ( classDragger.mouseButtonPressed === true || domainDragger.mouseButtonPressed === true || rangeDragger.mouseButtonPressed === true ) { return true; } return false; }; /** --------------------------------------------------------- **/ /** -- VOWL EDITOR create/ edit /delete functions -- **/ /** --------------------------------------------------------- **/ graph.changeNodeType = function ( element ){ var typeString = d3.select("#typeEditor").node().value; if ( graph.classesSanityCheck(element, typeString) === false ) { // call reselection to restore previous type selection graph.options().editSidebar().updateSelectionInformation(element); return; } var prototype = NodePrototypeMap.get(typeString.toLowerCase()); var aNode = new prototype(graph); aNode.x = element.x; aNode.y = element.y; aNode.px = element.x; aNode.py = element.y; aNode.id(element.id()); aNode.copyInformation(element); if ( typeString === "owl:Thing" ) { aNode.label("Thing"); } else if ( elementTools.isDatatype(element) === false ) { if ( element.backupLabel() !== undefined ) { aNode.label(element.backupLabel()); } else if ( aNode.backupLabel() !== undefined ) { aNode.label(aNode.backupLabel()); } else { aNode.label("NewClass"); } } if ( typeString === "rdfs:Datatype" ) { if ( aNode.dType() === "undefined" ) aNode.label("undefined"); else { var identifier = aNode.dType().split(":")[1]; aNode.label(identifier); } } var i; // updates the property domain and range for ( i = 0; i < unfilteredData.properties.length; i++ ) { if ( unfilteredData.properties[i].domain() === element ) { // unfilteredData.properties[i].toString(); unfilteredData.properties[i].domain(aNode); } if ( unfilteredData.properties[i].range() === element ) { unfilteredData.properties[i].range(aNode); // unfilteredData.properties[i].toString(); } } // update for fastUpdate: for ( i = 0; i < properties.length; i++ ) { if ( properties[i].domain() === element ) { // unfilteredData.properties[i].toString(); properties[i].domain(aNode); } if ( properties[i].range() === element ) { properties[i].range(aNode); // unfilteredData.properties[i].toString(); } } var remId = unfilteredData.nodes.indexOf(element); if ( remId !== -1 ) unfilteredData.nodes.splice(remId, 1); remId = classNodes.indexOf(element); if ( remId !== -1 ) classNodes.splice(remId, 1); // very important thing for selection!; addNewNodeElement(aNode); // handle focuser! options.focuserModule().handle(aNode); generateDictionary(unfilteredData); graph.getUpdateDictionary(); element = null; }; graph.changePropertyType = function ( element ){ var typeString = d3.select("#typeEditor").node().value; // create warning if ( graph.sanityCheckProperty(element.domain(), element.range(), typeString) === false ) return false; var propPrototype = PropertyPrototypeMap.get(typeString.toLowerCase()); var aProp = new propPrototype(graph); aProp.copyInformation(element); aProp.id(element.id()); element.domain().removePropertyElement(element); element.range().removePropertyElement(element); aProp.domain(element.domain()); aProp.range(element.range()); if ( element.backupLabel() !== undefined ) { aProp.label(element.backupLabel()); } else { aProp.label("newObjectProperty"); } if ( aProp.type() === "rdfs:subClassOf" ) { aProp.iri("http://www.w3.org/2000/01/rdf-schema#subClassOf"); } else { if ( element.iri() === "http://www.w3.org/2000/01/rdf-schema#subClassOf" ) aProp.iri(graph.options().getGeneralMetaObjectProperty('iri') + aProp.id()); } if ( graph.propertyCheckExistenceChecker(aProp, element.domain(), element.range()) === false ) { graph.options().editSidebar().updateSelectionInformation(element); return; } // // TODO: change its base IRI to proper value // var ontoIRI="http://someTest.de"; // aProp.baseIri(ontoIRI); // aProp.iri(aProp.baseIri()+aProp.id()); // add this to the data; unfilteredData.properties.push(aProp); if ( properties.indexOf(aProp) === -1 ) properties.push(aProp); var remId = unfilteredData.properties.indexOf(element); if ( remId !== -1 ) unfilteredData.properties.splice(remId, 1); if ( properties.indexOf(aProp) === -1 ) properties.push(aProp); remId = properties.indexOf(element); if ( remId !== -1 ) properties.splice(remId, 1); graph.fastUpdate(); aProp.domain().addProperty(aProp); aProp.range().addProperty(aProp); if ( element.labelObject() && aProp.labelObject() ) { aProp.labelObject().x = element.labelObject().x; aProp.labelObject().px = element.labelObject().px; aProp.labelObject().y = element.labelObject().y; aProp.labelObject().py = element.labelObject().py; } options.focuserModule().handle(aProp); element = null; }; graph.removeEditElements = function (){ // just added to be called form outside removeEditElements(); }; function removeEditElements(){ rangeDragger.hideDragger(true); domainDragger.hideDragger(true); shadowClone.hideClone(true); classDragger.hideDragger(true); if ( addDataPropertyGroupElement ) addDataPropertyGroupElement.classed("hidden", true); if ( deleteGroupElement ) deleteGroupElement.classed("hidden", true); if ( hoveredNodeElement ) { if ( hoveredNodeElement.pinned() === false ) { hoveredNodeElement.locked(graph.paused()); hoveredNodeElement.frozen(graph.paused()); } } if ( hoveredPropertyElement ) { if ( hoveredPropertyElement.pinned() === false ) { hoveredPropertyElement.locked(graph.paused()); hoveredPropertyElement.frozen(graph.paused()); } } } graph.editorMode = function ( val ){ var create_entry = d3.select("#empty"); var create_container = d3.select("#emptyContainer"); var modeOfOpString = d3.select("#modeOfOperationString").node(); if ( !arguments.length ) { create_entry.node().checked = editMode; if ( editMode === false ) { create_container.node().title = "Enable editing in modes menu to create a new ontology"; create_entry.node().title = "Enable editing in modes menu to create a new ontology"; create_entry.style("pointer-events", "none"); } else { create_container.node().title = "Creates a new empty ontology"; create_entry.node().title = "Creates a new empty ontology"; d3.select("#useAccuracyHelper").style("color", "#2980b9"); d3.select("#useAccuracyHelper").style("pointer-events", "auto"); create_entry.node().disabled = false; create_entry.style("pointer-events", "auto"); } return editMode; } graph.options().setEditorModeForDefaultObject(val); // if (seenEditorHint===false && val===true){ // seenEditorHint=true; // graph.options().warningModule().showEditorHint(); // } editMode = val; if ( create_entry ) { create_entry.classed("disabled", !editMode); if ( !editMode ) { create_container.node().title = "Enable editing in modes menu to create a new ontology"; create_entry.node().title = "Enable editing in modes menu to create a new ontology"; create_entry.node().disabled = true; d3.select("#useAccuracyHelper").style("color", "#979797"); d3.select("#useAccuracyHelper").style("pointer-events", "none"); create_entry.style("pointer-events", "none"); } else { create_container.node().title = "Creates a new empty ontology"; create_entry.node().title = "Creates a new empty ontology"; d3.select("#useAccuracyHelper").style("color", "#2980b9"); d3.select("#useAccuracyHelper").style("pointer-events", "auto"); create_entry.style("pointer-events", "auto"); } } // adjust compact notation // selector = compactNotationOption; // box =ModuleCheckbox var compactNotationContainer = d3.select("#compactnotationModuleCheckbox"); if ( compactNotationContainer ) { compactNotationContainer.classed("disabled", !editMode); if ( !editMode ) { compactNotationContainer.node().title = ""; compactNotationContainer.node().disabled = false; compactNotationContainer.style("pointer-events", "auto"); d3.select("#compactNotationOption").style("color", ""); d3.select("#compactNotationOption").node().title = ""; options.literalFilter().enabled(true); graph.update(); } else { // if editor Mode //1) uncheck the element d3.select("#compactNotationOption").node().title = "Compact notation can only be used in view mode"; compactNotationContainer.node().disabled = true; compactNotationContainer.node().checked = false; options.compactNotationModule().enabled(false); options.literalFilter().enabled(false); graph.executeCompactNotationModule(); graph.executeEmptyLiteralFilter(); graph.lazyRefresh(); compactNotationContainer.style("pointer-events", "none"); d3.select("#compactNotationOption").style("color", "#979797"); } } if ( modeOfOpString ) { if ( touchDevice === true ) { modeOfOpString.innerHTML = "touch able device detected"; } else { modeOfOpString.innerHTML = "point & click device detected"; } } var svgGraph = d3.selectAll(".vowlGraph"); if ( editMode === true ) { options.leftSidebar().showSidebar(options.leftSidebar().getSidebarVisibility(), true); options.leftSidebar().hideCollapseButton(false); graph.options().editSidebar().updatePrefixUi(); graph.options().editSidebar().updateElementWidth(); svgGraph.on("dblclick.zoom", graph.modified_dblClickFunction); } else { svgGraph.on("dblclick.zoom", originalD3_dblClickFunction); options.leftSidebar().showSidebar(0); options.leftSidebar().hideCollapseButton(true); // hide hovered edit elements removeEditElements(); } options.sidebar().updateShowedInformation(); options.editSidebar().updateElementWidth(); }; function createLowerCasePrototypeMap( prototypeMap ){ return d3.map(prototypeMap.values(), function ( Prototype ){ return new Prototype().type().toLowerCase(); }); } function createNewNodeAtPosition( pos ){ var aNode, prototype; var forceUpdate = true; // create a node of that id; var typeToCreate = d3.select("#defaultClass").node().title; prototype = NodePrototypeMap.get(typeToCreate.toLowerCase()); aNode = new prototype(graph); var autoEditElement = false; if ( typeToCreate === "owl:Thing" ) { aNode.label("Thing"); } else { aNode.label("NewClass"); autoEditElement = true; } aNode.x = pos.x; aNode.y = pos.y; aNode.px = aNode.x; aNode.py = aNode.y; aNode.id("Class" + eN++); // aNode.paused(true); aNode.baseIri(d3.select("#iriEditor").node().value); aNode.iri(aNode.baseIri() + aNode.id()); addNewNodeElement(aNode, forceUpdate); options.focuserModule().handle(aNode, true); aNode.frozen(graph.paused()); aNode.locked(graph.paused()); aNode.enableEditing(autoEditElement); } function addNewNodeElement( element ){ unfilteredData.nodes.push(element); if ( classNodes.indexOf(element) === -1 ) classNodes.push(element); generateDictionary(unfilteredData); graph.getUpdateDictionary(); graph.fastUpdate(); } graph.getTargetNode = function ( position ){ var dx = position[0]; var dy = position[1]; var tN = null; var minDist = 1000000000000; // This is a bit OVERKILL for the computation of one node >> TODO: KD-TREE SEARCH unfilteredData.nodes.forEach(function ( el ){ var cDist = Math.sqrt((el.x - dx) * (el.x - dx) + (el.y - dy) * (el.y - dy)); if ( cDist < minDist ) { minDist = cDist; tN = el; } }); if ( hoveredNodeElement ) { var offsetDist = hoveredNodeElement.actualRadius() + 30; if ( minDist > offsetDist ) return null; if ( tN.renderType() === "rect" ) return null; if ( tN === hoveredNodeElement && minDist <= hoveredNodeElement.actualRadius() ) { return tN; } else if ( tN === hoveredNodeElement && minDist > hoveredNodeElement.actualRadius() ) { return null; } return tN; } else { if ( minDist > (tN.actualRadius() + 30) ) return null; else return tN; } }; graph.genericPropertySanityCheck = function ( domain, range, typeString, header, action ){ if ( domain === range && typeString === "rdfs:subClassOf" ) { graph.options().warningModule().showWarning(header, "rdfs:subClassOf can not be created as loops (domain == range)", action, 1, false); return false; } if ( domain === range && typeString === "owl:disjointWith" ) { graph.options().warningModule().showWarning(header, "owl:disjointWith can not be created as loops (domain == range)", action, 1, false); return false; } // allProps[i].type()==="owl:allValuesFrom" || // allProps[i].type()==="owl:someValuesFrom" if ( domain.type() === "owl:Thing" && typeString === "owl:allValuesFrom" ) { graph.options().warningModule().showWarning(header, "owl:allValuesFrom can not originate from owl:Thing", action, 1, false); return false; } if ( domain.type() === "owl:Thing" && typeString === "owl:someValuesFrom" ) { graph.options().warningModule().showWarning(header, "owl:someValuesFrom can not originate from owl:Thing", action, 1, false); return false; } if ( range.type() === "owl:Thing" && typeString === "owl:allValuesFrom" ) { graph.options().warningModule().showWarning(header, "owl:allValuesFrom can not be connected to owl:Thing", action, 1, false); return false; } if ( range.type() === "owl:Thing" && typeString === "owl:someValuesFrom" ) { graph.options().warningModule().showWarning(header, "owl:someValuesFrom can not be connected to owl:Thing", action, 1, false); return false; } return true; // we can Change the domain or range }; graph.checkIfIriClassAlreadyExist = function ( url ){ // search for a class node with this url var allNodes = unfilteredData.nodes; for ( var i = 0; i < allNodes.length; i++ ) { if ( elementTools.isDatatype(allNodes[i]) === true || allNodes[i].type() === "owl:Thing" ) continue; // now we are a real class; //get class IRI var classIRI = allNodes[i].iri(); // this gives me the node for halo if ( url === classIRI ) { return allNodes[i]; } } return false; }; graph.classesSanityCheck = function ( classElement, targetType ){ // this is added due to someValuesFrom properties // we should not be able to change a classElement to a owl:Thing // when it has a property attached to it that uses these restrictions // if ( targetType === "owl:Class" ) return true; else { // collect all properties which have that one as a domain or range var allProps = unfilteredData.properties; for ( var i = 0; i < allProps.length; i++ ) { if ( allProps[i].range() === classElement || allProps[i].domain() === classElement ) { // check for the type of that property if ( allProps[i].type() === "owl:someValuesFrom" ) { graph.options().warningModule().showWarning("Can not change class type", "The element has a property that is of type owl:someValuesFrom", "Element type not changed!", 1, true); return false; } if ( allProps[i].type() === "owl:allValuesFrom" ) { graph.options().warningModule().showWarning("Can not change class type", "The element has a property that is of type owl:allValuesFrom", "Element type not changed!", 1, true); return false; } } } } return true; }; graph.propertyCheckExistenceChecker = function ( property, domain, range ){ var allProps = unfilteredData.properties; var i; if ( property.type() === "rdfs:subClassOf" || property.type() === "owl:disjointWith" ) { for ( i = 0; i < allProps.length; i++ ) { if ( allProps[i] === property ) continue; if ( allProps[i].domain() === domain && allProps[i].range() === range && allProps[i].type() === property.type() ) { graph.options().warningModule().showWarning("Warning", "This triple already exist!", "Element not created!", 1, false); return false; } if ( allProps[i].domain() === range && allProps[i].range() === domain && allProps[i].type() === property.type() ) { graph.options().warningModule().showWarning("Warning", "Inverse assignment already exist! ", "Element not created!", 1, false); return false; } } return true; } return true; }; // graph.checkForTripleDuplicate=function(property){ // var domain=property.domain(); // var range=property.range(); // console.log("checking for duplicates"); // var b1= domain.isPropertyAssignedToThisElement(property); // var b2= range.isPropertyAssignedToThisElement(property); // // console.log("test domain results in "+ b1); // console.log("test range results in "+ b1); // // if (b1 && b2 ){ // graph.options().warningModule().showWarning("Warning", // "This triple already exist!", // "Element not created!",1,false); // return false; // } // return true; // }; graph.sanityCheckProperty = function ( domain, range, typeString ){ // check for duplicate triple in the element; if ( typeString === "owl:objectProperty" && graph.options().objectPropertyFilter().enabled() === true ) { graph.options().warningModule().showWarning("Warning", "Object properties are filtered out in the visualization!", "Element not created!", 1, false); return false; } if ( typeString === "owl:disjointWith" && graph.options().disjointPropertyFilter().enabled() === true ) { graph.options().warningModule().showWarning("Warning", "owl:disjointWith properties are filtered out in the visualization!", "Element not created!", 1, false); return false; } if ( domain === range && typeString === "rdfs:subClassOf" ) { graph.options().warningModule().showWarning("Warning", "rdfs:subClassOf can not be created as loops (domain == range)", "Element not created!", 1, false); return false; } if ( domain === range && typeString === "owl:disjointWith" ) { graph.options().warningModule().showWarning("Warning", "owl:disjointWith can not be created as loops (domain == range)", "Element not created!", 1, false); return false; } if ( domain.type() === "owl:Thing" && typeString === "owl:someValuesFrom" ) { graph.options().warningModule().showWarning("Warning", "owl:someValuesFrom can not originate from owl:Thing", "Element not created!", 1, false); return false; } if ( domain.type() === "owl:Thing" && typeString === "owl:allValuesFrom" ) { graph.options().warningModule().showWarning("Warning", "owl:allValuesFrom can not originate from owl:Thing", "Element not created!", 1, false); return false; } if ( range.type() === "owl:Thing" && typeString === "owl:allValuesFrom" ) { graph.options().warningModule().showWarning("Warning", "owl:allValuesFrom can not be connected to owl:Thing", "Element not created!", 1, false); return false; } if ( range.type() === "owl:Thing" && typeString === "owl:someValuesFrom" ) { graph.options().warningModule().showWarning("Warning", "owl:someValuesFrom can not be connected to owl:Thing", "Element not created!", 1, false); return false; } return true; // we can create a property }; function createNewObjectProperty( domain, range, draggerEndposition ){ // check type of the property that we want to create; var defaultPropertyName = d3.select("#defaultProperty").node().title; // check if we are allow to create that property if ( graph.sanityCheckProperty(domain, range, defaultPropertyName) === false ) return false; var propPrototype = PropertyPrototypeMap.get(defaultPropertyName.toLowerCase()); var aProp = new propPrototype(graph); aProp.id("objectProperty" + eP++); aProp.domain(domain); aProp.range(range); aProp.label("newObjectProperty"); aProp.baseIri(d3.select("#iriEditor").node().value); aProp.iri(aProp.baseIri() + aProp.id()); // check for duplicate; if ( graph.propertyCheckExistenceChecker(aProp, domain, range) === false ) { // delete aProp; // hope for garbage collection here -.- return false; } var autoEditElement = false; if ( defaultPropertyName === "owl:objectProperty" ) { autoEditElement = true; } var pX = 0.49 * (domain.x + range.x); var pY = 0.49 * (domain.y + range.y); if ( domain === range ) { // we use the dragger endposition to determine an angle to put the loop there; var dirD_x = draggerEndposition[0] - domain.x; var dirD_y = draggerEndposition[1] - domain.y; // normalize; var len = Math.sqrt(dirD_x * dirD_x + dirD_y * dirD_y); // it should be very hard to set the position on the same sport but why not handling this var nx = dirD_x / len; var ny = dirD_y / len; // is Nan in javascript like in c len==len returns false when it is not a number? if ( isNaN(len) ) { nx = 0; ny = -1; } // get domain actual raidus var offset = 2 * domain.actualRadius() + 50; pX = domain.x + offset * nx; pY = domain.y + offset * ny; } // add this property to domain and range; domain.addProperty(aProp); range.addProperty(aProp); // add this to the data; unfilteredData.properties.push(aProp); if ( properties.indexOf(aProp) === -1 ) properties.push(aProp); graph.fastUpdate(); aProp.labelObject().x = pX; aProp.labelObject().px = pX; aProp.labelObject().y = pY; aProp.labelObject().py = pY; aProp.frozen(graph.paused()); aProp.locked(graph.paused()); domain.frozen(graph.paused()); domain.locked(graph.paused()); range.frozen(graph.paused()); range.locked(graph.paused()); generateDictionary(unfilteredData); graph.getUpdateDictionary(); options.focuserModule().handle(aProp); graph.activateHoverElementsForProperties(true, aProp, false, touchDevice); aProp.labelObject().increasedLoopAngle = true; aProp.enableEditing(autoEditElement); } graph.createDataTypeProperty = function ( node ){ // random postion issues; clearTimeout(nodeFreezer); // tells user when element is filtered out if ( graph.options().datatypeFilter().enabled() === true ) { graph.options().warningModule().showWarning("Warning", "Datatype properties are filtered out in the visualization!", "Element not created!", 1, false); return; } var aNode, prototype; // create a default datatype Node >> HERE LITERAL; var defaultDatatypeName = d3.select("#defaultDatatype").node().title; if ( defaultDatatypeName === "rdfs:Literal" ) { prototype = NodePrototypeMap.get("rdfs:literal"); aNode = new prototype(graph); aNode.label("Literal"); aNode.iri("http://www.w3.org/2000/01/rdf-schema#Literal"); aNode.baseIri("http://www.w3.org/2000/01/rdf-schema#"); } else { prototype = NodePrototypeMap.get("rdfs:datatype"); aNode = new prototype(graph); var identifier = ""; if ( defaultDatatypeName === "undefined" ) { identifier = "undefined"; aNode.label(identifier); // TODO : HANDLER FOR UNDEFINED DATATYPES!!<<<>>>>>>>>>>>.. aNode.iri("http://www.undefinedDatatype.org/#" + identifier); aNode.baseIri("http://www.undefinedDatatype.org/#"); aNode.dType(defaultDatatypeName); } else { identifier = defaultDatatypeName.split(":")[1]; aNode.label(identifier); aNode.dType(defaultDatatypeName); aNode.iri("http://www.w3.org/2001/XMLSchema#" + identifier); aNode.baseIri("http://www.w3.org/2001/XMLSchema#"); } } var nX = node.x - node.actualRadius() - 100; var nY = node.y + node.actualRadius() + 100; aNode.x = nX; aNode.y = nY; aNode.px = aNode.x; aNode.py = aNode.y; aNode.id("NodeId" + eN++); // add this property to the nodes; unfilteredData.nodes.push(aNode); if ( classNodes.indexOf(aNode) === -1 ) classNodes.push(aNode); // add also the datatype Property to it var propPrototype = PropertyPrototypeMap.get("owl:datatypeproperty"); var aProp = new propPrototype(graph); aProp.id("datatypeProperty" + eP++); // create the connection aProp.domain(node); aProp.range(aNode); aProp.label("newDatatypeProperty"); // TODO: change its base IRI to proper value var ontoIri = d3.select("#iriEditor").node().value; aProp.baseIri(ontoIri); aProp.iri(ontoIri + aProp.id()); // add this to the data; unfilteredData.properties.push(aProp); if ( properties.indexOf(aProp) === -1 ) properties.push(aProp); graph.fastUpdate(); generateDictionary(unfilteredData); graph.getUpdateDictionary(); nodeFreezer = setTimeout(function (){ if ( node && node.frozen() === true && node.pinned() === false && graph.paused() === false ) { node.frozen(graph.paused()); node.locked(graph.paused()); } }, 1000); options.focuserModule().handle(undefined); if ( node ) { node.frozen(true); node.locked(true); } }; graph.removeNodesViaResponse = function ( nodesToRemove, propsToRemove ){ var i, remId; // splice them; for ( i = 0; i < propsToRemove.length; i++ ) { remId = unfilteredData.properties.indexOf(propsToRemove[i]); if ( remId !== -1 ) unfilteredData.properties.splice(remId, 1); remId = properties.indexOf(propsToRemove[i]); if ( remId !== -1 ) properties.splice(remId, 1); propsToRemove[i] = null; } for ( i = 0; i < nodesToRemove.length; i++ ) { remId = unfilteredData.nodes.indexOf(nodesToRemove[i]); if ( remId !== -1 ) { unfilteredData.nodes.splice(remId, 1); } remId = classNodes.indexOf(nodesToRemove[i]); if ( remId !== -1 ) classNodes.splice(remId, 1); nodesToRemove[i] = null; } graph.fastUpdate(); generateDictionary(unfilteredData); graph.getUpdateDictionary(); options.focuserModule().handle(undefined); nodesToRemove = null; propsToRemove = null; }; graph.removeNodeViaEditor = function ( node ){ var propsToRemove = []; var nodesToRemove = []; var datatypes = 0; nodesToRemove.push(node); for ( var i = 0; i < unfilteredData.properties.length; i++ ) { if ( unfilteredData.properties[i].domain() === node || unfilteredData.properties[i].range() === node ) { propsToRemove.push(unfilteredData.properties[i]); if ( unfilteredData.properties[i].type().toLocaleLowerCase() === "owl:datatypeproperty" && unfilteredData.properties[i].range() !== node ) { nodesToRemove.push(unfilteredData.properties[i].range()); datatypes++; } } } // Build confirmation message describing everything that will be removed var nodeLabel = node.labelForCurrentLanguage() || node.iri() || "this node"; var text; if ( propsToRemove.length === 0 ) { text = "Delete class \"" + nodeLabel + "\"?"; } else if ( datatypes !== 0 ) { text = "Delete class \"" + nodeLabel + "\", " + datatypes + " datatype(s) and " + propsToRemove.length + " propert" + (propsToRemove.length === 1 ? "y" : "ies") + "?"; } else { text = "Delete class \"" + nodeLabel + "\" and " + propsToRemove.length + " connected propert" + (propsToRemove.length === 1 ? "y" : "ies") + "?"; } graph.options().warningModule().responseWarning( "Delete node", text, "This action cannot be undone.", graph.removeNodesViaResponse, [nodesToRemove, propsToRemove], false); }; graph._doRemoveProperty = function ( property ){ property.domain().removePropertyElement(property); property.range().removePropertyElement(property); var remId; if ( property.type().toLocaleLowerCase() === "owl:datatypeproperty" ) { var datatype = property.range(); remId = unfilteredData.nodes.indexOf(property.range()); if ( remId !== -1 ) unfilteredData.nodes.splice(remId, 1); remId = classNodes.indexOf(property.range()); if ( remId !== -1 ) classNodes.splice(remId, 1); datatype = null; } remId = unfilteredData.properties.indexOf(property); if ( remId !== -1 ) unfilteredData.properties.splice(remId, 1); remId = properties.indexOf(property); if ( remId !== -1 ) properties.splice(remId, 1); if ( property.inverse() ) { property.inverse().inverse(0); } hoveredPropertyElement = undefined; graph.fastUpdate(); generateDictionary(unfilteredData); graph.getUpdateDictionary(); options.focuserModule().handle(undefined); property = null; }; graph.removePropertyViaEditor = function ( property ){ var propLabel = property.labelForCurrentLanguage() || property.iri() || "this property"; var text = "Delete property \"" + propLabel + "\"?"; if ( property.type().toLocaleLowerCase() === "owl:datatypeproperty" ) { text = "Delete datatype property \"" + propLabel + "\" and its range datatype node?"; } graph.options().warningModule().responseWarning( "Delete property", text, "This action cannot be undone.", graph._doRemoveProperty, [property], false); }; graph.executeColorExternalsModule = function (){ options.colorExternalsModule().filter(unfilteredData.nodes, unfilteredData.properties); }; graph.executeCompactNotationModule = function (){ if ( unfilteredData ) { options.compactNotationModule().filter(unfilteredData.nodes, unfilteredData.properties); } }; graph.executeEmptyLiteralFilter = function (){ if ( unfilteredData && unfilteredData.nodes.length > 1 ) { options.literalFilter().filter(unfilteredData.nodes, unfilteredData.properties); unfilteredData.nodes = options.literalFilter().filteredNodes(); unfilteredData.properties = options.literalFilter().filteredProperties(); } }; /** --------------------------------------------------------- **/ /** -- animation functions for the nodes -- **/ /** --------------------------------------------------------- **/ graph.animateDynamicLabelWidth = function (){ var wantedWidth = options.dynamicLabelWidth(); var i; for ( i = 0; i < classNodes.length; i++ ) { var nodeElement = classNodes[i]; if ( elementTools.isDatatype(nodeElement) ) { nodeElement.animateDynamicLabelWidth(wantedWidth); } } for ( i = 0; i < properties.length; i++ ) { properties[i].animateDynamicLabelWidth(wantedWidth); } }; /** --------------------------------------------------------- **/ /** -- Touch behaviour functions -- **/ /** --------------------------------------------------------- **/ graph.setTouchDevice = function ( val ){ touchDevice = val; }; graph.isTouchDevice = function (){ return touchDevice; }; graph.modified_dblClickFunction = function (){ d3.event.stopPropagation(); d3.event.preventDefault(); // get position where we want to add the node; var grPos = getClickedScreenCoords(d3.event.clientX, d3.event.clientY, graph.translation(), graph.scaleFactor()); createNewNodeAtPosition(grPos); }; function doubletap(){ var touch_time = d3.event.timeStamp; var numTouchers = 1; if ( d3.event && d3.event.touches && d3.event.touches.length ) numTouchers = d3.event.touches.length; if ( touch_time - last_touch_time < 300 && numTouchers === 1 ) { d3.event.stopPropagation(); if ( editMode === true ) { //graph.modified_dblClickFunction(); d3.event.preventDefault(); d3.event.stopPropagation(); last_touch_time = touch_time; return true; } } last_touch_time = touch_time; return false; } function touchzoomed(){ forceNotZooming = true; var touch_time = d3.event.timeStamp; if ( touch_time - last_touch_time < 300 && d3.event.touches.length === 1 ) { d3.event.stopPropagation(); if ( editMode === true ) { //graph.modified_dblClickFunction(); d3.event.preventDefault(); d3.event.stopPropagation(); zoom.translate(graphTranslation); zoom.scale(zoomFactor); graph.modified_dblTouchFunction(); } else { forceNotZooming = false; if ( originalD3_touchZoomFunction ) originalD3_touchZoomFunction(); } return; } forceNotZooming = false; last_touch_time = touch_time; // TODO: WORK AROUND TO CHECK FOR ORIGINAL FUNCTION if ( originalD3_touchZoomFunction ) originalD3_touchZoomFunction(); } graph.modified_dblTouchFunction = function ( d ){ d3.event.stopPropagation(); d3.event.preventDefault(); var xy; if ( editMode === true ) { xy = d3.touches(d3.selectAll(".vowlGraph").node()); } var grPos = getClickedScreenCoords(xy[0][0], xy[0][1], graph.translation(), graph.scaleFactor()); createNewNodeAtPosition(grPos); }; /** --------------------------------------------------------- **/ /** -- Hover and Selection functions, adding edit elements -- **/ /** --------------------------------------------------------- **/ graph.ignoreOtherHoverEvents = function ( val ){ if ( !arguments.length ) { return ignoreOtherHoverEvents; } else ignoreOtherHoverEvents = val; }; function delayedHiddingHoverElements( tbh ){ if ( tbh === true ) return; if ( hoveredNodeElement ) { if ( hoveredNodeElement.editingTextElement === true ) return; delayedHider = setTimeout(function (){ deleteGroupElement.classed("hidden", true); addDataPropertyGroupElement.classed("hidden", true); classDragger.hideDragger(true); if ( hoveredNodeElement && hoveredNodeElement.pinned() === false && graph.paused() === false && hoveredNodeElement.editingTextElement === false ) { hoveredNodeElement.frozen(false); hoveredNodeElement.locked(false); } }, 1000); } if ( hoveredPropertyElement ) { if ( hoveredPropertyElement.editingTextElement === true ) return; delayedHider = setTimeout(function (){ deleteGroupElement.classed("hidden", true); addDataPropertyGroupElement.classed("hidden", true); classDragger.hideDragger(true); rangeDragger.hideDragger(true); domainDragger.hideDragger(true); shadowClone.hideClone(true); if ( hoveredPropertyElement && hoveredPropertyElement.focused() === true && graph.options().drawPropertyDraggerOnHover() === true ) { hoveredPropertyElement.labelObject().increasedLoopAngle = false; // lazy update recalculatePositions(); } if ( hoveredPropertyElement && hoveredPropertyElement.pinned() === false && graph.paused() === false && hoveredPropertyElement.editingTextElement === false ) { hoveredPropertyElement.frozen(false); hoveredPropertyElement.locked(false); } }, 1000); } } // TODO : experimental code for updating dynamic label with and its hover element graph.hideHoverPropertyElementsForAnimation = function (){ deleteGroupElement.classed("hidden", true); }; graph.showHoverElementsAfterAnimation = function ( property, inversed ){ setDeleteHoverElementPositionProperty(property, inversed); deleteGroupElement.classed("hidden", false); }; function editElementHoverOnHidden(){ classDragger.nodeElement.classed("classDraggerNodeHovered", true); classDragger.nodeElement.classed("classDraggerNode", false); editElementHoverOn(); } function editElementHoverOutHidden(){ classDragger.nodeElement.classed("classDraggerNodeHovered", false); classDragger.nodeElement.classed("classDraggerNode", true); editElementHoverOut(); } function editElementHoverOn( touch ){ if ( touch === true ) return; clearTimeout(delayedHider); // ignore touch behaviour } graph.killDelayedTimer = function (){ clearTimeout(delayedHider); clearTimeout(nodeFreezer); }; function editElementHoverOut( tbh ){ if ( hoveredNodeElement ) { if ( graph.ignoreOtherHoverEvents() === true || tbh === true || hoveredNodeElement.editingTextElement === true ) return; delayedHider = setTimeout(function (){ if ( graph.isADraggerActive() === true ) return; deleteGroupElement.classed("hidden", true); addDataPropertyGroupElement.classed("hidden", true); classDragger.hideDragger(true); if ( hoveredNodeElement && hoveredNodeElement.pinned() === false && graph.paused() === false ) { hoveredNodeElement.frozen(false); hoveredNodeElement.locked(false); } }, 1000); } if ( hoveredPropertyElement ) { if ( graph.ignoreOtherHoverEvents() === true || tbh === true || hoveredPropertyElement.editingTextElement === true ) return; delayedHider = setTimeout(function (){ if ( graph.isADraggerActive() === true ) return; deleteGroupElement.classed("hidden", true); addDataPropertyGroupElement.classed("hidden", true); classDragger.hideDragger(true); if ( hoveredPropertyElement && hoveredPropertyElement.pinned() === false && graph.paused() === false ) { hoveredPropertyElement.frozen(false); hoveredPropertyElement.locked(false); } }, 1000); } } graph.activateHoverElementsForProperties = function ( val, property, inversed, touchBehaviour ){ if ( editMode === false ) return; // nothing to do; if ( touchBehaviour === undefined ) touchBehaviour = false; if ( val === true ) { clearTimeout(delayedHider); if ( hoveredPropertyElement ) { if ( hoveredPropertyElement.domain() === hoveredPropertyElement.range() ) { hoveredPropertyElement.labelObject().increasedLoopAngle = false; recalculatePositions(); } } hoveredPropertyElement = property; if ( graph.options().drawPropertyDraggerOnHover() === true ) { if ( property.type() !== "owl:DatatypeProperty" ) { if ( property.domain() === property.range() ) { property.labelObject().increasedLoopAngle = true; recalculatePositions(); } shadowClone.setParentProperty(property, inversed); rangeDragger.setParentProperty(property, inversed); rangeDragger.hideDragger(false); rangeDragger.addMouseEvents(); domainDragger.setParentProperty(property, inversed); domainDragger.hideDragger(false); domainDragger.addMouseEvents(); } else if ( property.type() === "owl:DatatypeProperty" ) { shadowClone.setParentProperty(property, inversed); rangeDragger.setParentProperty(property, inversed); rangeDragger.hideDragger(true); rangeDragger.addMouseEvents(); domainDragger.setParentProperty(property, inversed); domainDragger.hideDragger(false); domainDragger.addMouseEvents(); } } else { // hide when we dont want that option if ( graph.options().drawPropertyDraggerOnHover() === true ) { rangeDragger.hideDragger(true); domainDragger.hideDragger(true); shadowClone.hideClone(true); if ( property.domain() === property.range() ) { property.labelObject().increasedLoopAngle = false; recalculatePositions(); } } } if ( hoveredNodeElement ) { if ( hoveredNodeElement && hoveredNodeElement.pinned() === false && graph.paused() === false ) { hoveredNodeElement.frozen(false); hoveredNodeElement.locked(false); } } hoveredNodeElement = undefined; deleteGroupElement.classed("hidden", false); setDeleteHoverElementPositionProperty(property, inversed); deleteGroupElement.selectAll("*").on("click", function (){ if ( touchBehaviour && property.focused() === false ) { graph.options().focuserModule().handle(property); return; } graph.removePropertyViaEditor(property); d3.event.stopPropagation(); }); classDragger.hideDragger(true); addDataPropertyGroupElement.classed("hidden", true); } else { delayedHiddingHoverElements(); } }; graph.updateDraggerElements = function (){ // set opacity style for all elements rangeDragger.draggerObject.classed("superOpacityElement", !graph.options().showDraggerObject()); domainDragger.draggerObject.classed("superOpacityElement", !graph.options().showDraggerObject()); classDragger.draggerObject.classed("superOpacityElement", !graph.options().showDraggerObject()); nodeContainer.selectAll(".superHiddenElement").classed("superOpacityElement", !graph.options().showDraggerObject()); labelContainer.selectAll(".superHiddenElement").classed("superOpacityElement", !graph.options().showDraggerObject()); deleteGroupElement.selectAll(".superHiddenElement").classed("superOpacityElement", !graph.options().showDraggerObject()); addDataPropertyGroupElement.selectAll(".superHiddenElement").classed("superOpacityElement", !graph.options().showDraggerObject()); }; function setAddDataPropertyHoverElementPosition( node ){ var delX, delY = 0; if ( node.renderType() === "round" ) { var scale = 0.5 * Math.sqrt(2.0); var oX = scale * node.actualRadius(); var oY = scale * node.actualRadius(); delX = node.x - oX; delY = node.y + oY; addDataPropertyGroupElement.attr("transform", "translate(" + delX + "," + delY + ")"); } } function setDeleteHoverElementPosition( node ){ var delX, delY = 0; if ( node.renderType() === "round" ) { var scale = 0.5 * Math.sqrt(2.0); var oX = scale * node.actualRadius(); var oY = scale * node.actualRadius(); delX = node.x + oX; delY = node.y - oY; } else { delX = node.x + 0.5 * node.width() + 6; delY = node.y - 0.5 * node.height() - 6; } deleteGroupElement.attr("transform", "translate(" + delX + "," + delY + ")"); } function setDeleteHoverElementPositionProperty( property, inversed ){ if ( property && property.labelElement() ) { var pos = [property.labelObject().x, property.labelObject().y]; var widthElement = parseFloat(property.getShapeElement().attr("width")); var heightElement = parseFloat(property.getShapeElement().attr("height")); var delX = pos[0] + 0.5 * widthElement + 6; var delY = pos[1] - 0.5 * heightElement - 6; // this is the lower element if ( property.labelElement().attr("transform") === "translate(0,15)" ) delY += 15; // this is upper element if ( property.labelElement().attr("transform") === "translate(0,-15)" ) delY -= 15; deleteGroupElement.attr("transform", "translate(" + delX + "," + delY + ")"); } else { deleteGroupElement.classed("hidden", true);// hide when there is no property } } graph.activateHoverElements = function ( val, node, touchBehaviour ){ if ( editMode === false ) { return; // nothing to do; } if ( touchBehaviour === undefined ) touchBehaviour = false; if ( val === true ) { if ( graph.options().drawPropertyDraggerOnHover() === true ) { rangeDragger.hideDragger(true); domainDragger.hideDragger(true); shadowClone.hideClone(true); } // make them visible clearTimeout(delayedHider); clearTimeout(nodeFreezer); if ( hoveredNodeElement && node.pinned() === false && graph.paused() === false ) { hoveredNodeElement.frozen(false); hoveredNodeElement.locked(false); } hoveredNodeElement = node; if ( node && node.frozen() === false && node.pinned() === false ) { node.frozen(true); node.locked(false); } if ( hoveredPropertyElement && hoveredPropertyElement.focused() === false ) { hoveredPropertyElement.labelObject().increasedLoopAngle = false; recalculatePositions(); // update the loopAngles; } hoveredPropertyElement = undefined; deleteGroupElement.classed("hidden", false); setDeleteHoverElementPosition(node); deleteGroupElement.selectAll("*").on("click", function (){ if ( touchBehaviour && node.focused() === false ) { graph.options().focuserModule().handle(node); return; } graph.removeNodeViaEditor(node); d3.event.stopPropagation(); }) .on("mouseover", function (){ editElementHoverOn(node, touchBehaviour); }) .on("mouseout", function (){ editElementHoverOut(node, touchBehaviour); }); addDataPropertyGroupElement.classed("hidden", true); classDragger.nodeElement.on("mouseover", editElementHoverOn) .on("mouseout", editElementHoverOut); classDragger.draggerObject.on("mouseover", editElementHoverOnHidden) .on("mouseout", editElementHoverOutHidden); // add the dragger element; if ( node.renderType() === "round" ) { classDragger.svgRoot(draggerLayer); classDragger.setParentNode(node); classDragger.hideDragger(false); addDataPropertyGroupElement.classed("hidden", false); setAddDataPropertyHoverElementPosition(node); addDataPropertyGroupElement.selectAll("*").on("click", function (){ if ( touchBehaviour && node.focused() === false ) { graph.options().focuserModule().handle(node); return; } graph.createDataTypeProperty(node); d3.event.stopPropagation(); }) .on("mouseover", function (){ editElementHoverOn(node, touchBehaviour); }) .on("mouseout", function (){ editElementHoverOut(node, touchBehaviour); }); } else { classDragger.hideDragger(true); } } else { delayedHiddingHoverElements(node, touchBehaviour); } }; return graph; };