diff --git a/www/js/hugvey_console.js b/www/js/hugvey_console.js
index af7620e..ef15563 100644
--- a/www/js/hugvey_console.js
+++ b/www/js/hugvey_console.js
@@ -11,18 +11,18 @@ class Panopticon {
hugveys: []
},
methods: {
- time_passed: function (hugvey, property) {
- console.log("property!", Date(hugvey[property] * 1000));
- return moment(Date(hugvey[property] * 1000)).fromNow();
+ time_passed: function( hugvey, property ) {
+ console.log( "property!", Date( hugvey[property] * 1000 ) );
+ return moment( Date( hugvey[property] * 1000 ) ).fromNow();
},
- loadNarrative: function(code, file) {
- return panopticon.loadNarrative(code, file);
+ loadNarrative: function( code, file ) {
+ return panopticon.loadNarrative( code, file );
}
}
} );
-
-
+
+
this.socket = new ReconnectingWebSocket( "ws://localhost:8888/ws", null, { debug: true, reconnectInterval: 3000 } );
this.graph = new Graph();
@@ -37,18 +37,18 @@ class Panopticon {
this.socket.addEventListener( 'message', ( e ) => {
let msg = JSON.parse( e.data );
if ( typeof msg['alert'] !== 'undefined' ) {
- alert(msg['alert']);
+ alert( msg['alert'] );
}
-
+
if ( typeof msg['action'] === 'undefined' ) {
console.error( "not a valid message: " + e.data );
return;
}
switch ( msg['action'] ) {
-
+
case 'status':
- this.hugveys.uptime = this.stringToHHMMSS(msg['uptime']);
+ this.hugveys.uptime = this.stringToHHMMSS( msg['uptime'] );
this.hugveys.languages = msg['languages'];
this.hugveys.hugveys = msg['hugveys'];
break;
@@ -57,54 +57,54 @@ class Panopticon {
}
send( msg ) {
- if(this.socket.readyState == WebSocket.OPEN) {
- this.socket.send( JSON.stringify( msg ) );
+ if ( this.socket.readyState == WebSocket.OPEN ) {
+ this.socket.send( JSON.stringify( msg ) );
} else {
- console.error("Socket not open: ", this.socket.readyState);
+ console.error( "Socket not open: ", this.socket.readyState );
}
}
getStatus() {
-// console.log('get status', this, panopticon);
+ // console.log('get status', this, panopticon);
panopticon.send( { action: 'get_status' } );
}
init() {
setInterval( this.getStatus, 3000 );
}
-
- stringToHHMMSS (string) {
- var sec_num = parseInt(string, 10); // don't forget the second param
- var hours = Math.floor(sec_num / 3600);
- var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
- var seconds = sec_num - (hours * 3600) - (minutes * 60);
- if (hours < 10) {hours = "0"+hours;}
- if (minutes < 10) {minutes = "0"+minutes;}
- if (seconds < 10) {seconds = "0"+seconds;}
- return hours+':'+minutes+':'+seconds;
+ stringToHHMMSS( string ) {
+ var sec_num = parseInt( string, 10 ); // don't forget the second param
+ var hours = Math.floor( sec_num / 3600 );
+ var minutes = Math.floor(( sec_num - ( hours * 3600 ) ) / 60 );
+ var seconds = sec_num - ( hours * 3600 ) - ( minutes * 60 );
+
+ if ( hours < 10 ) { hours = "0" + hours; }
+ if ( minutes < 10 ) { minutes = "0" + minutes; }
+ if ( seconds < 10 ) { seconds = "0" + seconds; }
+ return hours + ':' + minutes + ':' + seconds;
}
-
-
- loadNarrative(code, file) {
+
+
+ loadNarrative( code, file ) {
let req = new XMLHttpRequest();
let graph = this.graph;
- req.addEventListener("load", function(e){
- graph.loadData(JSON.parse(this.response), code);
-// console.log(, e);
- });
- req.open("GET", "/local/" + file);
+ req.addEventListener( "load", function( e ) {
+ graph.loadData( JSON.parse( this.response ), code );
+ // console.log(, e);
+ } );
+ req.open( "GET", "/local/" + file );
req.send();
}
- resume(hv_id) {
- this.send({ action: 'resume', hugvey: hv_id })
+ resume( hv_id ) {
+ this.send( { action: 'resume', hugvey: hv_id } )
}
- pause(hv_id) {
- this.send({ action: 'play', hugvey: hv_id })
+ pause( hv_id ) {
+ this.send( { action: 'play', hugvey: hv_id } )
}
- restart(hv_id) {
- this.send({ action: 'restart', hugvey: hv_id })
+ restart( hv_id ) {
+ this.send( { action: 'restart', hugvey: hv_id } )
}
}
@@ -113,16 +113,16 @@ class Panopticon {
window.addEventListener( 'load', function() {
panopticon = new Panopticon();
panopticon.init();
-});
+} );
-class Graph{
+class Graph {
constructor() {
this.width = 1280;
this.height = 1024;
this.nodeSize = 80;
this.maxChars = 16;
- this.svg = d3.select('#graph');
- this.container = d3.select('#container');
+ this.svg = d3.select( '#graph' );
+ this.container = d3.select( '#container' );
this.selectedMsg = null;
this.language_code = null;
this.messages = []; // initialise empty array. For the simulation, make sure we keep the same array object
@@ -132,79 +132,79 @@ class Graph{
let graph = this;
this.controlDown = false;
- document.addEventListener('keydown', function(e){
- console.log(e);
- if(e.which == "17") {
+ document.addEventListener( 'keydown', function( e ) {
+ console.log( e );
+ if ( e.which == "17" ) {
graph.controlDown = true;
- document.body.classList.add('controlDown');
+ document.body.classList.add( 'controlDown' );
}
- });
- document.addEventListener('keyup', function(e){
- console.log(e);
- if(e.which == "17") {
+ } );
+ document.addEventListener( 'keyup', function( e ) {
+ console.log( e );
+ if ( e.which == "17" ) {
graph.controlDown = false;
- document.body.classList.remove('controlDown');
+ document.body.classList.remove( 'controlDown' );
}
- });
+ } );
let c = this.container;
- let zoomed = function(){
- c.attr("transform", d3.event.transform);
+ let zoomed = function() {
+ c.attr( "transform", d3.event.transform );
}
- this.svg.call(d3.zoom()
- .scaleExtent([1 / 2, 8])
- .on("zoom", zoomed));
+ this.svg.call( d3.zoom()
+ .scaleExtent( [1 / 2, 8] )
+ .on( "zoom", zoomed ) );
- this.nodesG = this.container.append("g")
- .attr("id", "nodes")
+ this.nodesG = this.container.append( "g" )
+ .attr( "id", "nodes" )
- this.linkG = this.container.append("g")
- .attr("id", "links");
+ this.linkG = this.container.append( "g" )
+ .attr( "id", "links" );
- document.getElementById('btn-save').addEventListener('click', function(e){ graph.saveJson(); });
- document.getElementById('btn-addMsg').addEventListener('click', function(e){ graph.createMsg(); });
+ document.getElementById( 'btn-save' ).addEventListener( 'click', function( e ) { graph.saveJson(); } );
+ document.getElementById( 'btn-addMsg' ).addEventListener( 'click', function( e ) { graph.createMsg(); } );
}
- clickMsg(msg) {
+ clickMsg( msg ) {
// event when a message is clicked.
- console.log(msg);
+ console.log( msg );
- if(this.controlDown) {
- this.secondarySelectMsg(msg);
+ if ( this.controlDown ) {
+ this.secondarySelectMsg( msg );
} else {
- this.selectMsg(msg);
+ this.selectMsg( msg );
}
}
- secondarySelectMsg(msg) {
- if(this.selectedMsg !== null) {
- this.addDirection(this.selectedMsg, msg);
+ secondarySelectMsg( msg ) {
+ if ( this.selectedMsg !== null ) {
+ this.addDirection( this.selectedMsg, msg );
} else {
- console.error('No message selected as Source');
+ console.error( 'No message selected as Source' );
}
}
- selectMsg(msg) {
- let selectedEls = document.getElementsByClassName('selectedMsg');
- while(selectedEls.length > 0){
- selectedEls[0].classList.remove('selectedMsg');
+ selectMsg( msg ) {
+ let selectedEls = document.getElementsByClassName( 'selectedMsg' );
+ while ( selectedEls.length > 0 ) {
+ selectedEls[0].classList.remove( 'selectedMsg' );
}
- document.getElementById(msg['@id']).classList.add('selectedMsg');
+ document.getElementById( msg['@id'] ).classList.add( 'selectedMsg' );
this.selectedMsg = msg;
- this.showMsg(msg);
+ this.showMsg( msg );
}
updateMsg() {
// used eg. after a condition creation.
- this.showMsg(this.selectedMsg);
+ this.showMsg( this.selectedMsg );
}
- showMsg(msg) {
- let msgEl = document.getElementById('msg');
+ showMsg( msg ) {
+ let msgEl = document.getElementById( 'msg' );
msgEl.innerHTML = "";
- let startAttributes = {
+ let startAttributes = {
'name': msg['@id'] + '-start',
'disabled': true,
'type': 'checkbox',
@@ -212,14 +212,14 @@ class Graph{
'change': this.getEditEventListener()
}
}
- if(msg['start'] == true) {
+ if ( msg['start'] == true ) {
startAttributes['checked'] = 'checked';
}
- let msgInfoEl = crel('div', {'class': 'msg__info'},
- crel('h1', {'class':'msg__id'}, msg['@id']),
- crel('label',
- crel('span', 'Text'),
- crel('input', {
+ let msgInfoEl = crel( 'div', { 'class': 'msg__info' },
+ crel( 'h1', { 'class': 'msg__id' }, msg['@id'] ),
+ crel( 'label',
+ crel( 'span', 'Text' ),
+ crel( 'input', {
'name': msg['@id'] + '-text',
'value': msg['text'],
'on': {
@@ -227,274 +227,299 @@ class Graph{
}
} )
),
- crel('label',
- crel('span', 'Start'),
- crel('input', startAttributes)
+ crel( 'label',
+ crel( 'span', 'Start' ),
+ crel( 'input', startAttributes )
)
);
- msgEl.appendChild(msgInfoEl);
+ msgEl.appendChild( msgInfoEl );
+
+// crel('form')
// let directionHEl = document.createElement('h2');
// directionHEl.innerHTML = "Directions";
- let fromDirections =[] , toDirections = [];
+ let fromDirections = [], toDirections = [];
- for(let direction of this.getDirectionsTo(msg)) {
- toDirections.push(this.getDirectionEl(direction, msg));
+ for ( let direction of this.getDirectionsTo( msg ) ) {
+ toDirections.push( this.getDirectionEl( direction, msg ) );
}
- for(let direction of this.getDirectionsFrom(msg)) {
- fromDirections.push(this.getDirectionEl(direction, msg));
+ for ( let direction of this.getDirectionsFrom( msg ) ) {
+ fromDirections.push( this.getDirectionEl( direction, msg ) );
}
- let directionsEl = crel('div', {'class': 'directions'},
- crel('h2', 'Directions'),
+ let directionsEl = crel( 'div', { 'class': 'directions' },
+ crel( 'h2', 'Directions' ),
...toDirections, ...fromDirections
);
- msgEl.appendChild(directionsEl);
+ msgEl.appendChild( directionsEl );
}
- getDirectionEl(direction, msg) {
- let directionEl = document.createElement('div');
- if(direction['source'] == msg) {
- directionEl.innerHTML = `
To ${direction['target']['@id']}
`;
- } else {
- directionEl.innerHTML = `
From ${direction['source']['@id']}
`;
- }
- let del = document.createElement('div');
- del.innerHTML = "delete";
- del.classList.add("deleteBtn");
+ getDirectionEl( direction, msg ) {
let g = this;
- del.addEventListener('click', (e) => g.rmDirection(direction));
- directionEl.appendChild(del);
-
- // TODO; conditions
-
- for(let conditionId of direction['conditions']) {
- let condition = this.getNodeById(conditionId);
- directionEl.appendChild(this.getEditConditionFormEl(condition, direction));
+ let directionEl = crel('div',
+ {
+ 'class': 'direction ' + (direction['source'] == msg ? 'dir-to' : 'dir-from'),
+ 'on': {
+ 'mouseover': function(e) {
+ console.log('over', direction['@id']);
+ directionEl.classList.add('dir-highlight');
+ document.getElementById(direction['@id']).classList.add('dir-highlight');
+ },
+ 'mouseout': function(e) {
+ console.log('& out ', direction['@id']);
+ directionEl.classList.remove('dir-highlight');
+ document.getElementById(direction['@id']).classList.remove('dir-highlight');
+ }
+ }
+ },
+ crel(
+ 'h3',
+ {'title': direction['@id']},
+ direction['source'] == msg ? `To ${direction['target']['@id']}`: `From ${direction['source']['@id']}`
+ ),
+ crel('div', {
+ 'class':'btn btn--delete',
+ 'on': {
+ 'click': ( e ) => g.rmDirection( direction )
+ }
+ }, 'delete')
+ );
+
+ for ( let conditionId of direction['conditions'] ) {
+ let condition = this.getNodeById( conditionId );
+ directionEl.appendChild( this.getEditConditionFormEl( condition, direction ) );
}
- directionEl.appendChild(this.getAddConditionFormEl(direction));
+ directionEl.appendChild( this.getAddConditionFormEl( direction ) );
return directionEl;
}
- getEditConditionFormEl(condition, direction) {
- let conditionEl = crel('div', {'class': 'condition condition--edit'},
- crel('h4', {'title': condition['@id']}, condition['type'])
+ getEditConditionFormEl( condition, direction ) {
+ let conditionEl = crel( 'div', { 'class': 'condition condition--edit' },
+ crel( 'h4', { 'title': condition['@id'] }, condition['type'] )
)
- let labelLabel = document.createElement('label');
+ let labelLabel = document.createElement( 'label' );
labelLabel.innerHTML = "Description";
- let labelInput = crel('input',{
- 'name': `${condition['@id']}-label`,
- 'value': typeof condition['label'] == 'undefined' ? "" : condition['label'],
- 'on': {
- 'change': this.getEditEventListener()
- }
- });
- labelLabel.appendChild(labelInput);
- conditionEl.appendChild(labelLabel);
+ let labelInput = crel( 'input', {
+ 'name': `${condition['@id']}-label`,
+ 'value': typeof condition['label'] == 'undefined' ? "" : condition['label'],
+ 'on': {
+ 'change': this.getEditEventListener()
+ }
+ } );
+ labelLabel.appendChild( labelInput );
+ conditionEl.appendChild( labelLabel );
- for(let v in condition['vars']) {
- let varLabel = document.createElement('label');
+ for ( let v in condition['vars'] ) {
+ let varLabel = document.createElement( 'label' );
varLabel.innerHTML = v;
- let varInput = document.createElement('input');
- if(v == 'seconds') {
+ let varInput = document.createElement( 'input' );
+ if ( v == 'seconds' ) {
varInput.type = 'number';
}
varInput.name = `${condition['@id']}-vars.${v}`;
varInput.value = condition['vars'][v];
- varInput.addEventListener('change', this.getEditEventListener());
- varLabel.appendChild(varInput);
- conditionEl.appendChild(varLabel);
+ varInput.addEventListener( 'change', this.getEditEventListener() );
+ varLabel.appendChild( varInput );
+ conditionEl.appendChild( varLabel );
}
return conditionEl;
}
getConditionTypes() {
- if(typeof this.conditionTypes === 'undefined') {
+ if ( typeof this.conditionTypes === 'undefined' ) {
// type: vars: attribtes for crel()
this.conditionTypes = {
'timeout': {
- 'seconds': {'type': 'number', 'value': 10, 'min':0, 'step': 0.1}
+ 'seconds': { 'type': 'number', 'value': 10, 'min': 0, 'step': 0.1 }
},
'replyContains': {
- 'regex': {'value': '.+'}
+ 'regex': { 'value': '.+' }
}
}
}
return this.conditionTypes;
}
- fillConditionFormForType(conditionForm, type) {
+ fillConditionFormForType( conditionForm, type ) {
conditionForm.innerHTML = "";
let vars = this.getConditionTypes()[type];
- for(let v in vars){
+ for ( let v in vars ) {
let attr = vars[v];
attr['name'] = v;
conditionForm.appendChild(
- crel('label',
- crel('span', v),
- crel('input', attr)
+ crel( 'label',
+ crel( 'span', v ),
+ crel( 'input', attr )
)
);
}
}
- getAddConditionFormEl(direction) {
+ getAddConditionFormEl( direction ) {
let optionEls = [];
let types = this.getConditionTypes();
- for(let type in types) {
- optionEls.push(crel('option', type));
+ for ( let type in types ) {
+ optionEls.push( crel( 'option', type ) );
}
- let conditionForm = crel('div', {'class': 'condition--vars'});
+ let conditionForm = crel( 'div', { 'class': 'condition--vars' } );
let g = this;
- let addConditionEl = crel('div', {'class': 'condition condition--add'},
- crel('form', {
- 'on': {
- 'submit': function(e) {
- e.preventDefault();
- let form = new FormData(e.target);
- console.log('submit', form);
- let type = form.get('type');
- form.delete('type');
- let label = form.get('label');
- form.delete('label');
- let vars = {};
- for(var pair of form.entries()) {
- vars[pair[0]] = pair[1];
- }
- g.addConditionForDirection(type, label, vars, direction);
+ let addConditionEl = crel( 'div', { 'class': 'condition condition--add' },
+ crel( 'form', {
+ 'on': {
+ 'submit': function( e ) {
+ e.preventDefault();
+ let form = new FormData( e.target );
+ console.log( 'submit', form );
+ let type = form.get( 'type' );
+ form.delete( 'type' );
+ let label = form.get( 'label' );
+ form.delete( 'label' );
+ let vars = {};
+ for ( var pair of form.entries() ) {
+ vars[pair[0]] = pair[1];
}
+ g.addConditionForDirection( type, label, vars, direction );
}
- },
- crel("h4", "Create New Condition"),
- crel("label",
- crel('span', "Type"),
- crel('select', {
- 'name': 'type',
- 'on': {
- 'change': function(e){
- g.fillConditionFormForType(conditionForm, e.target.value);
+ }
+ },
+ crel( "h4", {
+ 'class': "divToggle",
+ 'on': {
+ 'click': function(e) { this.classList.toggle('opened'); }
+ }
+ }, "Create New Condition" ),
+ crel('div', {'class': 'divToggle-target'},
+ crel( "label",
+ crel( 'span', "Type" ),
+ crel( 'select', {
+ 'name': 'type',
+ 'on': {
+ 'change': function( e ) {
+ g.fillConditionFormForType( conditionForm, e.target.value );
+ }
}
- }}, optionEls),
- ),
- crel("label",
- crel('span', "Description"),
- crel('input', {'name': 'label'})
- ),
- conditionForm,
- crel('input', {
- 'type':'submit',
- 'value':'create'
- })
+ }, optionEls ),
+ ),
+ crel( "label",
+ crel( 'span', "Description" ),
+ crel( 'input', { 'name': 'label' } )
+ ),
+ conditionForm,
+ crel( 'input', {
+ 'type': 'submit',
+ 'value': 'create'
+ } )
+ )
)
);
- this.fillConditionFormForType(conditionForm, optionEls[0].value);
+ this.fillConditionFormForType( conditionForm, optionEls[0].value );
return addConditionEl;
}
- rmConditionFromDirection(condition, direction) {
+ rmConditionFromDirection( condition, direction ) {
let id = condition['@id'];
// TODO
- if(typeof direction != 'undefined') {
+ if ( typeof direction != 'undefined' ) {
}
- this._rmNode(id);
+ this._rmNode( id );
}
- getConditionEl(condition) {
- let conditionEl = document.createElement('div');
+ getConditionEl( condition ) {
+ let conditionEl = document.createElement( 'div' );
return conditionEl;
}
- getDirectionsFrom(msg) {
- return this.directions.filter(d => d['source'] == msg);
+ getDirectionsFrom( msg ) {
+ return this.directions.filter( d => d['source'] == msg );
}
- getDirectionsTo(msg) {
- return this.directions.filter(d => d['target'] == msg);
+ getDirectionsTo( msg ) {
+ return this.directions.filter( d => d['target'] == msg );
}
addMsg() {
let msg = {
- "@id": "n" + Date.now().toString(36),
+ "@id": this.language_code.substring( 0, 2 ) + "-n" + Date.now().toString( 36 ),
"@type": "Msg",
"text": "New",
"start": false
}
- this.data.push(msg);
+ this.data.push( msg );
this.updateFromData();
this.build();
return msg;
}
- rmMsg(msg) {
- let invalidatedDirections = this.directions.filter(d => d['source'] == msg || d['target'] == msg);
- console.log('invalidated', invalidatedDirections);
- for(let dir of invalidatedDirections) {
- let i = this.data.indexOf(dir);
- this.data.splice(i, 1);
+ rmMsg( msg ) {
+ let invalidatedDirections = this.directions.filter( d => d['source'] == msg || d['target'] == msg );
+ console.log( 'invalidated', invalidatedDirections );
+ for ( let dir of invalidatedDirections ) {
+ let i = this.data.indexOf( dir );
+ this.data.splice( i, 1 );
}
- this._rmNode(msg);
+ this._rmNode( msg );
}
- _rmNode(node) {
+ _rmNode( node ) {
// remove msg/direction/condition/etc
- let i = this.data.indexOf(node);
- this.data.splice(i, 1);
+ let i = this.data.indexOf( node );
+ this.data.splice( i, 1 );
this.updateFromData();
this.build();
return this.data;
}
- addConditionForDirection(type, label, vars, direction) {
- let con = this.addCondition(type, label, vars, true);
- direction['conditions'].push(con['@id']);
+ addConditionForDirection( type, label, vars, direction ) {
+ let con = this.addCondition( type, label, vars, true );
+ direction['conditions'].push( con['@id'] );
this.updateFromData();
this.build();
this.updateMsg();
}
- addCondition(type, label, vars, skip) {
+ addCondition( type, label, vars, skip ) {
let con = {
- "@id": "c" + Date.now().toString(36),
+ "@id": this.language_code.substring( 0, 2 ) + "-c" + Date.now().toString( 36 ),
"@type": "Condition",
"type": type,
"label": label,
"vars": vars
}
- this.data.push(con);
- if(skip !== true) {
+ this.data.push( con );
+ if ( skip !== true ) {
this.updateFromData();
this.build();
}
return con;
}
- addDirection(source, target) {
+ addDirection( source, target ) {
let dir = {
- "@id": "d" + Date.now().toString(36),
+ "@id": this.language_code.substring( 0, 2 ) + "-d" + Date.now().toString( 36 ),
"@type": "Direction",
"source": source,
"target": target,
"conditions": []
}
- this.data.push(dir);
+ this.data.push( dir );
this.updateFromData();
this.build();
return dir;
}
- rmDirection(dir) {
- this._rmNode(dir);
+ rmDirection( dir ) {
+ this._rmNode( dir );
}
createMsg() {
@@ -502,29 +527,29 @@ class Graph{
this.build();
}
- getNodeById(id) {
- return this.data.filter(node => node['@id'] == id)[0];
+ getNodeById( id ) {
+ return this.data.filter( node => node['@id'] == id )[0];
}
/**
* Use wrapper method, because for event handlers 'this' will refer to
* the input object
*/
- getEditEventListener(){
+ getEditEventListener() {
let graph = this;
- let el = function(e){
- let parts = e.srcElement.name.split('-');
+ let el = function( e ) {
+ let parts = e.srcElement.name.split( '-' );
let id = parts[0], field = parts[1];
- console.log(this, graph);
- let node = graph.getNodeById(id);
- let path = field.split('.'); // use vars.test to set ['vars']['test'] = value
- var res=node;
- for (var i=0;i
node['@type'] == 'Msg');
- this.directions = this.data.filter((node) => node['@type'] == 'Direction');
- this.conditions = this.data.filter((node) => node['@type'] == 'Condition');
- this.interruptions = this.data.filter((node) => node['@type'] == 'Interruption');
+ this.messages = this.data.filter(( node ) => node['@type'] == 'Msg' );
+ this.directions = this.data.filter(( node ) => node['@type'] == 'Direction' );
+ this.conditions = this.data.filter(( node ) => node['@type'] == 'Condition' );
+ this.interruptions = this.data.filter(( node ) => node['@type'] == 'Interruption' );
+
+ document.getElementById('current_lang').innerHTML = "";
+ document.getElementById('current_lang').appendChild(crel('span', {
+ 'class': 'flag-icon ' + this.language_code
+ }));
// save state;
this.saveState();
}
saveState() {
- window.localStorage.setItem("lastState", this.getJsonString());
+ window.localStorage.setItem( "lastState", this.getJsonString() );
}
hasSavedState() {
- return window.localStorage.getItem("lastState") !== null;
+ return window.localStorage.getItem( "lastState" ) !== null;
}
loadFromState() {
- this.loadData(JSON.parse(window.localStorage.getItem("lastState")));
+ this.loadData( JSON.parse( window.localStorage.getItem( "lastState" ) ) );
}
- build(isInit) {
- this.simulation = d3.forceSimulation(this.messages)
- .force("link", d3.forceLink(this.directions).id(d => d['@id']))
- .force("charge", d3.forceManyBody().strength(-1000))
- .force("center", d3.forceCenter(this.width / 2, this.height / 2))
- .force("collide", d3.forceCollide(this.nodeSize*2))
+ build( isInit ) {
+ this.simulation = d3.forceSimulation( this.messages )
+ .force( "link", d3.forceLink( this.directions ).id( d => d['@id'] ) )
+ .force( "charge", d3.forceManyBody().strength( -1000 ) )
+ .force( "center", d3.forceCenter( this.width / 2, this.height / 2 ) )
+ .force( "collide", d3.forceCollide( this.nodeSize * 2 ) )
;
// Update existing nodes
let node = this.nodesG
- .selectAll("g")
- .data(this.messages, n => n['@id'])
- ;
+ .selectAll( "g" )
+ .data( this.messages, n => n['@id'] )
+ ;
// Update existing nodes
let newNode = node.enter();
- let newNodeG = newNode.append("g")
- .attr('id', d => d['@id'])
- .call(d3.drag(this.simulation))
- .on('click', function(d){
- this.clickMsg(d);
- }.bind(this))
- ;
- console.log('a');
- let circle = newNodeG.append("circle")
- .attr('r', this.nodeSize)
- // .text(d => d.id)
- ;
- let text = newNodeG.append("text")
- ;
+ let newNodeG = newNode.append( "g" )
+ .attr( 'id', d => d['@id'] )
+ .call( d3.drag( this.simulation ) )
+ .on( 'click', function( d ) {
+ this.clickMsg( d );
+ }.bind( this ) )
+ ;
+ console.log( 'a' );
+ let circle = newNodeG.append( "circle" )
+ .attr( 'r', this.nodeSize )
+ // .text(d => d.id)
+ ;
+ let textId = newNodeG.append( "text" ).attr( 'class', 'msg_id' );
+ let textContent = newNodeG.append( "text" ).attr( 'class', 'msg_txt' );
- // remove
- node.exit().remove();
- node = node.merge(newNodeG);
+ // remove
+ node.exit().remove();
+ node = node.merge( newNodeG );
- // for all existing nodes:
- node.attr('class', msg => {
- let classes = [];
- if( this.selectedMsg == msg) classes.push('selectedMsg');
- if( msg['start'] == true ) classes.push('startMsg');
- if(this.getDirectionsFrom(msg).length < 1) {
- classes.push('endMsg');
- if(this.getDirectionsTo(msg).length < 1) classes.push('orphanedMsg');
- }
+ // for all existing nodes:
+ node.attr( 'class', msg => {
+ let classes = [];
+ if ( this.selectedMsg == msg ) classes.push( 'selectedMsg' );
+ if ( msg['start'] == true ) classes.push( 'startMsg' );
+ if ( this.getDirectionsFrom( msg ).length < 1 ) {
+ classes.push( 'endMsg' );
+ if ( this.getDirectionsTo( msg ).length < 1 ) classes.push( 'orphanedMsg' );
+ }
- return classes.join(' ');
- })
+ return classes.join( ' ' );
+ } )
- let link = this.linkG
- .selectAll("line")
- .data(this.directions)
- ;
- let newLink = link.enter()
- .append("line")
- ;
- //remove
- link.exit().remove();
- link = link.merge(newLink);
+ let link = this.linkG
+ .selectAll( "line" )
+ .data( this.directions )
+ ;
+ let newLink = link.enter()
+ .append( "line" )
+ ;
+
+ //remove
+ link.exit().remove();
+ link = link.merge( newLink );
- link.attr('class', l => { return `link ` + (l['conditions'].length == 0 ? "link--noconditions" : "link--withconditions"); });
+ link.attr( 'class', l => { return `link ` + ( l['conditions'].length == 0 ? "link--noconditions" : "link--withconditions" ); } );
+ link.attr('id', (l) => l['@id']);
- // console.log('c');
- let formatText = (t) => {
- if(t.length > this.maxChars) {
- return t.substr(0, this.maxChars - 3) + '...';
- } else {
- return t;
- }
- };
+ // console.log('c');
+ let formatText = ( t ) => {
+ if ( t.length > this.maxChars ) {
+ return t.substr( 0, this.maxChars - 3 ) + '...';
+ } else {
+ return t;
+ }
+ };
- node.selectAll("text").text(d => formatText(`(${d['@id']}) ${d['text']}`));
- // console.log('q');
- // // TODO: update text
- // let text = newNodeG.append("text")
- // // .attr('stroke', "black")
- // .text(d => formatText(`(${d['@id']}) ${d['text']}`))
- // // .attr('title', d => d.label)
- // ;
+ node.selectAll( "text.msg_id" ).text( d => d['@id'] );
+ node.selectAll( "text.msg_txt" ).text( d => formatText( `${d['text']}` ) );
+ // console.log('q');
+ // // TODO: update text
+ // let text = newNodeG.append("text")
+ // // .attr('stroke', "black")
+ // .text(d => formatText(`(${d['@id']}) ${d['text']}`))
+ // // .attr('title', d => d.label)
+ // ;
- let n = this.nodesG;
- this.simulation.on("tick", () => {
+ let n = this.nodesG;
+ this.simulation.on( "tick", () => {
link
- .each(function(d){
+ .each( function( d ) {
let sourceX, targetX, midX, dx, dy, angle;
// This mess makes the arrows exactly perfect.
// thanks to http://bl.ocks.org/curran/9b73eb564c1c8a3d8f3ab207de364bf4
- if( d.source.x < d.target.x ){
- sourceX = d.source.x;
- targetX = d.target.x;
- } else if( d.target.x < d.source.x ){
- targetX = d.target.x;
- sourceX = d.source.x;
- } else if (d.target.isCircle) {
- targetX = sourceX = d.target.x;
- } else if (d.source.isCircle) {
- targetX = sourceX = d.source.x;
+ if ( d.source.x < d.target.x ) {
+ sourceX = d.source.x;
+ targetX = d.target.x;
+ } else if ( d.target.x < d.source.x ) {
+ targetX = d.target.x;
+ sourceX = d.source.x;
+ } else if ( d.target.isCircle ) {
+ targetX = sourceX = d.target.x;
+ } else if ( d.source.isCircle ) {
+ targetX = sourceX = d.source.x;
} else {
- midX = (d.source.x + d.target.x) / 2;
- if(midX > d.target.x){
- midX = d.target.x;
- } else if(midX > d.source.x){
- midX = d.source.x;
- } else if(midX < d.target.x){
- midX = d.target.x;
- } else if(midX < d.source.x){
- midX = d.source.x;
- }
- targetX = sourceX = midX;
+ midX = ( d.source.x + d.target.x ) / 2;
+ if ( midX > d.target.x ) {
+ midX = d.target.x;
+ } else if ( midX > d.source.x ) {
+ midX = d.source.x;
+ } else if ( midX < d.target.x ) {
+ midX = d.target.x;
+ } else if ( midX < d.source.x ) {
+ midX = d.source.x;
+ }
+ targetX = sourceX = midX;
}
dx = targetX - sourceX;
dy = d.target.y - d.source.y;
- angle = Math.atan2(dx, dy);
+ angle = Math.atan2( dx, dy );
// Compute the line endpoint such that the arrow
// is touching the edge of the node rectangle perfectly.
- d.sourceX = sourceX + Math.sin(angle) * this.nodeSize;
- d.targetX = targetX - Math.sin(angle) * this.nodeSize;
- d.sourceY = d.source.y + Math.cos(angle) * this.nodeSize;
- d.targetY = d.target.y - Math.cos(angle) * this.nodeSize;
- }.bind(this))
- .attr("x1", function(d) { return d.sourceX; })
- .attr("y1", function(d) { return d.sourceY; })
- .attr("x2", function(d) { return d.targetX; })
- .attr("y2", function(d) { return d.targetY; });
+ d.sourceX = sourceX + Math.sin( angle ) * this.nodeSize;
+ d.targetX = targetX - Math.sin( angle ) * this.nodeSize;
+ d.sourceY = d.source.y + Math.cos( angle ) * this.nodeSize;
+ d.targetY = d.target.y - Math.cos( angle ) * this.nodeSize;
+ }.bind( this ) )
+ .attr( "x1", function( d ) { return d.sourceX; } )
+ .attr( "y1", function( d ) { return d.sourceY; } )
+ .attr( "x2", function( d ) { return d.targetX; } )
+ .attr( "y2", function( d ) { return d.targetY; } );
- node.attr("transform", d => `translate(${d.x},${d.y})`);
- // .attr("cy", d => d.y);
- });
+ node.attr( "transform", d => `translate(${d.x},${d.y})` );
+ // .attr("cy", d => d.y);
+ } );
- // this.simulation.alpha(1);
- // this.simulation.restart();
- if(typeof isInit != 'undefined' && isInit) {
- for (let i = 0, n = Math.ceil(Math.log(this.simulation.alphaMin()) / Math.log(1 - this.simulation.alphaDecay())); i < n; ++i) {
- this.simulation.tick();
- }
+ // this.simulation.alpha(1);
+ // this.simulation.restart();
+ if ( typeof isInit != 'undefined' && isInit ) {
+ for ( let i = 0, n = Math.ceil( Math.log( this.simulation.alphaMin() ) / Math.log( 1 - this.simulation.alphaDecay() ) ); i < n; ++i ) {
+ this.simulation.tick();
}
- return this.svg.node();
}
+ return this.svg.node();
+ }
}
diff --git a/www/scss/styles.scss b/www/scss/styles.scss
index 0cb1bcc..b6dbcca 100644
--- a/www/scss/styles.scss
+++ b/www/scss/styles.scss
@@ -1,15 +1,31 @@
+$status_width: 430px;
+$status_width_open: 860px;
+
body{
font-family: "Noto Sans", sans-serif;
margin: 0;
}
-.btn{
+.btn, input[type="submit"]{
display:inline-block;
cursor: pointer;
background: #333;
padding: 5px;
color: white;
border-radius: 5px;
+ margin-right: 5px;
+ white-space: nowrap;
+ border: none;
+
+ &:hover{
+ background: #666;
+ }
+}
+
+@keyframes dash-animation {
+ to {
+ stroke-dashoffset: -1000;
+ }
}
#interface{
@@ -17,6 +33,9 @@ body{
flex-direction: row;
height: 100vh;
width: 100vw;
+
+ &.showStatus{
+ }
}
#status{
@@ -33,6 +52,10 @@ body{
border: solid 1px;
box-sizing: border-box;
position: relative;
+
+ overview{
+ width: 100% / 3 * 2;
+ }
}
.hugvey{
@@ -72,11 +95,13 @@ body{
#story{
position: relative;
+ width: calc(100% - #{$status_width});
#controls{
position:absolute;
top: 5px;
left: 5px;
+ white-space: nowrap;
}
svg#graph{
width: 100%;
@@ -106,15 +131,29 @@ body{
font-size: 11pt;
font-family: sans-serif;
fill: white;
+
+ &.msg_id {
+ transform: translateY(-20px);
+ opacity: .5;
+ }
+ &.msg_txt{
+ font-weight: bold;
+ }
}
line{
marker-end: url('#arrowHead');
stroke-width: 2px;
stroke: black;
- }
- line.link--noconditions{
- stroke-dasharray: 5 4;
- stroke: red;
+
+ &.link--noconditions{
+ stroke-dasharray: 5 4;
+ stroke: red;
+ }
+ &.dir-highlight{
+ stroke-dasharray: 5;
+ animation: dash-animation 20s infinite linear;
+ stroke-width: 3px;
+ }
}
label::after {
content: '';
@@ -148,6 +187,27 @@ body{
margin-bottom: 10px;
background:lightgray;
}
+
+ .direction{
+ position: relative;
+ h3{
+ margin-top:0;
+ }
+ .btn--delete{
+ position: absolute;
+ top: 5px;
+ right: 0px;
+ }
+
+ .condition--add{
+ h4{
+ margin: 0;
+ }
+ h4 +div {
+ margin-top: 10px;
+ }
+ }
+ }
}
#nodes g:hover circle,
@@ -179,4 +239,54 @@ body{
.condition--add{
/* text-align: center; */
}
-}
\ No newline at end of file
+}
+
+
+.flag-icon {
+ background-size: contain;
+ background-position: 50%;
+ background-repeat: no-repeat;
+ position: relative;
+ display: inline-block;
+ width: (4 / 3) * 1em;
+ line-height: 1em;
+ &:before {
+ content: '\00a0';
+ }
+ &.flag-icon-squared {
+ width: 1em;
+ }
+
+ &.en-GB {
+ background-image: url('/images/gb.svg');
+ }
+
+ &.de-DE {
+ background-image: url('/images/de.svg');
+ }
+
+ &.fr-FR {
+ background-image: url('/images/fr.svg');
+ }
+ &.nl-NL {
+ background-image: url('/images/nl.svg');
+ }
+
+}
+
+.divToggle{
+ cursor: pointer;
+ &:hover{
+ text-decoration: underline;
+ }
+ &.opened {
+ + div{
+ display: block;
+ }
+ }
+ + div{
+ display: none;
+ }
+}
+
+