Final Countdown
parent
cc7ad31ed4
commit
c3e8fa7dc4
|
@ -17,6 +17,9 @@
|
|||
<script src="node_modules/dagre/dist/dagre.min.js" type="text/javascript"></script>
|
||||
<script src="node_modules/bootstrap-3-typeahead/bootstrap3-typeahead.min.js" type="text/javascript"></script>
|
||||
<script src="src/js/papaparse.min.js" type="text/javascript"></script>
|
||||
<script src="src/js/simple-slider.min.js" type="text/javascript"></script>
|
||||
<link type="text/css" rel="stylesheet" href="src/css/simple-slider.css">
|
||||
<link type="text/css" rel="stylesheet" href="src/css/simple-slider-volume.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
|
|
|
@ -5,11 +5,14 @@ import SpotlightContainer from './components/Spotlight/spotlightcontainer';
|
|||
import LogoutModal from './components/Modals/logoutmodal';
|
||||
import ClearWarnModal from './components/Modals/clearwarnmodal'
|
||||
import ClearConfirmModal from './components/Modals/clearconfirmmodal'
|
||||
import ClearingModal from './components/Modals/clearingmodal'
|
||||
import LoadingContainer from './components/loadingcontainer';
|
||||
import GenericAlert from './components/alert';
|
||||
import RawQuery from './components/rawquery';
|
||||
import MenuContainer from './components/Menu/menucontainer';
|
||||
import ExportContainer from './components/Float/exportcontainer';
|
||||
import Settings from './components/Float/settings'
|
||||
import ZoomContainer from './components/Zoom/zoomcontainer'
|
||||
|
||||
export default class AppContainer extends Component {
|
||||
constructor(){
|
||||
|
@ -28,8 +31,11 @@ export default class AppContainer extends Component {
|
|||
<LogoutModal />
|
||||
<ClearWarnModal />
|
||||
<ClearConfirmModal />
|
||||
<ClearingModal />
|
||||
<RawQuery />
|
||||
<MenuContainer />
|
||||
<Settings />
|
||||
<ZoomContainer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,9 +1,120 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
export default class Settings extends Component {
|
||||
constructor(){
|
||||
super();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
emitter.on('openSettings', function(){
|
||||
this.openSettings()
|
||||
}.bind(this))
|
||||
|
||||
$(this.refs.edge).simpleSlider({
|
||||
range: [0,20],
|
||||
step: 1,
|
||||
theme: 'volume slideinline'
|
||||
})
|
||||
|
||||
$(this.refs.sibling).simpleSlider({
|
||||
range: [0,20],
|
||||
step: 1,
|
||||
theme: 'volume slideinline'
|
||||
})
|
||||
|
||||
$(this.refs.edge).bind('slider:changed', this.edgeChange.bind(this))
|
||||
$(this.refs.sibling).bind('slider:changed', this.siblingChange.bind(this))
|
||||
|
||||
$(this.refs.edge).simpleSlider('setValue', appStore.performance.edge)
|
||||
$(this.refs.sibling).simpleSlider('setValue', appStore.performance.sibling)
|
||||
|
||||
$(this.refs.check).prop('checked', appStore.performance.lowGraphics)
|
||||
|
||||
$(this.refs.outer).fadeToggle(0)
|
||||
}
|
||||
|
||||
edgeChange(event, data){
|
||||
appStore.performance.edge = data.value;
|
||||
$(this.refs.edgeinput).val(data.value)
|
||||
conf.set('performance', appStore.performance)
|
||||
}
|
||||
|
||||
siblingChange(event, data){
|
||||
appStore.performance.sibling = data.value;
|
||||
$(this.refs.siblinginput).val(data.value)
|
||||
conf.set('performance', appStore.performance)
|
||||
}
|
||||
|
||||
onGfxChange(event){
|
||||
$(this.refs.check).prop('checked', event.target.checked)
|
||||
appStore.performance.lowGraphics = event.target.checked
|
||||
conf.set('performance', appStore.performance)
|
||||
emitter.emit('changeGraphicsMode')
|
||||
}
|
||||
|
||||
closeSettings(){
|
||||
$(this.refs.outer).fadeToggle(false)
|
||||
}
|
||||
|
||||
openSettings(){
|
||||
$(this.refs.outer).fadeToggle(false)
|
||||
}
|
||||
|
||||
updateSibling(event){
|
||||
$(this.refs.sibling).simpleSlider('setValue', event.target.value)
|
||||
}
|
||||
|
||||
updateEdge(event){
|
||||
$(this.refs.edge).simpleSlider('setValue', event.target.value)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div></div>
|
||||
<div ref="outer" className="settingsDiv panel panel-default">
|
||||
<div className="panel-heading">
|
||||
Settings
|
||||
<button type="button" className="close" onClick={this.closeSettings.bind(this)} aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="panel-body sliderfix">
|
||||
<div>
|
||||
<strong>Sibling Collapse Threshold</strong>
|
||||
<i data-toggle="tooltip"
|
||||
data-placement="right"
|
||||
title="Merge nodes that have the same parent. 0 to Disable, Default 10"
|
||||
className="glyphicon glyphicon-question-sign"></i>
|
||||
<br/>
|
||||
<input type="text" ref="sibling" />
|
||||
<span>
|
||||
<input onChange={this.updateSibling.bind(this)} type="number" min="0" max="20" className="sliderinput" ref="siblinginput" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<strong>Node Collapse Threshold</strong>
|
||||
<i data-toggle="tooltip"
|
||||
data-placement="right"
|
||||
title="Collapse nodes at the end of paths that only have one relationship. 0 to Disable, Default 5"
|
||||
className="glyphicon glyphicon-question-sign"></i>
|
||||
<br />
|
||||
<input type="text" ref="edge" />
|
||||
<span>
|
||||
<input type="number" min="0" max="20" className="sliderinput" ref="edgeinput" />
|
||||
</span>
|
||||
</div>
|
||||
<div className="checkbox-inline">
|
||||
<label>
|
||||
<input ref="check" type="checkbox" onChange={this.onGfxChange.bind(this)}/> Low Detail Mode
|
||||
</label>
|
||||
</div>
|
||||
<i data-toggle="tooltip"
|
||||
data-placement="right"
|
||||
title="Lower detail of graph to improve performance"
|
||||
className="glyphicon glyphicon-question-sign"></i>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,6 +106,26 @@ export default class GraphContainer extends Component {
|
|||
this.state.sigmaInstance.graph.clear()
|
||||
}
|
||||
|
||||
setGraphicsMode(){
|
||||
var lowgfx = appStore.performance.lowGraphics
|
||||
var sigmaInstance = this.state.sigmaInstance
|
||||
this.state.design.clear()
|
||||
if (lowgfx){
|
||||
sigmaInstance.settings('defaultEdgeType', 'line');
|
||||
sigmaInstance.settings('defaultEdgeColor', 'black');
|
||||
this.state.design.setPalette(appStore.lowResPalette);
|
||||
this.state.design.setStyles(appStore.lowResStyle);
|
||||
}else{
|
||||
sigmaInstance.settings('defaultEdgeType', 'tapered');
|
||||
sigmaInstance.settings('defaultEdgeColor', '#356');
|
||||
this.state.design.setPalette(appStore.highResPalette);
|
||||
this.state.design.setStyles(appStore.highResStyle);
|
||||
}
|
||||
this.state.design.deprecate()
|
||||
sigmaInstance.refresh()
|
||||
this.state.design.apply()
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
emitter.on('searchQuery', this.doSearchQuery.bind(this));
|
||||
emitter.on('pathQuery', this.doPathQuery.bind(this));
|
||||
|
@ -116,6 +136,44 @@ export default class GraphContainer extends Component {
|
|||
emitter.on('export', this.export.bind(this))
|
||||
emitter.on('import', this.import.bind(this))
|
||||
emitter.on('clearDB', this.clearGraph.bind(this))
|
||||
emitter.on('changeGraphicsMode', this.setGraphicsMode.bind(this))
|
||||
emitter.on('ungroupNode', this.ungroupNode.bind(this))
|
||||
emitter.on('unfoldNode', this.unfoldEdgeNode.bind(this))
|
||||
emitter.on('collapseNode', this.foldEdgeNode.bind(this))
|
||||
emitter.on('resetZoom', this.resetZoom.bind(this))
|
||||
emitter.on('zoomIn', this.zoomIn.bind(this))
|
||||
emitter.on('zoomOut', this.zoomOut.bind(this))
|
||||
}
|
||||
|
||||
resetZoom(){
|
||||
sigma.misc.animation.camera(
|
||||
this.state.sigmaInstance.camera,
|
||||
{ x: 0, y: 0, ratio: 1.075 })
|
||||
;
|
||||
}
|
||||
|
||||
zoomOut(){
|
||||
var sigmaInstance = this.state.sigmaInstance
|
||||
var cam = sigmaInstance.camera;
|
||||
|
||||
sigma.misc.animation.camera(cam, {
|
||||
ratio: cam.ratio * cam.settings('zoomingRatio')
|
||||
}, {
|
||||
duration: sigmaInstance.settings('animationsTime')
|
||||
});
|
||||
}
|
||||
|
||||
zoomIn(){
|
||||
var sigmaInstance = this.state.sigmaInstance
|
||||
var cam = sigmaInstance.camera;
|
||||
|
||||
sigma.misc.animation.camera(cam,
|
||||
{
|
||||
ratio: cam.ratio / cam.settings('zoomingRatio')
|
||||
},
|
||||
{
|
||||
duration: sigmaInstance.settings('animationsTime')
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -253,6 +311,35 @@ export default class GraphContainer extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
unfoldEdgeNode(id){
|
||||
var sigmaInstance = this.state.sigmaInstance
|
||||
sigmaInstance.graph.read(sigmaInstance.graph.nodes(id).folded)
|
||||
this.state.design.deprecate()
|
||||
this.state.design.apply();
|
||||
this.relayout()
|
||||
}
|
||||
|
||||
foldEdgeNode(id){
|
||||
var sigmaInstance = this.state.sigmaInstance
|
||||
$.each(sigmaInstance.graph.nodes(id).folded.nodes, function(index, node){
|
||||
sigmaInstance.graph.dropNode(node.id)
|
||||
})
|
||||
sigmaInstance.refresh()
|
||||
this.state.design.deprecate();
|
||||
this.state.design.apply();
|
||||
this.relayout();
|
||||
}
|
||||
|
||||
ungroupNode(id){
|
||||
var sigmaInstance = this.state.sigmaInstance
|
||||
var node = sigmaInstance.graph.nodes(id)
|
||||
sigmaInstance.graph.dropNode(id);
|
||||
sigmaInstance.graph.read(node.folded)
|
||||
this.state.design.deprecate()
|
||||
this.state.design.apply()
|
||||
this.relayout();
|
||||
}
|
||||
|
||||
doSearchQuery(payload){
|
||||
this.doQueryNative({
|
||||
statement: payload,
|
||||
|
@ -388,13 +475,13 @@ export default class GraphContainer extends Component {
|
|||
var template = this.state.template;
|
||||
node.expand = false;
|
||||
node.collapse = false;
|
||||
// if (typeof node.folded != 'undefined' && !node.grouped) {
|
||||
// if (typeof sigmaInstance.graph.nodes(node.folded.nodes[0].id) == 'undefined') {
|
||||
// node.expand = true;
|
||||
// } else {
|
||||
// node.collapse = true;
|
||||
// }
|
||||
// }
|
||||
if (node.folded.nodes.length > 0 && !node.groupedNode) {
|
||||
if (typeof this.state.sigmaInstance.graph.nodes(node.folded.nodes[0].id) == 'undefined') {
|
||||
node.expand = true;
|
||||
} else {
|
||||
node.collapse = true;
|
||||
}
|
||||
}
|
||||
return Mustache.render(template, node);
|
||||
}.bind(this)
|
||||
}]
|
||||
|
|
|
@ -80,6 +80,10 @@ export default class MenuContainer extends Component {
|
|||
})
|
||||
}
|
||||
|
||||
_settingsClick(){
|
||||
emitter.emit('openSettings')
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="menudiv">
|
||||
|
@ -99,7 +103,7 @@ export default class MenuContainer extends Component {
|
|||
<MenuButton click={this._changeLayoutClick.bind(this)} hoverVal="Change Layout Type" glyphicon="fa fa-line-chart" />
|
||||
</div>
|
||||
<div>
|
||||
<MenuButton click={this._refreshClick.bind(this)} hoverVal="Settings" glyphicon="fa fa-cogs" />
|
||||
<MenuButton click={this._settingsClick.bind(this)} hoverVal="Settings" glyphicon="fa fa-cogs" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
var Modal = require('react-bootstrap').Modal;
|
||||
|
||||
export default class ClearingModal extends Component {
|
||||
constructor(){
|
||||
super();
|
||||
this.state = {
|
||||
open: false
|
||||
}
|
||||
}
|
||||
|
||||
openModal(){
|
||||
this.setState({open: true})
|
||||
}
|
||||
|
||||
closeModal(){
|
||||
this.setState({ open: false })
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
emitter.on('openClearingModal', this.openModal.bind(this))
|
||||
emitter.on('hideDBClearModal', this.closeModal.bind(this))
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Modal
|
||||
show={this.state.open}
|
||||
onHide={this.closeModal}
|
||||
aria-labelledby="ClearingModalHeader">
|
||||
|
||||
<Modal.Header closeButton={true}>
|
||||
<Modal.Title id="ClearingModalHeader">Clearing Database</Modal.Title>
|
||||
</Modal.Header>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -107,7 +107,7 @@ export default class SearchContainer extends Component {
|
|||
},
|
||||
afterSelect: function(selected) {
|
||||
if (!this.state.pathfindingIsOpen) {
|
||||
var statement = "MATCH (n) WHERE n.name =~ '(?i).*{}.*' RETURN n".format(escapeRegExp(selected));
|
||||
var statement = "MATCH (n) WHERE n.name = '{}' RETURN n".format(escapeRegExp(selected));
|
||||
emitter.emit('searchQuery', statement)
|
||||
} else {
|
||||
var start = jQuery(this.refs.searchbar).val();
|
||||
|
@ -125,7 +125,7 @@ export default class SearchContainer extends Component {
|
|||
jQuery(this.refs.pathbar).typeahead({
|
||||
source: function(query, process) {
|
||||
var options = fullAjax(
|
||||
"MATCH (n) WHERE n.name =~ '(?i).*{}.*' RETURN n.name LIMIT 10".format(escapeRegExp(query)),
|
||||
"MATCH (n) WHERE n.name = '{}' RETURN n".format(escapeRegExp(query)),
|
||||
function(json){
|
||||
var data = []
|
||||
$.each(json.results[0].data, function(index, d){
|
||||
|
@ -153,8 +153,23 @@ export default class SearchContainer extends Component {
|
|||
var key = e.keyCode ? e.keyCode : e.which
|
||||
var start = jQuery(this.refs.searchbar).val();
|
||||
var end = jQuery(this.refs.pathbar).val();
|
||||
var stop = false;
|
||||
|
||||
if (key === 13){
|
||||
$('.searchSelectorS > ul li').each(function(i){
|
||||
if($(this).hasClass('active')){
|
||||
stop = true
|
||||
}
|
||||
})
|
||||
|
||||
$('.searchSelectorP > ul li').each(function(i){
|
||||
if($(this).hasClass('active')){
|
||||
stop = true
|
||||
}
|
||||
})
|
||||
if (stop){
|
||||
return;
|
||||
}
|
||||
if (!this.state.pathfindingIsOpen) {
|
||||
var statement = "MATCH (n) WHERE n.name =~ '(?i).*{}.*' RETURN n".format(escapeRegExp(start));
|
||||
emitter.emit('searchQuery', statement)
|
||||
|
@ -171,7 +186,7 @@ export default class SearchContainer extends Component {
|
|||
render(){
|
||||
return (
|
||||
<div className="searchdiv">
|
||||
<div className="input-group input-group-unstyled">
|
||||
<div className="input-group input-group-unstyled searchSelectorS">
|
||||
<GlyphiconSpan
|
||||
tooltip={true}
|
||||
tooltipDir="bottom"
|
||||
|
@ -199,7 +214,7 @@ export default class SearchContainer extends Component {
|
|||
</GlyphiconSpan>
|
||||
</div>
|
||||
<div ref="pathfinding">
|
||||
<div className="input-group input-group-unstyled">
|
||||
<div className="input-group input-group-unstyled searchSelectorP">
|
||||
<GlyphiconSpan
|
||||
tooltip={false}
|
||||
classes="input-group-addon spanfix invisible">
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
export default class ZoomContainer extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="zoomBox">
|
||||
<div>
|
||||
<button onClick={function(){
|
||||
emitter.emit('zoomIn')
|
||||
}} className="btn zoomIn">
|
||||
<span className="fa fa-plus"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={function(){
|
||||
emitter.emit('resetZoom')
|
||||
}} className="btn">
|
||||
<span className="fa fa-ban" style={{width: 11}}></span>
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={function(){
|
||||
emitter.emit('zoomOut')
|
||||
}} className="btn zoomOut">
|
||||
<span className="fa fa-minus"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@
|
|||
<li onclick="emitter.emit('setEnd', '{{label}}')">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Set as Ending Node
|
||||
</li>
|
||||
<li onclick="emitter.emit('query', 'MATCH (n {name:\'{{label}}\'})<-[r:MemberOf]-(m) RETURN n,r,m)', '{{label}}')>
|
||||
<li onclick="emitter.emit('query', 'MATCH (n {name:\'{{label}}\'})<-[r:MemberOf]-(m) RETURN n,r,m)', '{{label}}')"">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Get Members
|
||||
</li>
|
||||
<li onclick="emitter.emit('query', 'MATCH (n {name:\'{{label}}\'})-[r:AdminTo]->(m) RETURN n,r,m', '{{label}}')">
|
||||
|
@ -48,18 +48,18 @@
|
|||
</li>
|
||||
{{/type_group}}
|
||||
{{#expand}}
|
||||
<li onclick="unfold({{id}})">
|
||||
<li onclick="emitter.emit('unfoldNode', {{id}}); appStore.currentTooltip.close()">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Expand
|
||||
</li>
|
||||
{{/expand}}
|
||||
{{#collapse}}
|
||||
<li onclick="collapse({{id}})">
|
||||
<li onclick="emitter.emit('collapseNode', {{id}}); appStore.currentTooltip.close()">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Collapse
|
||||
</li>
|
||||
{{/collapse}}
|
||||
{{#grouped}}
|
||||
<li onclick="ungroup({{id}})">
|
||||
{{#groupedNode}}
|
||||
<li onclick="emitter.emit('ungroupNode', {{id}}); appStore.currentTooltip.close()">
|
||||
<i class="glyphicon glyphicon-screenshot"> </i> Expand
|
||||
</li>
|
||||
{{/grouped}}
|
||||
{{/groupedNode}}
|
||||
</ul>
|
||||
|
|
|
@ -570,6 +570,10 @@ body {
|
|||
margin-top: -5px !important;
|
||||
}
|
||||
|
||||
.sliderfix i {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.center-align{
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
@ -126,7 +126,7 @@ global.appStore = {
|
|||
if (typeof conf.get('performance') === 'undefined'){
|
||||
conf.set('performance', {
|
||||
edge: 5,
|
||||
sibling: 5,
|
||||
sibling: 10,
|
||||
lowGraphics: false
|
||||
})
|
||||
}
|
||||
|
|
|
@ -260,6 +260,7 @@ export function findGraphPath(sigmaInstance, reverse, nodeid){
|
|||
}
|
||||
|
||||
export function clearDatabase(){
|
||||
emitter.emit('openClearingModal');
|
||||
deleteEdges()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue