Final Countdown

master
Rohan Vazarkar 2016-07-29 22:09:00 -04:00
parent cc7ad31ed4
commit c3e8fa7dc4
12 changed files with 321 additions and 21 deletions

View File

@ -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">

View File

@ -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>
);
};

View File

@ -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">&times;</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>
);
}
}

View File

@ -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)
}]

View File

@ -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>
);

View File

@ -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>
);
}
}

View File

@ -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">

View File

@ -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>
);
}
}

View File

@ -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>

View File

@ -570,6 +570,10 @@ body {
margin-top: -5px !important;
}
.sliderfix i {
margin-left: 5px;
}
.center-align{
text-align: center;
}

View File

@ -126,7 +126,7 @@ global.appStore = {
if (typeof conf.get('performance') === 'undefined'){
conf.set('performance', {
edge: 5,
sibling: 5,
sibling: 10,
lowGraphics: false
})
}

View File

@ -260,6 +260,7 @@ export function findGraphPath(sigmaInstance, reverse, nodeid){
}
export function clearDatabase(){
emitter.emit('openClearingModal');
deleteEdges()
}