2019-01-23 14:26:44 +00:00
var panopticon ;
class Panopticon {
constructor ( ) {
console . log ( "Init panopticon" ) ;
2019-04-27 10:13:34 +00:00
this . hasGraph = document . body . classList . contains ( 'editor' ) ;
2019-01-25 09:43:55 +00:00
this . languages = [ ]
2019-01-25 14:45:46 +00:00
// this.selectedHugvey = null;
2019-01-23 14:26:44 +00:00
this . hugveys = new Vue ( {
2019-01-25 14:45:46 +00:00
el : "#interface" ,
2019-01-23 14:26:44 +00:00
data : {
uptime : 0 ,
2019-07-03 15:54:14 +00:00
loop _timer : 0 ,
2019-01-23 14:26:44 +00:00
languages : [ ] ,
2019-01-25 14:45:46 +00:00
hugveys : [ ] ,
selectedId : null ,
2019-04-27 13:33:51 +00:00
logbook : "" ,
2019-05-10 14:59:14 +00:00
logbookId : null ,
2019-07-03 16:33:31 +00:00
selectedLang : null ,
2019-11-13 11:28:01 +00:00
blockedHugveys : 0 ,
availableHugveys : 0 ,
2019-11-28 17:17:52 +00:00
starts _since _lang _change : 0 ,
2019-11-29 19:51:29 +00:00
future _language : 0 ,
2019-01-23 14:26:44 +00:00
} ,
methods : {
2019-01-24 13:27:04 +00:00
time _passed : function ( hugvey , property ) {
return moment ( Date ( hugvey [ property ] * 1000 ) ) . fromNow ( ) ;
2019-01-23 14:26:44 +00:00
} ,
2019-01-25 09:43:55 +00:00
timer : function ( hugvey , property ) {
return panopticon . stringToHHMMSS ( hugvey [ property ] ) ;
} ,
2019-04-27 13:33:51 +00:00
formatted : function ( time ) {
return moment ( time ) . utc ( ) . format ( "hh:mm:ss" ) ;
} ,
2019-01-24 13:27:04 +00:00
loadNarrative : function ( code , file ) {
2019-05-17 14:39:53 +00:00
panopticon . selectHugvey ( null ) ;
2019-05-13 12:12:54 +00:00
2019-04-27 10:13:34 +00:00
if ( panopticon . hasGraph ) {
return panopticon . loadNarrative ( code , file ) ;
}
2019-01-25 09:43:55 +00:00
} ,
2019-11-11 11:45:08 +00:00
changeLanguageForAvailable : function ( code ) {
let el = document . getElementById ( "change-lang-" + code ) ;
el . classList . add ( 'loading' ) ;
2019-11-13 22:43:02 +00:00
console . log ( el ) ;
panopticon . send ( { action : 'change_language_for_available' , lang _code : code } ) ;
// TODO: loop with a timer
let i = 0 ;
for ( let hv of panopticon . hugveys . hugveys ) {
setTimeout ( function ( ) {
console . log ( hv [ 'available' ] ) ;
if ( hv . hasOwnProperty ( 'available' ) && hv [ 'available' ] ) {
panopticon . change _language ( hv . id , code ) ;
}
} , i * 1000 ) ;
i ++ ;
}
setTimeout ( function ( ) { el . classList . remove ( 'loading' ) ; } , i * 1000 ) ;
2019-11-11 11:45:08 +00:00
} ,
2019-04-25 15:39:44 +00:00
block : function ( hv ) {
hv . status = "loading" ;
return panopticon . block ( hv . id ) ;
} ,
unblock : function ( hv ) {
hv . status = "loading" ;
return panopticon . unblock ( hv . id ) ;
} ,
2019-01-25 10:59:03 +00:00
pause : function ( hv ) {
hv . status = "loading" ;
return panopticon . pause ( hv . id ) ;
2019-01-25 09:43:55 +00:00
} ,
2019-01-25 10:59:03 +00:00
resume : function ( hv ) {
hv . status = "loading" ;
return panopticon . resume ( hv . id ) ;
2019-01-25 09:43:55 +00:00
} ,
2019-01-25 10:59:03 +00:00
restart : function ( hv ) {
hv . status = "loading" ;
return panopticon . restart ( hv . id ) ;
} ,
2019-04-16 10:38:12 +00:00
finish : function ( hv ) {
hv . status = "loading" ;
return panopticon . finish ( hv . id ) ;
} ,
2019-01-25 10:59:03 +00:00
change _lang : function ( hv , lang _code ) {
hv . status = "loading" ;
return panopticon . change _language ( hv . id , lang _code ) ;
2019-01-25 13:10:19 +00:00
} ,
2019-05-10 14:59:14 +00:00
change _light : function ( e ) {
let hv _id = parseInt ( e . target . dataset . hvid ) ;
let light _id = parseInt ( e . target . value ) ;
console . log ( hv _id , light _id , this ) ;
return panopticon . change _light _id ( hv _id , light _id ) ;
} ,
2019-06-08 15:20:47 +00:00
change _light _status : function ( e ) {
let hv _id = parseInt ( e . target . dataset . hvid ) ;
let checked = e . target . checked ;
panopticon . send ( { action : 'change_light_status' , hugvey : hv _id , light _status : checked } ) ;
} ,
2019-01-25 13:10:19 +00:00
showHugvey : function ( hv ) {
2019-05-17 14:39:53 +00:00
panopticon . selectHugvey ( hv . language ? hv . id : null ) ;
2019-04-27 13:33:51 +00:00
panopticon . hugveys . logbook = [ ] ;
panopticon . hugveys . logbookId = null ;
2019-01-25 13:10:19 +00:00
panopticon . updateSelectedHugvey ( ) ;
2019-11-13 11:20:32 +00:00
} ,
unblockAll : function ( ) {
2019-11-14 15:18:49 +00:00
let el = document . getElementById ( 'unblock-all' ) ;
el . classList . add ( 'loading' ) ;
2019-11-13 22:43:02 +00:00
let i = 0 ;
2019-11-13 11:20:32 +00:00
for ( let hv of panopticon . hugveys . hugveys ) {
2019-11-13 22:43:02 +00:00
setTimeout ( function ( ) {
if ( hv . status == 'blocked' ) {
hv . status = "loading" ;
panopticon . unblock ( hv . id )
}
} , i * 1000 ) ;
i ++ ;
2019-11-13 11:20:32 +00:00
}
2019-11-14 15:18:49 +00:00
setTimeout ( ( ) => el . classList . remove ( 'loading' ) , i * 1000 ) ;
2019-11-13 11:20:32 +00:00
} ,
startAll : function ( ) {
2019-11-14 15:18:49 +00:00
let el = document . getElementById ( 'start-all' ) ;
el . classList . add ( 'loading' ) ;
2019-11-13 22:43:02 +00:00
let i = 0 ;
2019-11-13 11:20:32 +00:00
for ( let hv of panopticon . hugveys . hugveys ) {
2019-11-13 22:43:02 +00:00
setTimeout ( function ( ) {
if ( hv . status == 'available' ) {
hv . status = "loading" ;
panopticon . restart ( hv . id )
}
} , i * 1000 ) ;
i ++ ;
2019-11-13 11:20:32 +00:00
}
2019-11-14 15:18:49 +00:00
setTimeout ( ( ) => el . classList . remove ( 'loading' ) , i * 1000 ) ;
2019-11-13 11:20:32 +00:00
} ,
2019-01-23 14:26:44 +00:00
}
} ) ;
2019-01-24 13:27:04 +00:00
2019-11-12 10:01:56 +00:00
// this.socket = new ReconnectingWebSocket( "ws://localhost:8888/ws", null, { debug: false, reconnectInterval: 3000 } );
this . socket = new ReconnectingWebSocket ( window . location . origin . replace ( 'http' , 'ws' ) + '/ws' , null , { debug : false , reconnectInterval : 3000 } ) ;
2019-05-13 12:12:54 +00:00
2019-04-27 10:13:34 +00:00
if ( this . hasGraph ) {
this . graph = new Graph ( ) ;
}
2019-05-13 12:12:54 +00:00
2019-01-23 14:26:44 +00:00
this . socket . addEventListener ( 'open' , ( e ) => {
this . send ( { action : 'init' } ) ;
} ) ;
2019-05-13 12:12:54 +00:00
2019-03-27 12:36:09 +00:00
// request close before unloading
window . addEventListener ( 'beforeunload' , function ( ) {
panopticon . socket . close ( ) ;
} ) ;
2019-01-23 14:26:44 +00:00
this . socket . addEventListener ( 'close' , function ( e ) {
console . log ( 'Closed connection' ) ;
} ) ;
this . socket . addEventListener ( 'message' , ( e ) => {
2019-04-27 10:13:34 +00:00
if ( e . data == 'hello!' ) {
console . log ( "Websocket connected" )
return ;
}
2019-05-13 12:12:54 +00:00
2019-01-23 14:26:44 +00:00
let msg = JSON . parse ( e . data ) ;
if ( typeof msg [ 'alert' ] !== 'undefined' ) {
2019-01-24 13:27:04 +00:00
alert ( msg [ 'alert' ] ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
2019-01-23 14:26:44 +00:00
if ( typeof msg [ 'action' ] === 'undefined' ) {
console . error ( "not a valid message: " + e . data ) ;
return ;
}
2019-05-13 12:12:54 +00:00
2019-03-27 12:36:09 +00:00
console . debug ( msg ) ;
2019-05-13 12:12:54 +00:00
2019-01-23 14:26:44 +00:00
switch ( msg [ 'action' ] ) {
2019-01-24 13:27:04 +00:00
2019-01-23 14:26:44 +00:00
case 'status' :
2019-01-24 13:27:04 +00:00
this . hugveys . uptime = this . stringToHHMMSS ( msg [ 'uptime' ] ) ;
2019-07-03 15:54:14 +00:00
this . hugveys . loop _timer = this . stringToHHMMSS ( msg [ 'loop_timer' ] ) ;
2019-01-23 14:26:44 +00:00
this . hugveys . languages = msg [ 'languages' ] ;
2019-01-25 09:43:55 +00:00
this . languages = msg [ 'languages' ] ;
2019-01-23 14:26:44 +00:00
this . hugveys . hugveys = msg [ 'hugveys' ] ;
2019-04-27 13:33:51 +00:00
this . hugveys . logbook = msg [ 'logbook' ] ;
this . hugveys . logbookId = msg [ 'logbookId' ] ;
2019-11-28 17:17:52 +00:00
this . hugveys . starts _since _lang _change = msg [ 'starts_since_lang_change' ] ;
2019-11-29 19:51:29 +00:00
this . hugveys . future _language = msg [ 'future_language' ] ;
2019-01-25 14:45:46 +00:00
if ( this . hugveys . selectedId ) {
2019-01-25 13:10:19 +00:00
this . updateSelectedHugvey ( ) ;
}
2020-01-13 07:23:30 +00:00
2019-11-13 11:28:01 +00:00
let avail = 0 ;
let blocked = 0 ;
for ( let hv of this . hugveys . hugveys ) {
if ( hv . status == 'available' ) avail ++ ;
if ( hv . status == 'blocked' ) blocked ++ ;
}
2020-01-13 07:23:30 +00:00
2019-11-13 11:28:01 +00:00
this . hugveys . blockedHugveys = blocked ;
this . hugveys . availableHugveys = avail ;
2020-01-13 07:23:30 +00:00
2019-01-23 14:26:44 +00:00
break ;
2019-03-27 12:36:09 +00:00
case 'log' :
break ;
2019-01-23 14:26:44 +00:00
}
} ) ;
}
2020-01-13 07:23:30 +00:00
2019-05-17 14:39:53 +00:00
selectHugvey ( hv _id ) {
this . hugveys . selectedId = hv _id ;
this . send ( { action : 'selection' , selected _id : hv _id } ) ;
}
2020-01-13 07:23:30 +00:00
2019-07-10 13:24:02 +00:00
change _loop _time ( newTime ) {
console . log ( 'update' , newTime ) ;
this . send ( { action : 'loop_time' , time : newTime } ) ;
}
2019-05-13 12:12:54 +00:00
2019-01-25 13:10:19 +00:00
updateSelectedHugvey ( ) {
2019-01-25 14:45:46 +00:00
let hv = null ;
2019-05-13 12:12:54 +00:00
2019-01-25 14:45:46 +00:00
if ( this . hugveys . selectedId ) {
hv = this . getHugvey ( this . hugveys . selectedId ) ;
2019-05-13 12:12:54 +00:00
2019-04-27 10:13:34 +00:00
if ( this . hasGraph ) {
if ( hv . language && this . graph . language _code != hv . language ) {
this . loadNarrative ( hv . language ) ;
}
2019-01-25 14:45:46 +00:00
}
2019-05-13 12:12:54 +00:00
2019-05-11 21:34:06 +00:00
// let varEl = document.getElementById("variables");
// varEl.innerHTML = "";
2019-01-25 13:10:19 +00:00
}
2019-05-13 12:12:54 +00:00
2019-04-27 10:13:34 +00:00
if ( this . hasGraph ) {
this . graph . updateHugveyStatus ( hv ) ;
}
2019-01-25 13:10:19 +00:00
}
2019-05-13 12:12:54 +00:00
2019-01-25 13:10:19 +00:00
getHugvey ( id ) {
for ( let hv of this . hugveys . hugveys ) {
if ( hv . id == id ) {
return hv ;
}
}
return null ;
}
2019-01-23 14:26:44 +00:00
send ( msg ) {
2019-01-24 13:27:04 +00:00
if ( this . socket . readyState == WebSocket . OPEN ) {
this . socket . send ( JSON . stringify ( msg ) ) ;
2019-01-23 14:26:44 +00:00
} else {
2019-01-24 13:27:04 +00:00
console . error ( "Socket not open: " , this . socket . readyState ) ;
2019-01-23 14:26:44 +00:00
}
}
2019-05-16 13:23:36 +00:00
// getStatus() {
// // console.log('get status', this, panopticon);
// panopticon.send( { action: 'get_status', selected_id: panopticon.hugveys.selectedId } );
// }
2019-01-23 14:26:44 +00:00
init ( ) {
2019-05-16 13:23:36 +00:00
// setInterval( this.getStatus, 3000 );
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
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 ) {
2019-01-25 09:43:55 +00:00
if ( typeof file == 'undefined' ) {
for ( let lang of this . languages ) {
if ( lang [ 'code' ] == code ) {
file = lang [ 'file' ] ;
}
}
}
2019-07-03 16:33:31 +00:00
this . hugveys . selectedLang = code ;
2020-01-13 07:23:30 +00:00
2019-01-23 14:26:44 +00:00
let req = new XMLHttpRequest ( ) ;
let graph = this . graph ;
2019-01-24 13:27:04 +00:00
req . addEventListener ( "load" , function ( e ) {
graph . loadData ( JSON . parse ( this . response ) , code ) ;
2019-05-13 12:12:54 +00:00
// console.log(, e);
2019-01-24 13:27:04 +00:00
} ) ;
req . open ( "GET" , "/local/" + file ) ;
2019-01-23 14:26:44 +00:00
req . send ( ) ;
}
2019-04-25 15:39:44 +00:00
block ( hv _id ) {
this . send ( { action : 'block' , hugvey : hv _id } )
}
unblock ( hv _id ) {
this . send ( { action : 'unblock' , hugvey : hv _id } )
}
2019-01-24 13:27:04 +00:00
resume ( hv _id ) {
this . send ( { action : 'resume' , hugvey : hv _id } )
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
pause ( hv _id ) {
2019-01-25 13:10:19 +00:00
this . send ( { action : 'pause' , hugvey : hv _id } )
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
restart ( hv _id ) {
2019-01-25 13:10:19 +00:00
this . send ( { action : 'restart' , hugvey : hv _id } ) ;
2019-01-23 14:26:44 +00:00
}
2019-04-16 10:38:12 +00:00
finish ( hv _id ) {
this . send ( { action : 'finish' , hugvey : hv _id } ) ;
}
2019-01-25 10:59:03 +00:00
change _language ( hv _id , lang _code ) {
this . send ( { action : 'change_language' , hugvey : hv _id , lang _code : lang _code } ) ;
}
2019-05-10 14:59:14 +00:00
change _light _id ( hv _id , light _id ) {
console . log ( "Light" , hv _id , light _id ) ;
this . send ( { action : 'change_light' , hugvey : hv _id , light _id : light _id } ) ;
}
2019-05-13 12:12:54 +00:00
2019-05-11 21:40:52 +00:00
playFromSelected ( msg _id , reloadStory ) {
2019-01-25 14:45:46 +00:00
if ( ! this . hugveys . selectedId ) {
alert ( 'No hugvey selected' ) ;
} else {
2019-05-11 21:40:52 +00:00
this . send ( { action : 'play_msg' , hugvey : this . hugveys . selectedId , msg _id : msg _id , reloadStory : reloadStory } )
2019-01-25 14:45:46 +00:00
}
}
2019-01-23 14:26:44 +00:00
}
window . addEventListener ( 'load' , function ( ) {
panopticon = new Panopticon ( ) ;
panopticon . init ( ) ;
2019-01-24 13:27:04 +00:00
} ) ;
2019-01-23 14:26:44 +00:00
2019-01-24 13:27:04 +00:00
class Graph {
2019-01-23 14:26:44 +00:00
constructor ( ) {
this . width = 1280 ;
this . height = 1024 ;
this . nodeSize = 80 ;
this . maxChars = 16 ;
2019-01-24 13:27:04 +00:00
this . svg = d3 . select ( '#graph' ) ;
this . container = d3 . select ( '#container' ) ;
2019-01-23 14:26:44 +00:00
this . selectedMsg = null ;
2019-01-23 21:38:27 +00:00
this . language _code = null ;
2019-01-23 14:26:44 +00:00
this . messages = [ ] ; // initialise empty array. For the simulation, make sure we keep the same array object
this . directions = [ ] ; // initialise empty array. For the simulation, make sure we keep the same array object
this . conditions = [ ] ; // initialise empty array. For the simulation, make sure we keep the same array object
2019-01-25 09:43:55 +00:00
this . diversions = [ ] ; // initialise empty array. For the simulation, make sure we keep the same array object
2019-01-23 14:26:44 +00:00
let graph = this ;
this . controlDown = false ;
2019-01-24 13:27:04 +00:00
document . addEventListener ( 'keydown' , function ( e ) {
2019-02-02 17:20:55 +00:00
if ( e . which == "16" ) { // shift
2019-01-23 14:26:44 +00:00
graph . controlDown = true ;
2019-01-24 13:27:04 +00:00
document . body . classList . add ( 'controlDown' ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
} ) ;
document . addEventListener ( 'keyup' , function ( e ) {
2019-02-02 17:20:55 +00:00
if ( e . which == "16" ) { // shift
2019-01-23 14:26:44 +00:00
graph . controlDown = false ;
2019-01-24 13:27:04 +00:00
document . body . classList . remove ( 'controlDown' ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
} ) ;
2019-01-23 14:26:44 +00:00
let c = this . container ;
2019-01-24 13:27:04 +00:00
let zoomed = function ( ) {
c . attr ( "transform" , d3 . event . transform ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
this . svg . call ( d3 . zoom ( )
2019-02-25 16:05:14 +00:00
. scaleExtent ( [ 1 / 16 , 8 ] )
2019-01-24 13:27:04 +00:00
. on ( "zoom" , zoomed ) ) ;
2019-01-23 14:26:44 +00:00
2019-01-24 13:27:04 +00:00
this . nodesG = this . container . append ( "g" )
. attr ( "id" , "nodes" )
2019-01-23 14:26:44 +00:00
2019-01-24 13:27:04 +00:00
this . linkG = this . container . append ( "g" )
. attr ( "id" , "links" ) ;
2019-01-23 14:26:44 +00:00
2019-11-11 17:25:04 +00:00
document . getElementById ( 'btn-save' ) . addEventListener ( 'click' , function ( e ) {
let el = e . target ;
el . classList . add ( 'loading' ) ;
// give the ui a fraction to actually apply the 'loading' class
setTimeout ( function ( ) {
graph . saveJson ( ) ;
el . classList . remove ( 'loading' ) ;
} , 100 ) ;
2020-01-13 07:23:30 +00:00
2019-11-11 17:25:04 +00:00
} ) ;
2019-01-24 13:27:04 +00:00
document . getElementById ( 'btn-addMsg' ) . addEventListener ( 'click' , function ( e ) { graph . createMsg ( ) ; } ) ;
2019-03-07 19:19:43 +00:00
document . getElementById ( 'btn-diversions' ) . addEventListener ( 'click' , function ( e ) { graph . showDiversions ( ) ; } ) ;
2019-04-10 16:46:32 +00:00
document . getElementById ( 'btn-audio' ) . addEventListener ( 'click' , function ( e ) { graph . showAudioFiles ( ) ; } ) ;
2019-05-12 12:54:37 +00:00
document . getElementById ( 'btn-config' ) . addEventListener ( 'click' , function ( e ) { graph . showConfiguration ( ) ; } ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
clickMsg ( msg ) {
2019-01-23 14:26:44 +00:00
// event when a message is clicked.
2019-01-24 13:27:04 +00:00
if ( this . controlDown ) {
this . secondarySelectMsg ( msg ) ;
2019-01-23 14:26:44 +00:00
} else {
2019-01-24 13:27:04 +00:00
this . selectMsg ( msg ) ;
2019-01-23 14:26:44 +00:00
}
}
2019-01-24 13:27:04 +00:00
secondarySelectMsg ( msg ) {
if ( this . selectedMsg !== null ) {
this . addDirection ( this . selectedMsg , msg ) ;
2019-01-23 14:26:44 +00:00
} else {
2019-01-24 13:27:04 +00:00
console . error ( 'No message selected as Source' ) ;
2019-01-23 14:26:44 +00:00
}
}
2019-01-24 13:27:04 +00:00
selectMsg ( msg ) {
let selectedEls = document . getElementsByClassName ( 'selectedMsg' ) ;
while ( selectedEls . length > 0 ) {
selectedEls [ 0 ] . classList . remove ( 'selectedMsg' ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
document . getElementById ( msg [ '@id' ] ) . classList . add ( 'selectedMsg' ) ;
2019-01-23 14:26:44 +00:00
this . selectedMsg = msg ;
2019-01-24 13:27:04 +00:00
this . showMsg ( msg ) ;
2019-01-23 14:26:44 +00:00
}
updateMsg ( ) {
// used eg. after a condition creation.
2019-01-24 13:27:04 +00:00
this . showMsg ( this . selectedMsg ) ;
2019-01-23 14:26:44 +00:00
}
2019-05-13 12:12:54 +00:00
2019-02-18 19:38:54 +00:00
getAudioUrlForMsg ( msg ) {
let isVariable = msg [ 'text' ] . includes ( '$' ) ? '1' : '0' ;
2019-04-09 07:40:50 +00:00
let lang = panopticon . graph . language _code ;
2019-11-12 10:01:56 +00:00
return ` ${ window . location . origin } /voice?text= ${ encodeURIComponent ( msg [ 'text' ] ) } &variable= ${ isVariable } &lang= ${ lang } &filename=0 ` ;
2019-02-18 19:38:54 +00:00
}
2019-05-13 12:12:54 +00:00
2019-05-12 12:54:37 +00:00
getConfig ( ) {
2019-05-13 12:12:54 +00:00
2019-05-12 12:54:37 +00:00
}
2019-05-13 12:12:54 +00:00
2019-03-07 19:19:43 +00:00
getNumericId ( prefix ) {
let id , i = 0 ;
let hasId = function ( a , id ) {
for ( let i of a ) {
if ( i [ '@id' ] == id ) {
return true ;
}
}
return false ;
}
do {
id = prefix + i ;
i ++ ;
} while ( hasId ( this . data , id ) )
2019-05-13 12:12:54 +00:00
2019-03-07 19:19:43 +00:00
return id ;
}
2019-05-13 12:12:54 +00:00
2019-03-07 19:19:43 +00:00
createDiversion ( type ) {
let div = {
"@id" : this . getNumericId ( this . language _code . substring ( 0 , 2 ) + ` -div- ${ type } # ` ) ,
'@type' : 'Diversion' ,
'type' : type ,
'params' : { }
}
2019-05-13 12:12:54 +00:00
2019-03-07 19:19:43 +00:00
if ( type == 'no_response' ) {
div [ 'params' ] [ 'consecutiveSilences' ] = 3 ;
div [ 'params' ] [ 'timesOccured' ] = 0 ;
div [ 'params' ] [ 'returnAfterStrand' ] = true ;
div [ 'params' ] [ 'msgId' ] = "" ;
}
2019-04-24 11:38:41 +00:00
else if ( type == 'reply_contains' ) {
div [ 'params' ] [ 'regex' ] = "" ;
div [ 'params' ] [ 'returnAfterStrand' ] = true ;
2019-06-09 09:34:41 +00:00
div [ 'params' ] [ 'nextChapterOnReturn' ] = false ;
2019-04-24 11:38:41 +00:00
div [ 'params' ] [ 'msgId' ] = "" ;
2019-04-25 09:12:27 +00:00
div [ 'params' ] [ 'notForColor' ] = "" ;
2019-05-10 13:14:13 +00:00
div [ 'params' ] [ 'waitTime' ] = 1.8 ;
2019-04-24 11:38:41 +00:00
}
2019-05-01 16:27:10 +00:00
else if ( type == 'interrupt' ) {
div [ 'params' ] [ 'msgId' ] = "" ;
}
2019-04-24 14:09:41 +00:00
else if ( type == 'timeout' ) {
div [ 'params' ] [ 'interval' ] = 20 ;
div [ 'params' ] [ 'timesOccured' ] = 0 ;
2019-04-24 14:50:34 +00:00
div [ 'params' ] [ 'minTimeAfterMessage' ] = 2. ;
2019-04-24 14:09:41 +00:00
div [ 'params' ] [ 'fromLastMessage' ] = false ;
div [ 'params' ] [ 'returnAfterStrand' ] = true ;
div [ 'params' ] [ 'msgId' ] = "" ;
}
2019-03-07 19:19:43 +00:00
else if ( type == 'repeat' ) {
2019-05-13 12:12:54 +00:00
div [ 'params' ] [ 'regex' ] = "can you repeat that\\?" ;
2020-01-13 07:23:30 +00:00
}
2019-07-03 15:54:14 +00:00
else if ( type == 'collective_moment' ) {
2019-07-10 12:11:57 +00:00
div [ 'params' ] [ 'start_second' ] = 20 * 60 ; // second to start
2019-07-03 15:54:14 +00:00
div [ 'params' ] [ 'window' ] = 60 ; // how long to wait, in seconds
}
else {
2019-03-07 19:19:43 +00:00
console . log ( "invalid type" , type ) ;
alert ( 'invalid type for diversion' ) ;
}
2019-05-13 12:12:54 +00:00
2019-05-01 16:27:10 +00:00
if ( type != 'repeat' && type != 'interrupt' ) {
2019-04-28 09:34:30 +00:00
div [ 'params' ] [ 'notAfterMsgId' ] = "" ;
}
2019-05-13 12:12:54 +00:00
2019-03-07 19:19:43 +00:00
this . data . push ( div ) ;
this . updateFromData ( ) ;
this . build ( ) ;
2019-05-13 12:12:54 +00:00
2019-03-07 19:19:43 +00:00
this . showDiversions ( ) ;
return msg ;
}
2019-05-13 12:12:54 +00:00
2019-03-07 19:19:43 +00:00
deleteDiversion ( div ) {
this . _rmNode ( div ) ;
this . showDiversions ( ) ;
}
2019-05-13 12:12:54 +00:00
2019-03-07 19:19:43 +00:00
showDiversions ( ) {
let msgEl = document . getElementById ( 'msg' ) ;
msgEl . innerHTML = "" ;
2019-05-13 12:12:54 +00:00
2019-07-03 15:54:14 +00:00
let divsNoResponse = [ ] , divsRepeat = [ ] , divsReplyContains = [ ] , divsTimeouts = [ ] , divsInterrupts = [ ] , divsCollectiveMoment = [ ] ;
2019-03-07 19:19:43 +00:00
for ( let div of this . diversions ) {
2019-04-28 09:34:30 +00:00
let notAfterMsgIdEl = "" ;
if ( div [ 'type' ] != 'repeat' ) {
let notMsgOptions = [ crel ( 'option' , "" ) ] ;
let chapterMsgs = this . messages . filter ( m => m . hasOwnProperty ( 'chapterStart' ) && m [ 'chapterStart' ] == true ) ;
for ( let startMsg of chapterMsgs ) {
let optionParams = {
'value' : startMsg [ '@id' ]
} ;
if ( div [ 'params' ] [ 'notAfterMsgId' ] == startMsg [ '@id' ] ) {
optionParams [ 'selected' ] = 'selected' ;
}
2019-11-30 17:04:12 +00:00
notMsgOptions . push ( crel ( 'option' , optionParams , ` ${ this . getLabel ( startMsg ) } ( ${ startMsg [ '@id' ] } ) ` ) ) ;
2019-04-28 09:34:30 +00:00
}
notAfterMsgIdEl = crel ( 'label' , 'Not when chapter has hit:' ,
crel ( 'select' , { 'on' : {
'change' : ( e ) => div [ 'params' ] [ 'notAfterMsgId' ] = e . target . value
} } , ... notMsgOptions )
) ;
}
2019-05-13 12:12:54 +00:00
2019-03-07 19:19:43 +00:00
if ( div [ 'type' ] == 'no_response' ) {
let returnAttrs = {
'type' : 'checkbox' ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'returnAfterStrand' ] = e . target . checked
}
}
if ( div [ 'params' ] [ 'returnAfterStrand' ] ) {
returnAttrs [ 'checked' ] = 'checked' ;
}
let msgOptions = [ crel ( 'option' , "" ) ] ;
let starts = this . messages . filter ( m => m . hasOwnProperty ( 'start' ) && m [ 'start' ] == true ) ;
for ( let startMsg of starts ) {
let optionParams = { } ;
if ( div [ 'params' ] [ 'msgId' ] == startMsg [ '@id' ] ) {
optionParams [ 'selected' ] = 'selected' ;
}
msgOptions . push ( crel ( 'option' , optionParams , startMsg [ '@id' ] ) ) ;
}
2019-05-13 12:12:54 +00:00
2019-03-07 19:19:43 +00:00
divsNoResponse . push ( crel (
'div' , {
'class' : 'diversion' ,
'on' : {
'mouseover' : function ( e ) {
if ( div [ 'params' ] [ 'msgId' ] )
document . getElementById ( div [ 'params' ] [ 'msgId' ] ) . classList . add ( 'selectedMsg' ) ;
} ,
'mouseout' : function ( e ) {
if ( div [ 'params' ] [ 'msgId' ] )
document . getElementById ( div [ 'params' ] [ 'msgId' ] ) . classList . remove ( 'selectedMsg' ) ;
}
}
} ,
crel ( 'h3' , div [ '@id' ] ) ,
crel (
'div' , {
'class' : 'btn btn--delete' ,
'on' : {
'click' : ( e ) => this . deleteDiversion ( div )
}
} , 'Delete diversion' ) ,
crel ( 'label' , 'Consecutive Silences' ,
crel ( 'input' , {
'type' : 'number' ,
'value' : div [ 'params' ] [ 'consecutiveSilences' ] ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'consecutiveSilences' ] = parseInt ( e . target . value )
}
} )
) ,
crel ( 'label' , 'On n-th instance' ,
crel ( 'input' , {
'type' : 'number' ,
'value' : div [ 'params' ] [ 'timesOccured' ] ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'timesOccured' ] = parseInt ( e . target . value )
}
} )
) ,
crel ( 'label' , 'Return to point of departure afterwards' ,
crel ( 'input' , returnAttrs )
) ,
crel ( 'label' , 'Go to (start message)' ,
crel ( 'select' , { 'on' : {
'change' : ( e ) => div [ 'params' ] [ 'msgId' ] = e . target . value
} } , ... msgOptions )
2019-04-28 09:34:30 +00:00
) ,
notAfterMsgIdEl
2019-03-07 19:19:43 +00:00
) ) ;
2019-04-24 11:38:41 +00:00
}
if ( div [ 'type' ] == 'reply_contains' ) {
let returnAttrs = {
'type' : 'checkbox' ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'returnAfterStrand' ] = e . target . checked
}
}
if ( div [ 'params' ] [ 'returnAfterStrand' ] ) {
returnAttrs [ 'checked' ] = 'checked' ;
}
2019-06-09 09:34:41 +00:00
let returnChapterAttrs = {
'type' : 'checkbox' ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'nextChapterOnReturn' ] = e . target . checked
}
}
if ( div [ 'params' ] [ 'nextChapterOnReturn' ] ) {
returnChapterAttrs [ 'checked' ] = 'checked' ;
}
2019-04-24 11:38:41 +00:00
let msgOptions = [ crel ( 'option' , "" ) ] ;
let starts = this . messages . filter ( m => m . hasOwnProperty ( 'start' ) && m [ 'start' ] == true ) ;
for ( let startMsg of starts ) {
let optionParams = { } ;
if ( div [ 'params' ] [ 'msgId' ] == startMsg [ '@id' ] ) {
optionParams [ 'selected' ] = 'selected' ;
}
msgOptions . push ( crel ( 'option' , optionParams , startMsg [ '@id' ] ) ) ;
}
2019-05-13 12:12:54 +00:00
2019-04-24 11:38:41 +00:00
divsReplyContains . push ( crel (
'div' , {
'class' : 'diversion' ,
'on' : {
'mouseover' : function ( e ) {
if ( div [ 'params' ] [ 'msgId' ] )
document . getElementById ( div [ 'params' ] [ 'msgId' ] ) . classList . add ( 'selectedMsg' ) ;
} ,
'mouseout' : function ( e ) {
if ( div [ 'params' ] [ 'msgId' ] )
document . getElementById ( div [ 'params' ] [ 'msgId' ] ) . classList . remove ( 'selectedMsg' ) ;
}
}
} ,
crel ( 'h3' , div [ '@id' ] ) ,
crel (
'div' , {
'class' : 'btn btn--delete' ,
'on' : {
'click' : ( e ) => this . deleteDiversion ( div )
}
} , 'Delete diversion' ) ,
crel ( 'label' , 'Regex' ,
2019-04-25 09:12:27 +00:00
crel ( 'input' , {
'type' : 'text' ,
'value' : div [ 'params' ] [ 'regex' ] ,
'placeholder' : 'regex' ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'regex' ] = e . target . value
}
} )
) ,
crel ( 'label' , 'Ignore for color' ,
crel ( 'input' , {
'type' : 'text' , // use text instead of color, as color doesn't allow for empty values
'value' : typeof div [ 'params' ] [ 'notForColor' ] !== 'undefined' ? div [ 'params' ] [ 'notForColor' ] : "" ,
'on' : {
'change' : function ( e ) {
if ( e . target . value . length > 0 && e . target . value . substr ( 0 , 1 ) !== '#' ) {
alert ( "Don't forget to have a valid hex including the #-character, eg: #00ff00" ) ;
}
div [ 'params' ] [ 'notForColor' ] = e . target . value ;
}
}
} )
) ,
2019-04-24 11:38:41 +00:00
crel ( 'label' , 'Return to point of departure afterwards' ,
crel ( 'input' , returnAttrs )
) ,
2019-06-09 09:34:41 +00:00
crel ( 'label' , 'On return, skip to next chapter' ,
crel ( 'input' , returnChapterAttrs )
) ,
2019-04-24 11:38:41 +00:00
crel ( 'label' , 'Go to (start message)' ,
crel ( 'select' , { 'on' : {
'change' : ( e ) => div [ 'params' ] [ 'msgId' ] = e . target . value
} } , ... msgOptions )
2019-04-28 09:34:30 +00:00
) ,
2019-05-10 13:14:13 +00:00
crel ( 'label' , 'Wait time' ,
crel ( 'input' , {
'type' : 'number' ,
'step' : 0.1 ,
'value' : div [ 'params' ] [ 'waitTime' ] ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'waitTime' ] = parseFloat ( e . target . value )
}
} )
) ,
2019-04-28 09:34:30 +00:00
notAfterMsgIdEl
2019-04-24 11:38:41 +00:00
) ) ;
2019-05-13 12:12:54 +00:00
}
2019-07-03 15:54:14 +00:00
if ( div [ 'type' ] == 'collective_moment' ) {
let msgOptions = [ crel ( 'option' , "" ) ] ;
let starts = this . messages . filter ( m => m . hasOwnProperty ( 'start' ) && m [ 'start' ] == true ) ;
for ( let startMsg of starts ) {
let optionParams = { } ;
if ( div [ 'params' ] [ 'msgId' ] == startMsg [ '@id' ] ) {
optionParams [ 'selected' ] = 'selected' ;
}
msgOptions . push ( crel ( 'option' , optionParams , startMsg [ '@id' ] ) ) ;
}
divsCollectiveMoment . push ( crel (
'div' , {
'class' : 'diversion' ,
'on' : {
'mouseover' : function ( e ) {
if ( div [ 'params' ] [ 'msgId' ] )
document . getElementById ( div [ 'params' ] [ 'msgId' ] ) . classList . add ( 'selectedMsg' ) ;
} ,
'mouseout' : function ( e ) {
if ( div [ 'params' ] [ 'msgId' ] )
document . getElementById ( div [ 'params' ] [ 'msgId' ] ) . classList . remove ( 'selectedMsg' ) ;
}
}
} ,
crel ( 'h3' , div [ '@id' ] ) ,
crel (
'div' , {
'class' : 'btn btn--delete' ,
'on' : {
'click' : ( e ) => this . deleteDiversion ( div )
}
} , 'Delete diversion' ) ,
2019-07-10 12:11:57 +00:00
crel ( 'label' , 'Start time (seconds)' ,
2019-07-03 15:54:14 +00:00
crel ( 'input' , {
'type' : 'number' ,
2019-07-10 12:11:57 +00:00
// 'max': 59,
2019-07-03 15:54:14 +00:00
'min' : 0 ,
2019-07-10 12:11:57 +00:00
'value' : div [ 'params' ] [ 'start_second' ] ,
'placeholder' : 'time in loop (seconds)' ,
2019-07-03 15:54:14 +00:00
'on' : {
2019-07-10 12:11:57 +00:00
'change' : ( e ) => div [ 'params' ] [ 'start_second' ] = e . target . value
2019-07-03 15:54:14 +00:00
}
} )
) ,
crel ( 'label' , 'Duration (seconds)' ,
crel ( 'input' , {
'type' : 'number' ,
'max' : 60 * 15 ,
'min' : 0 ,
'value' : div [ 'params' ] [ 'window' ] ,
'placeholder' : 'seconds' ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'window' ] = e . target . value
}
} )
) ,
crel ( 'label' , 'Go to (start message)' ,
crel ( 'select' , { 'on' : {
'change' : ( e ) => div [ 'params' ] [ 'msgId' ] = e . target . value
} } , ... msgOptions )
)
) ) ;
}
2019-04-24 14:09:41 +00:00
if ( div [ 'type' ] == 'timeout' ) {
let returnAttrs = {
'type' : 'checkbox' ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'returnAfterStrand' ] = e . target . checked
}
}
if ( div [ 'params' ] [ 'returnAfterStrand' ] ) {
returnAttrs [ 'checked' ] = 'checked' ;
}
2019-05-13 12:12:54 +00:00
2019-04-24 14:09:41 +00:00
let totalOrLocalAttrs = {
'type' : 'checkbox' ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'fromLastMessage' ] = e . target . checked
}
}
if ( div [ 'params' ] [ 'fromLastMessage' ] ) {
totalOrLocalAttrs [ 'checked' ] = 'checked' ;
}
2019-05-13 12:12:54 +00:00
2019-04-24 14:09:41 +00:00
let msgOptions = [ crel ( 'option' , "" ) ] ;
let starts = this . messages . filter ( m => m . hasOwnProperty ( 'start' ) && m [ 'start' ] == true ) ;
for ( let startMsg of starts ) {
let optionParams = { } ;
if ( div [ 'params' ] [ 'msgId' ] == startMsg [ '@id' ] ) {
optionParams [ 'selected' ] = 'selected' ;
}
msgOptions . push ( crel ( 'option' , optionParams , startMsg [ '@id' ] ) ) ;
}
2019-05-13 12:12:54 +00:00
2019-04-24 14:09:41 +00:00
divsTimeouts . push ( crel (
'div' , {
'class' : 'diversion' ,
'on' : {
'mouseover' : function ( e ) {
if ( div [ 'params' ] [ 'msgId' ] )
document . getElementById ( div [ 'params' ] [ 'msgId' ] ) . classList . add ( 'selectedMsg' ) ;
} ,
'mouseout' : function ( e ) {
if ( div [ 'params' ] [ 'msgId' ] )
document . getElementById ( div [ 'params' ] [ 'msgId' ] ) . classList . remove ( 'selectedMsg' ) ;
}
}
} ,
crel ( 'h3' , div [ '@id' ] ) ,
crel (
'div' , {
'class' : 'btn btn--delete' ,
'on' : {
'click' : ( e ) => this . deleteDiversion ( div )
}
} , 'Delete diversion' ) ,
crel ( 'label' , 'For last message only' ,
crel ( 'input' , totalOrLocalAttrs )
) ,
crel ( 'label' , 'Seconds of silence' ,
crel ( 'input' , {
'type' : 'number' ,
'value' : div [ 'params' ] [ 'interval' ] ,
'precision' : . 1 ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'interval' ] = parseFloat ( e . target . value )
}
} )
) ,
crel ( 'label' , 'On n-th instance' ,
crel ( 'input' , {
'type' : 'number' ,
'value' : div [ 'params' ] [ 'timesOccured' ] ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'timesOccured' ] = parseInt ( e . target . value )
}
} )
2019-04-24 14:50:34 +00:00
) ,
crel ( 'label' , 'Minimum time after message' ,
crel ( 'input' , {
'type' : 'number' ,
'value' : div [ 'params' ] [ 'minTimeAfterMessage' ] ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'minTimeAfterMessage' ] = parseFloat ( e . target . value )
}
} )
2019-04-24 14:09:41 +00:00
) ,
crel ( 'label' , 'Return to point of departure afterwards' ,
crel ( 'input' , returnAttrs )
) ,
crel ( 'label' , 'Go to (start message)' ,
crel ( 'select' , { 'on' : {
'change' : ( e ) => div [ 'params' ] [ 'msgId' ] = e . target . value
} } , ... msgOptions )
2019-04-28 09:34:30 +00:00
) ,
notAfterMsgIdEl
2019-04-24 14:09:41 +00:00
) ) ;
}
2019-03-07 19:19:43 +00:00
if ( div [ 'type' ] == 'repeat' ) {
divsRepeat . push ( crel (
'div' , { 'class' : 'diversion' } ,
crel ( 'h3' , div [ '@id' ] ) ,
crel (
'div' , {
'class' : 'btn btn--delete' ,
'on' : {
'click' : ( e ) => this . deleteDiversion ( div )
}
} , 'Delete diversion' ) ,
crel ( 'label' , 'Regex' ,
crel ( 'input' , {
'type' : 'text' ,
'value' : div [ 'params' ] [ 'regex' ] ,
'on' : {
'change' : ( e ) => div [ 'params' ] [ 'regex' ] = e . target . value
}
} )
)
) ) ;
}
2019-05-01 16:27:10 +00:00
if ( div [ 'type' ] == 'interrupt' ) {
let msgOptions = [ crel ( 'option' , "" ) ] ;
let starts = this . messages . filter ( m => m . hasOwnProperty ( 'start' ) && m [ 'start' ] == true ) ;
for ( let startMsg of starts ) {
let optionParams = { } ;
if ( div [ 'params' ] [ 'msgId' ] == startMsg [ '@id' ] ) {
optionParams [ 'selected' ] = 'selected' ;
}
msgOptions . push ( crel ( 'option' , optionParams , startMsg [ '@id' ] ) ) ;
}
2019-05-13 12:12:54 +00:00
2019-05-01 16:27:10 +00:00
divsInterrupts . push ( crel (
'div' , { 'class' : 'diversion' } ,
crel ( 'h3' , div [ '@id' ] ) ,
crel (
'div' , {
'class' : 'btn btn--delete' ,
'on' : {
'click' : ( e ) => this . deleteDiversion ( div )
}
} , 'Delete diversion' ) ,
crel ( 'label' , 'Go to (start message)' ,
crel ( 'select' , { 'on' : {
'change' : ( e ) => div [ 'params' ] [ 'msgId' ] = e . target . value
} } , ... msgOptions )
)
) ) ;
}
2019-03-07 19:19:43 +00:00
}
2019-05-13 12:12:54 +00:00
2019-07-03 15:54:14 +00:00
console . log ( divsReplyContains , divsNoResponse , divsRepeat , divsTimeouts , divsInterrupts , divsCollectiveMoment ) ;
2019-05-13 12:12:54 +00:00
2019-03-07 19:19:43 +00:00
let divEl = crel (
'div' ,
{
'id' : 'diversions'
} ,
crel ( 'h1' , 'Configure Diversions' ) ,
crel ( 'div' ,
crel ( 'h2' , 'In case of No Response' ) ,
... divsNoResponse ,
crel ( 'div' ,
{
'class' : 'btn' ,
'on' : {
'click' : ( e ) => this . createDiversion ( 'no_response' )
}
} ,
'New case for no_response'
)
) ,
2019-04-24 11:38:41 +00:00
crel ( 'div' ,
crel ( 'h2' , 'Reply Contains' ) ,
... divsReplyContains ,
crel ( 'div' ,
{
'class' : 'btn' ,
'on' : {
'click' : ( e ) => this . createDiversion ( 'reply_contains' )
}
} ,
'New case for reply contains'
)
) ,
2019-03-07 19:19:43 +00:00
crel ( 'div' ,
crel ( 'h2' , 'Request repeat' ) ,
... divsRepeat ,
crel ( 'div' ,
{
'class' : 'btn' ,
'on' : {
'click' : ( e ) => this . createDiversion ( 'repeat' )
}
2019-05-13 12:12:54 +00:00
} ,
2019-03-07 19:19:43 +00:00
'New case for repeat'
)
2019-04-24 14:09:41 +00:00
) ,
crel ( 'div' ,
crel ( 'h2' , 'Timeouts' ) ,
... divsTimeouts ,
crel ( 'div' ,
{
'class' : 'btn' ,
'on' : {
'click' : ( e ) => this . createDiversion ( 'timeout' )
}
2019-05-13 12:12:54 +00:00
} ,
2019-04-24 14:09:41 +00:00
'New case for timeout'
)
2019-07-03 15:54:14 +00:00
) ,
crel ( 'div' ,
crel ( 'h2' , 'Collective moments' ) ,
... divsCollectiveMoment ,
crel ( 'div' ,
{
'class' : 'btn' ,
'on' : {
'click' : ( e ) => this . createDiversion ( 'collective_moment' )
}
} ,
'New collective moment'
)
2019-03-07 19:19:43 +00:00
)
2019-05-07 12:02:33 +00:00
// ,
// crel('div',
// crel('h2', 'Interruptions (random pick)'),
// ...divsInterrupts,
// crel('div',
// {
// 'class': 'btn',
// 'on': {
// 'click': (e) => this.createDiversion('interrupt')
// }
2019-05-13 12:12:54 +00:00
// },
2019-05-07 12:02:33 +00:00
// 'New case for Interrupt'
// )
// )
2019-03-07 19:19:43 +00:00
) ;
2019-05-13 12:12:54 +00:00
2019-03-07 19:19:43 +00:00
msgEl . appendChild ( divEl ) ;
}
2019-01-23 14:26:44 +00:00
2019-04-10 16:46:32 +00:00
showAudioFiles ( ) {
let audioFilesEl = crel ( 'div' , {
'id' : 'audioFiles'
} ,
crel (
'div' ,
{
'class' : 'btn btn-close' ,
'on' : {
'click' : function ( ) {
audioFilesEl . parentNode . removeChild ( audioFilesEl )
}
}
} ,
'close'
) ) ;
for ( let msg of panopticon . graph . messages ) {
audioFilesEl . appendChild ( crel (
'audio' ,
{ 'controls' : 'controls' } ,
crel ( 'source' , { 'src' : msg [ 'audio' ] ? msg [ 'audio' ] [ 'file' ] : panopticon . graph . getAudioUrlForMsg ( msg ) } )
) )
}
document . getElementById ( "interface" ) . appendChild ( audioFilesEl ) ;
}
2019-05-12 12:54:37 +00:00
showConfiguration ( ) {
let configEl = crel ( 'div' , {
'id' : 'configuration'
} ,
crel (
'div' ,
{
'class' : 'btn btn-close' ,
'on' : {
'click' : function ( ) {
configEl . parentNode . removeChild ( configEl )
}
}
} ,
'close'
) ,
crel ( 'h2' , ` Language based settings for ${ this . language _code } ` ) ,
crel (
'label' ,
'volume' ,
crel ( 'input' , {
'type' : 'number' ,
'step' : 0.05 ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'volume' ] = parseFloat ( e . target . value )
}
} ,
'value' : this . configuration . hasOwnProperty ( 'volume' ) ? this . configuration . volume : 1
} )
2020-02-15 13:43:49 +00:00
) , crel ( 'br' ) ,
2019-05-13 12:45:52 +00:00
crel (
'label' ,
"Text replacement when no variable is set: " ,
crel ( 'input' , {
'type' : 'text' ,
'on' : {
'change' : function ( e ) {
2019-05-13 12:52:54 +00:00
panopticon . graph . configuration [ 'nothing_text' ] = e . target . value
2019-05-13 12:45:52 +00:00
}
} ,
'value' : this . configuration . hasOwnProperty ( 'nothing_text' ) ? this . configuration . nothing _text : "nothing"
} )
2020-02-15 13:43:49 +00:00
) , crel ( 'br' ) ,
2019-11-11 17:25:04 +00:00
crel (
'label' ,
2019-12-20 11:12:11 +00:00
"Condition timing factor: (< 1 is faster, >1 is slower)" ,
2019-11-11 17:25:04 +00:00
crel ( 'input' , {
'type' : 'number' ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'time_factor' ] = parseFloat ( e . target . value )
}
} ,
'value' : this . configuration . hasOwnProperty ( 'time_factor' ) ? this . configuration . time _factor : 1 ,
'step' : 0.01
} )
2020-02-15 13:43:49 +00:00
) , crel ( 'br' ) ,
2019-12-20 11:12:11 +00:00
crel (
'label' ,
2020-02-15 13:43:49 +00:00
"Playback tempo factor: (< 1 is slower, >1 is faster)" ,
2019-12-20 11:12:11 +00:00
crel ( 'input' , {
'type' : 'number' ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'tempo_factor' ] = parseFloat ( e . target . value )
}
} ,
'value' : this . configuration . hasOwnProperty ( 'tempo_factor' ) ? this . configuration . tempo _factor : 1 ,
'step' : 0.01
} )
2020-02-15 13:43:49 +00:00
) , crel ( 'br' ) ,
2020-01-13 07:23:30 +00:00
crel (
'label' ,
"Playback pitch modifier: (< 0 is lower, >0 is higher)" ,
crel ( 'input' , {
'type' : 'number' ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'pitch_modifier' ] = parseFloat ( e . target . value )
}
} ,
'value' : this . configuration . hasOwnProperty ( 'pitch_modifier' ) ? this . configuration . pitch _modifier : 0 ,
'step' : 1
} )
) ,
2019-07-03 16:33:31 +00:00
crel ( 'hr' ) ,
2019-07-04 14:33:10 +00:00
crel ( 'h2' , 'Light fade setting #0' ) ,
2019-07-03 16:33:31 +00:00
crel (
'label' ,
"Light intensity: " ,
crel ( 'input' , {
'type' : 'number' ,
'min' : 0 ,
'max' : 255 ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light0_intensity' ] = e . target . value
}
} ,
2019-07-10 12:07:57 +00:00
'value' : this . configuration . hasOwnProperty ( 'light0_intensity' ) ? this . configuration . light0 _intensity : ""
2019-07-03 16:33:31 +00:00
} )
) ,
crel (
'label' ,
"Fade time: " ,
crel ( 'input' , {
'type' : 'number' ,
'min' : 1 ,
2019-08-26 06:04:24 +00:00
'max' : 92 ,
'step' : 1 ,
2019-07-03 16:33:31 +00:00
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light0_fade' ] = e . target . value
}
} ,
2019-07-04 14:33:10 +00:00
'value' : this . configuration . hasOwnProperty ( 'light0_fade' ) ? this . configuration . light0 _fade : ""
2019-07-03 16:33:31 +00:00
} )
) ,
2019-11-28 15:21:00 +00:00
crel (
'label' ,
"Is Sophie: " ,
crel ( 'input' , {
'type' : 'checkbox' ,
'checked_value' : this . configuration . hasOwnProperty ( 'light0_isSophie' ) ? this . configuration . light0 _isSophie : false ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light0_isSophie' ] = e . target . checked
}
}
} )
) ,
2019-07-04 14:33:10 +00:00
crel ( 'h2' , 'Light fade setting #1' ) ,
2019-07-03 16:33:31 +00:00
crel (
'label' ,
"Light intensity: " ,
crel ( 'input' , {
'type' : 'number' ,
'min' : 0 ,
'max' : 255 ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light1_intensity' ] = e . target . value
}
} ,
2019-07-04 14:33:10 +00:00
'value' : this . configuration . hasOwnProperty ( 'light1_intensity' ) ? this . configuration . light1 _intensity : ""
2019-07-03 16:33:31 +00:00
} )
) ,
crel (
'label' ,
"Fade time: " ,
crel ( 'input' , {
'type' : 'number' ,
'min' : 1 ,
2019-07-04 14:33:10 +00:00
// 'max': 5,
2019-07-03 16:33:31 +00:00
'step' : . 1 ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light1_fade' ] = e . target . value
}
} ,
2019-07-04 14:33:10 +00:00
'value' : this . configuration . hasOwnProperty ( 'light1_fade' ) ? this . configuration . light1 _fade : ""
2019-07-03 16:33:31 +00:00
} )
) ,
2019-11-28 15:21:00 +00:00
crel (
'label' ,
"Is Sophie: " ,
crel ( 'input' , {
'type' : 'checkbox' ,
'checked_value' : this . configuration . hasOwnProperty ( 'light1_isSophie' ) ? this . configuration . light1 _isSophie : false ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light1_isSophie' ] = e . target . checked
}
}
} )
) ,
2019-07-04 14:33:10 +00:00
crel ( 'h2' , 'Light fade setting #2' ) ,
2019-07-03 16:33:31 +00:00
crel (
'label' ,
"Light intensity: " ,
crel ( 'input' , {
'type' : 'number' ,
'min' : 0 ,
'max' : 255 ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light2_intensity' ] = e . target . value
}
} ,
2019-07-04 14:33:10 +00:00
'value' : this . configuration . hasOwnProperty ( 'light2_intensity' ) ? this . configuration . light2 _intensity : ""
2019-07-03 16:33:31 +00:00
} )
) ,
crel (
'label' ,
"Fade time: " ,
crel ( 'input' , {
'type' : 'number' ,
'min' : 1 ,
2019-07-04 14:33:10 +00:00
// 'max': 5,
2019-07-03 16:33:31 +00:00
'step' : . 1 ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light2_fade' ] = e . target . value
}
} ,
2019-07-04 14:33:10 +00:00
'value' : this . configuration . hasOwnProperty ( 'light2_fade' ) ? this . configuration . light2 _fade : ""
2019-07-03 16:33:31 +00:00
} )
) ,
2019-11-28 15:21:00 +00:00
crel (
'label' ,
"Is Sophie: " ,
crel ( 'input' , {
'type' : 'checkbox' ,
'checked_value' : this . configuration . hasOwnProperty ( 'light2_isSophie' ) ? this . configuration . light2 _isSophie : false ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light2_isSophie' ] = e . target . checked
}
}
} )
) ,
2019-07-04 14:33:10 +00:00
crel ( 'h2' , 'Light fade setting #3' ) ,
2019-07-03 16:33:31 +00:00
crel (
'label' ,
"Light intensity: " ,
crel ( 'input' , {
'type' : 'number' ,
'min' : 0 ,
'max' : 255 ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light3_intensity' ] = e . target . value
}
} ,
2019-07-04 14:33:10 +00:00
'value' : this . configuration . hasOwnProperty ( 'light3_intensity' ) ? this . configuration . light3 _intensity : ""
2019-07-03 16:33:31 +00:00
} )
) ,
crel (
'label' ,
"Fade time: " ,
crel ( 'input' , {
'type' : 'number' ,
'min' : 1 ,
2019-07-04 14:33:10 +00:00
// 'max': 5,
2019-07-03 16:33:31 +00:00
'step' : . 1 ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light3_fade' ] = e . target . value
}
} ,
2019-07-04 14:33:10 +00:00
'value' : this . configuration . hasOwnProperty ( 'light3_fade' ) ? this . configuration . light3 _fade : ""
2019-07-03 16:33:31 +00:00
} )
) ,
2019-11-28 15:21:00 +00:00
crel (
'label' ,
"Is Sophie: " ,
crel ( 'input' , {
'type' : 'checkbox' ,
'checked_value' : this . configuration . hasOwnProperty ( 'light3_isSophie' ) ? this . configuration . light3 _isSophie : false ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light3_isSophie' ] = e . target . checked
}
}
} )
) ,
2019-07-04 14:33:10 +00:00
crel ( 'h2' , 'Light fade setting #4' ) ,
2019-07-03 16:33:31 +00:00
crel (
'label' ,
"Light intensity: " ,
crel ( 'input' , {
'type' : 'number' ,
'min' : 0 ,
'max' : 255 ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light4_intensity' ] = e . target . value
}
} ,
2019-07-04 14:33:10 +00:00
'value' : this . configuration . hasOwnProperty ( 'light4_intensity' ) ? this . configuration . light4 _intensity : ""
2019-07-03 16:33:31 +00:00
} )
) ,
crel (
'label' ,
"Fade time: " ,
crel ( 'input' , {
'type' : 'number' ,
'min' : 1 ,
2019-07-04 14:33:10 +00:00
// 'max': 5,
2019-07-03 16:33:31 +00:00
'step' : . 1 ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light4_fade' ] = e . target . value
}
} ,
2019-07-04 14:33:10 +00:00
'value' : this . configuration . hasOwnProperty ( 'light4_fade' ) ? this . configuration . light4 _fade : ""
2019-07-03 16:33:31 +00:00
} )
2019-11-28 15:21:00 +00:00
) ,
crel (
'label' ,
"Is Sophie: " ,
crel ( 'input' , {
'type' : 'checkbox' ,
'checked_value' : this . configuration . hasOwnProperty ( 'light4_isSophie' ) ? this . configuration . light4 _isSophie : false ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'light4_isSophie' ] = e . target . checked
}
}
} )
)
2019-05-12 12:54:37 +00:00
) ;
2019-05-13 12:12:54 +00:00
2019-05-12 12:54:37 +00:00
document . getElementById ( "interface" ) . appendChild ( configEl ) ;
}
2019-01-24 13:27:04 +00:00
showMsg ( msg ) {
let msgEl = document . getElementById ( 'msg' ) ;
2019-01-23 14:26:44 +00:00
msgEl . innerHTML = "" ;
2019-05-13 12:12:54 +00:00
2019-01-25 16:09:18 +00:00
if ( msg == null ) {
return ;
2019-01-25 14:45:46 +00:00
}
2019-05-13 12:12:54 +00:00
2019-01-24 13:27:04 +00:00
let startAttributes = {
2019-01-23 14:26:44 +00:00
'name' : msg [ '@id' ] + '-start' ,
2019-02-15 11:26:56 +00:00
// 'readonly': 'readonly',
2019-01-23 14:26:44 +00:00
'type' : 'checkbox' ,
'on' : {
'change' : this . getEditEventListener ( )
}
}
2019-01-24 13:27:04 +00:00
if ( msg [ 'start' ] == true ) {
2019-01-23 14:26:44 +00:00
startAttributes [ 'checked' ] = 'checked' ;
}
2019-03-07 19:19:43 +00:00
let beginningAttributes = {
'name' : msg [ '@id' ] + '-beginning' ,
// 'readonly': 'readonly',
'type' : 'checkbox' ,
'on' : {
'change' : this . getEditEventListener ( )
}
}
if ( msg [ 'beginning' ] == true ) {
beginningAttributes [ 'checked' ] = 'checked' ;
}
2019-05-13 12:12:54 +00:00
2019-04-26 09:14:49 +00:00
// chapter marker:
let chapterAttributes = {
'name' : msg [ '@id' ] + '-chapterStart' ,
// 'readonly': 'readonly',
'type' : 'checkbox' ,
'on' : {
'change' : this . getEditEventListener ( )
}
}
if ( typeof msg [ 'chapterStart' ] !== 'undefined' && msg [ 'chapterStart' ] == true ) {
chapterAttributes [ 'checked' ] = 'checked' ;
}
2019-05-13 12:12:54 +00:00
2019-08-23 10:37:01 +00:00
let generatedDirectionsJumpToChapterAttributes = {
'name' : msg [ '@id' ] + '-generatedDirectionsJumpToChapter' ,
// 'readonly': 'readonly',
'type' : 'checkbox' ,
'on' : {
'change' : this . getEditEventListener ( )
}
}
if ( msg . hasOwnProperty ( 'generatedDirectionsJumpToChapter' ) && msg [ 'generatedDirectionsJumpToChapter' ] == true ) {
generatedDirectionsJumpToChapterAttributes [ 'checked' ] = 'checked' ;
}
2020-02-15 13:43:49 +00:00
let dontGenerateDirectionsAttributes = {
'name' : msg [ '@id' ] + '-dontGenerateDirections' ,
// 'readonly': 'readonly',
'type' : 'checkbox' ,
'on' : {
'change' : this . getEditEventListener ( )
}
}
if ( msg . hasOwnProperty ( 'dontGenerateDirections' ) && msg [ 'dontGenerateDirections' ] == true ) {
dontGenerateDirectionsAttributes [ 'checked' ] = 'checked' ;
}
2019-02-28 17:58:03 +00:00
let params = { } ;
if ( msg . hasOwnProperty ( 'params' ) ) {
params = msg [ 'params' ] ;
} else {
msg [ 'params' ] = { } ;
}
2019-05-13 12:12:54 +00:00
2019-02-24 20:38:08 +00:00
let audioSrcEl = crel ( 'source' , { 'src' : msg [ 'audio' ] ? msg [ 'audio' ] [ 'file' ] : this . getAudioUrlForMsg ( msg ) } ) ;
2019-01-24 14:01:01 +00:00
let audioSpan = crel (
'span' ,
{
'title' : msg [ 'audio' ] ? msg [ 'audio' ] [ 'file' ] : "" ,
'class' : "label-value" ,
} ,
2019-02-18 19:38:54 +00:00
crel (
'audio' , { 'controls' : 'controls' } ,
audioSrcEl
2019-02-24 20:38:08 +00:00
) ,
crel ( 'div' , msg [ 'audio' ] ? crel (
'div' ,
crel ( 'div' , {
'class' : 'btn btn--delete' ,
'on' : {
'click' : function ( e ) {
e . stopPropagation ( ) ;
e . preventDefault ( ) ;
panopticon . graph . getNodeById ( msg [ '@id' ] ) [ 'audio' ] = null ;
panopticon . graph . showMsg ( msg ) ;
}
}
} , 'del' ) ,
"uploaded"
) : 'Auto-generated' )
2019-01-24 14:01:01 +00:00
) ;
2020-01-13 07:23:30 +00:00
2019-07-04 14:33:10 +00:00
let lightOptions = [
crel ( "option" , { 'value' : null } , "Do nothing" )
] ;
for ( let i = 0 ; i < 5 ; i ++ ) {
let l = { 'value' : i } ;
if ( msg . hasOwnProperty ( 'light' ) && msg [ 'light' ] == i ) {
l [ 'selected' ] = 'selected' ;
}
let intensity = "?" ;
if ( this . configuration . hasOwnProperty ( ` light ${ i } _intensity ` ) ) {
intensity = this . configuration [ ` light ${ i } _intensity ` ] ;
}
let duration = "?" ;
if ( this . configuration . hasOwnProperty ( ` light ${ i } _fade ` ) ) {
duration = this . configuration [ ` light ${ i } _fade ` ] ;
}
lightOptions . push ( crel ( "option" , l , ` Fade preset # ${ i } ( ${ intensity } in ${ duration } s) ` ) ) ;
2019-06-08 14:10:46 +00:00
}
2020-01-13 07:23:30 +00:00
2019-07-04 14:33:10 +00:00
// let lightOptionNone = {'value': null}
2020-01-13 07:23:30 +00:00
//
2019-07-04 14:33:10 +00:00
// let lightOptionOn = {'value': 1}
// let lightOptionOff = {'value': 0}
2020-01-13 07:23:30 +00:00
//
2019-07-04 14:33:10 +00:00
// if(msg.hasOwnProperty('light')) {
// if(msg['light'] === 1) lightOptionOn['selected'] = 'selected';
// if(msg['light'] === 0) lightOptionOff['selected'] = 'selected';
// if(msg['light'] === null) lightOptionNone['selected'] = 'selected';
// }
2020-01-13 07:23:30 +00:00
2019-01-24 13:27:04 +00:00
let msgInfoEl = crel ( 'div' , { 'class' : 'msg__info' } ,
2019-01-25 16:09:18 +00:00
crel ( 'div' , {
2019-02-24 20:38:08 +00:00
'class' : 'btn btn--delete btn--delete-msg' ,
2019-01-25 16:09:18 +00:00
'on' : {
'click' : function ( e ) {
if ( confirm ( ` Are you sure you want to remove message ${ msg [ '@id' ] } ` ) ) {
panopticon . graph . rmMsg ( msg ) ;
panopticon . graph . showMsg ( null ) ;
}
}
}
} , 'delete' ) ,
2019-02-25 11:18:45 +00:00
crel ( 'h1' , { 'class' : 'msg__id' } , msg [ '@id' ] + ` ( ${ panopticon . graph . distances [ msg [ '@id' ] ] } ) ` ) ,
2019-01-24 13:27:04 +00:00
crel ( 'label' ,
crel ( 'span' , 'Text' ) ,
crel ( 'input' , {
2019-01-23 14:26:44 +00:00
'name' : msg [ '@id' ] + '-text' ,
'value' : msg [ 'text' ] ,
'on' : {
2019-02-18 19:38:54 +00:00
'change' : this . getEditEventListener ( function ( ) {
audioSrcEl . src = panopticon . graph . getAudioUrlForMsg ( msg ) ;
audioSrcEl . parentElement . load ( ) ;
} )
2019-01-23 14:26:44 +00:00
}
} )
) ,
2019-11-30 17:04:12 +00:00
crel ( 'label' ,
crel ( 'span' , 'Label' ) ,
crel ( 'input' , {
'name' : msg [ '@id' ] + '-label' ,
'value' : msg . hasOwnProperty ( 'label' ) ? msg [ 'label' ] : "" ,
'on' : {
'change' : this . getEditEventListener ( )
}
} )
) ,
2019-01-24 13:27:04 +00:00
crel ( 'label' ,
crel ( 'span' , 'Start' ) ,
crel ( 'input' , startAttributes )
2019-01-24 14:01:01 +00:00
) ,
2019-03-07 19:19:43 +00:00
crel ( 'label' ,
2019-04-26 09:14:49 +00:00
crel ( 'span' , 'Beginning' ) ,
crel ( 'input' , beginningAttributes )
) ,
crel ( 'label' ,
crel ( 'span' , 'Chapter start' ) ,
crel ( 'input' , chapterAttributes )
) ,
2019-01-24 14:01:01 +00:00
crel ( 'label' ,
crel ( 'span' , 'Audio' ) ,
audioSpan ,
crel ( 'input' , {
'type' : 'file' ,
'name' : 'audio' ,
'accept' : '.wav,.ogg,.mp3' ,
'on' : {
'change' : function ( e ) {
audioSpan . innerHTML = "..." ;
panopticon . graph . saveJson ( msg [ '@id' ] , e . target , function ( e2 ) {
2019-02-15 11:26:56 +00:00
// console.log(e, e2);
2019-01-24 14:01:01 +00:00
audioSpan . innerHTML = e . target . files [ 0 ] . name + "<sup>*</sup>" ;
2019-01-25 09:43:55 +00:00
// reload graph:
2019-02-15 11:26:56 +00:00
// console.log('reload', panopticon.graph.language_code);
2019-01-25 09:43:55 +00:00
panopticon . loadNarrative ( panopticon . graph . language _code ) ;
2019-01-24 14:01:01 +00:00
} ) ;
// console.log(this,e);
}
}
} )
2019-02-11 20:28:48 +00:00
) ,
crel ( 'label' ,
2019-02-28 17:58:03 +00:00
crel ( 'span' , {
"title" : "The time after the reply in which one can still interrupt to continue speaking"
} , 'Afterrun time' ) ,
crel ( 'input' , {
'name' : msg [ '@id' ] + '-afterrunTime' ,
'step' : "0.1" ,
'value' : msg [ 'afterrunTime' ] ,
'type' : 'number' ,
'on' : {
'change' : this . getEditEventListener ( )
}
} )
) ,
crel ( 'label' ,
crel ( 'span' , {
"title" : "Playback volume factor"
} , 'Volume factor' ) ,
crel ( 'input' , {
'name' : msg [ '@id' ] + '-params.vol' ,
2019-05-14 15:56:43 +00:00
'value' : params . hasOwnProperty ( 'vol' ) ? params [ 'vol' ] : . 8 ,
2019-02-28 17:58:03 +00:00
'step' : "0.1" ,
'type' : 'number' ,
'on' : {
'change' : this . getEditEventListener ( )
}
} )
) ,
crel ( 'label' ,
crel ( 'span' , {
"title" : "Playback tempo factor"
} , 'Tempo factor' ) ,
crel ( 'input' , {
'name' : msg [ '@id' ] + '-params.tempo' ,
'value' : params . hasOwnProperty ( 'tempo' ) ? params [ 'tempo' ] : 1 ,
'step' : "0.1" ,
'min' : "0.1" ,
'type' : 'number' ,
'on' : {
'change' : this . getEditEventListener ( )
}
} )
) ,
crel ( 'label' ,
crel ( 'span' , {
2019-12-20 11:12:11 +00:00
"title" : "Playback pitch: negative: lower, positive: higher"
} , 'Pitch' ) ,
2019-02-28 17:58:03 +00:00
crel ( 'input' , {
'name' : msg [ '@id' ] + '-params.pitch' ,
'value' : params . hasOwnProperty ( 'pitch' ) ? params [ 'pitch' ] : 0 ,
'step' : "0.1" ,
'type' : 'number' ,
'on' : {
'change' : this . getEditEventListener ( )
}
} )
) ,
2019-05-13 12:12:54 +00:00
2019-02-28 17:58:03 +00:00
// color for beter overview
crel ( 'label' ,
crel ( 'span' , {
"title" : "Color - for your eyes only"
} , 'Color' ) ,
crel ( 'input' , {
'name' : msg [ '@id' ] + '-color' ,
'value' : msg . hasOwnProperty ( 'color' ) ? msg [ 'color' ] : '#77618e' ,
'type' : 'color' ,
'on' : {
'change' : this . getEditEventListener ( )
}
2019-02-11 20:28:48 +00:00
} )
2019-06-08 14:10:46 +00:00
) ,
crel ( 'label' ,
crel ( 'span' , {
"title" : "What to do with the light when this message is triggered?"
} , 'Light change' ) ,
crel ( 'select' , {
'name' : msg [ '@id' ] + '-light' ,
'on' : {
'change' : function ( e ) {
msg [ 'light' ] = e . target . value === "null" ? null : parseInt ( e . target . value ) ;
panopticon . graph . build ( ) ;
}
}
} ,
2019-07-04 14:33:10 +00:00
lightOptions
2019-06-08 14:10:46 +00:00
)
2019-08-23 10:37:01 +00:00
) ,
crel ( 'label' ,
crel ( 'span' , {
'title' : "Only for diversions: when this message is the final message of a diversion, it jumps back to the next chapter instead of the given message (overrules settings of diversion on a per message basis)"
} , 'Generated directions jump to next chapter' ) ,
crel ( 'input' , generatedDirectionsJumpToChapterAttributes )
) ,
2020-02-15 13:43:49 +00:00
crel ( 'label' ,
crel ( 'span' , {
'title' : "Only for diversions: when this message is the final message of a diversion, it ends the story instead of generating a return direction"
} , 'Is story ending (when diversion)' ) ,
crel ( 'input' , dontGenerateDirectionsAttributes )
) ,
2019-01-23 14:26:44 +00:00
) ;
2019-01-24 13:27:04 +00:00
msgEl . appendChild ( msgInfoEl ) ;
2019-01-23 14:26:44 +00:00
2019-05-13 12:12:54 +00:00
2019-01-25 16:09:18 +00:00
if ( panopticon . hugveys . selectedId ) {
2019-05-11 21:40:52 +00:00
msgEl . appendChild ( crel (
2019-01-25 16:09:18 +00:00
'div' ,
{ 'class' : 'play' } ,
crel (
'div' , {
'class' : 'btn btn--play' ,
'on' : {
'click' : function ( e ) {
console . log ( 'go save' ) ;
panopticon . graph . saveJson ( null , null , function ( ) {
console . log ( 'saved, now play' ) ;
2019-05-11 21:40:52 +00:00
panopticon . playFromSelected ( msg [ '@id' ] , true ) ;
2019-01-25 16:09:18 +00:00
} ) ;
}
}
} ,
"Save & play on #" + panopticon . hugveys . selectedId
)
2019-05-11 21:40:52 +00:00
) ) ;
msgEl . appendChild ( crel (
'div' ,
{ 'class' : 'play' } ,
crel (
'div' , {
'class' : 'btn btn--play' ,
'on' : {
'click' : function ( e ) {
panopticon . playFromSelected ( msg [ '@id' ] , false ) ;
}
}
} ,
"Continue on #" + panopticon . hugveys . selectedId
)
) ) ;
2019-05-13 12:12:54 +00:00
2019-01-25 16:09:18 +00:00
}
2019-05-13 12:12:54 +00:00
2019-01-25 16:09:18 +00:00
2019-01-23 14:26:44 +00:00
// let directionHEl = document.createElement('h2');
// directionHEl.innerHTML = "Directions";
2019-01-24 13:27:04 +00:00
let fromDirections = [ ] , toDirections = [ ] ;
2019-01-23 14:26:44 +00:00
2019-01-24 13:27:04 +00:00
for ( let direction of this . getDirectionsTo ( msg ) ) {
toDirections . push ( this . getDirectionEl ( direction , msg ) ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
for ( let direction of this . getDirectionsFrom ( msg ) ) {
fromDirections . push ( this . getDirectionEl ( direction , msg ) ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
let directionsEl = crel ( 'div' , { 'class' : 'directions' } ,
crel ( 'h2' , 'Directions' ) ,
2019-01-30 17:00:40 +00:00
... toDirections ,
crel ( 'div' , {
'class' : 'btn btn--create' ,
'on' : {
'click' : function ( e ) {
panopticon . graph . createConnectedMsg ( msg ) ;
}
}
} , 'Create new message' ) ,
... fromDirections
2019-01-23 14:26:44 +00:00
) ;
2019-01-24 13:27:04 +00:00
msgEl . appendChild ( directionsEl ) ;
2019-01-23 14:26:44 +00:00
}
2019-11-30 17:04:12 +00:00
getLabel ( msg ) {
if ( msg . hasOwnProperty ( 'label' ) && msg [ 'label' ] . length > 0 )
return msg [ 'label' ] ;
return msg [ 'text' ] ;
}
2019-01-24 13:27:04 +00:00
getDirectionEl ( direction , msg ) {
2019-01-23 14:26:44 +00:00
let g = this ;
2019-01-24 13:27:04 +00:00
let directionEl = crel ( 'div' ,
{
'class' : 'direction ' + ( direction [ 'source' ] == msg ? 'dir-to' : 'dir-from' ) ,
'on' : {
'mouseover' : function ( e ) {
directionEl . classList . add ( 'dir-highlight' ) ;
document . getElementById ( direction [ '@id' ] ) . classList . add ( 'dir-highlight' ) ;
} ,
'mouseout' : function ( e ) {
directionEl . classList . remove ( 'dir-highlight' ) ;
document . getElementById ( direction [ '@id' ] ) . classList . remove ( 'dir-highlight' ) ;
}
}
} ,
crel (
'h3' ,
{ 'title' : direction [ '@id' ] } ,
2019-11-30 17:04:12 +00:00
direction [ 'source' ] == msg ? ` To ${ this . getLabel ( direction [ 'target' ] ) } ` : ` From ${ this . getLabel ( direction [ 'source' ] ) } `
2019-01-24 13:27:04 +00:00
) ,
crel ( 'div' , {
'class' : 'btn btn--delete' ,
'on' : {
2019-01-25 16:09:18 +00:00
'click' : ( e ) => {
if ( confirm ( "Do you want to remove this direction and its conditions?" ) ) {
2019-05-13 12:12:54 +00:00
g . rmDirection ( direction ) ;
2019-01-25 16:09:18 +00:00
}
}
2019-01-24 13:27:04 +00:00
}
2019-01-25 16:09:18 +00:00
} , 'disconnect' )
2019-01-24 13:27:04 +00:00
) ;
2019-05-13 12:12:54 +00:00
2019-01-24 13:27:04 +00:00
for ( let conditionId of direction [ 'conditions' ] ) {
let condition = this . getNodeById ( conditionId ) ;
directionEl . appendChild ( this . getEditConditionFormEl ( condition , direction ) ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
directionEl . appendChild ( this . getAddConditionFormEl ( direction ) ) ;
2019-01-23 14:26:44 +00:00
return directionEl ;
}
2019-01-24 13:27:04 +00:00
getEditConditionFormEl ( condition , direction ) {
let conditionEl = crel ( 'div' , { 'class' : 'condition condition--edit' } ,
2019-01-25 16:09:18 +00:00
crel ( 'h4' , { 'title' : condition [ '@id' ] } , condition [ 'type' ] ) ,
crel ( 'div' , {
'class' : 'btn btn--delete' ,
'on' : {
'click' : ( e ) => {
if ( confirm ( "Do you want to remove this condition?" ) ) {
2019-02-25 14:56:59 +00:00
// console.log('remove condition for direction', condition, direction);
2019-05-13 12:12:54 +00:00
panopticon . graph . rmCondition ( condition , direction ) ;
2019-01-25 16:09:18 +00:00
}
}
}
2019-02-11 20:28:48 +00:00
} , 'delete' ) ,
... this . getConditionInputsForType ( condition [ 'type' ] , condition [ '@id' ] , condition [ 'vars' ] )
2019-01-23 14:26:44 +00:00
)
2019-01-24 13:27:04 +00:00
let labelLabel = document . createElement ( 'label' ) ;
2019-01-23 14:26:44 +00:00
labelLabel . innerHTML = "Description" ;
2019-01-24 13:27:04 +00:00
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 ) ;
2019-05-13 12:12:54 +00:00
2019-01-23 14:26:44 +00:00
2019-02-11 20:28:48 +00:00
// for ( let v in condition['vars'] ) {
// let varLabel = document.createElement( 'label' );
// varLabel.innerHTML = v;
// 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 );
// }
2019-01-23 14:26:44 +00:00
return conditionEl ;
}
getConditionTypes ( ) {
2019-02-11 20:28:48 +00:00
return {
2019-01-23 14:26:44 +00:00
'timeout' : {
2019-02-15 11:26:56 +00:00
'seconds' : { 'type' : 'number' , 'value' : 10 , 'min' : 0 , 'step' : 0.1 , 'unit' : "s" } ,
2019-03-27 14:43:02 +00:00
'onlyIfNoReply' : { 'type' : 'checkbox' , label : "Only if no reply" , "title" : "This timeout is only used if the participant doesn't say a word. If the participant starts speaking within the time of this timeout condition, only other conditions are applicable." } ,
2019-04-02 06:54:26 +00:00
'needsReply' : { 'type' : 'checkbox' , label : "Reply needed" , "title" : "If checked, the timeout is counted if met. Used by consecutive-timeouts diversions." } ,
2019-01-23 14:26:44 +00:00
} ,
2019-07-04 20:05:51 +00:00
'variable_storage' : {
// when matched, variable will be accessible as {store_name_1}
'var_name' : { 'label' : "Variable name" , 'type' : 'text' , 'description' : "When matched, variable will be accessible as $stored_VARNAME_1, $stored_VARNAME_2.. etc (use the name given here instead of VARNAME)" } ,
'number' : { 'label' : "Nr. of items to get" , 'type' : 'number' , 'value' : 5 , 'min' : 0 , 'step' : 1 } ,
2019-08-30 19:55:55 +00:00
'unique' : { 'label' : "Unique items" , 'type' : 'checkbox' , 'title' : "If checked, every word is returned only once, eg. love, dream, love, returns love, dream" } ,
2019-07-04 20:05:51 +00:00
} ,
2019-01-23 14:26:44 +00:00
'replyContains' : {
2019-02-11 20:28:48 +00:00
'delays.0.minReplyDuration' : { 'type' : 'number' , 'value' : 0 , 'min' : 0 , 'step' : 0.1 , 'label' : 'Delay 1 - reply duration' , 'unit' : "s" , 'readonly' : 'readonly' } ,
'delays.0.waitTime' : { 'type' : 'number' , 'value' : 3 , 'min' : 0 , 'step' : 0.1 , 'label' : 'Delay 1 - wait time' , 'unit' : "s" } ,
'delays.1.minReplyDuration' : { 'type' : 'number' , 'value' : 5 , 'min' : 0 , 'step' : 0.1 , 'label' : 'Delay 2 - reply duration' , 'unit' : "s" } ,
'delays.1.waitTime' : { 'type' : 'number' , 'value' : 1 , 'min' : 0 , 'step' : 0.1 , 'label' : 'Delay 2 - time' , 'unit' : "s" } ,
'delays.2.minReplyDuration' : { 'type' : 'number' , 'value' : 10 , 'min' : 0 , 'step' : 0.1 , 'label' : 'Delay 3 - reply duration' , 'unit' : "s" } ,
'delays.2.waitTime' : { 'type' : 'number' , 'value' : 0 , 'min' : 0 , 'step' : 0.1 , 'label' : 'Delay 3 - time' , 'unit' : "s" } ,
'regex' : { 'value' : '' , 'placeholder' : "match any input" } ,
'instantMatch' : { 'value' : '' , 'title' : "When matched, don't wait for reply to finish. Instantly take this direction." , 'type' : 'checkbox' } ,
2019-03-29 13:11:48 +00:00
} ,
2019-11-12 18:37:14 +00:00
'loop_time' : {
// can be used in two ways
'less_than' : { 'type' : 'number' , 'value' : 0 , 'min' : 0 , 'step' : 1 , 'label' : "Time in seconds, the loop should be before, 0 is ignored" } ,
'more_than' : { 'type' : 'number' , 'value' : 0 , 'min' : 0 , 'step' : 1 , 'label' : "Time the loop should be after, 0 is ignored" } ,
'inverseMatch' : { 'title' : "Inverse the matching." , 'type' : 'checkbox' } ,
2019-07-10 14:14:46 +00:00
} ,
2019-03-29 13:11:48 +00:00
'variable' : {
'variable' : { 'value' : '' , 'placeholder' : "Variable name (without $)" } ,
'notSet' : { "label" : "Not set" , 'value' : '' , 'title' : "Match if the variable is _not_ set." , 'type' : 'checkbox' } ,
2019-04-26 09:51:07 +00:00
} ,
2019-06-13 20:40:32 +00:00
'variableEquals' : {
'variable1' : { 'value' : '' , 'placeholder' : "Variable name (without $)" } ,
'variable2' : { 'value' : '' , 'placeholder' : "Variable name (without $)" } ,
'notEq' : { "label" : "Not equal" , 'value' : '' , 'title' : "Match if the variables are _not_ equal." , 'type' : 'checkbox' } ,
} ,
2019-04-26 09:51:07 +00:00
'diversion' : {
2019-06-16 15:44:13 +00:00
'diversionId' : { 'tag' : 'select' , 'value' : '' , 'options' : this . diversions . map ( ( d ) => d [ '@id' ] ) } ,
2019-04-26 09:51:07 +00:00
'inverseMatch' : { "label" : "Match if not done" , 'value' : '' , 'title' : "Match if the diversion has _not_ been done." , 'type' : 'checkbox' } ,
2019-06-16 15:44:13 +00:00
} ,
'messagePlayed' : {
'msgId' : { 'value' : '' , 'label' : 'Message ID' , 'placeholder' : "(eg. en-njsm9b0ni)" } ,
'inverseMatch' : { "label" : "Match if not played" , 'value' : '' , 'title' : "Match if the message has _not_ been played." , 'type' : 'checkbox' } ,
2019-11-11 18:27:20 +00:00
} ,
// audioError has no parameters. Just checks if there was an error fetching the audio file
'audioError' : { }
2019-02-11 20:28:48 +00:00
} ;
2019-01-23 14:26:44 +00:00
}
2019-05-13 12:12:54 +00:00
2019-02-11 20:28:48 +00:00
getConditionInputsForType ( type , conditionId , values ) {
let inputs = [ ] ;
2019-01-23 14:26:44 +00:00
let vars = this . getConditionTypes ( ) [ type ] ;
2019-01-24 13:27:04 +00:00
for ( let v in vars ) {
2019-01-23 14:26:44 +00:00
let attr = vars [ v ] ;
2019-04-26 09:51:07 +00:00
let inputType = attr . hasOwnProperty ( 'tag' ) ? attr [ 'tag' ] : 'input' ;
2019-05-13 12:12:54 +00:00
2019-02-11 20:28:48 +00:00
attr [ 'name' ] = typeof conditionId == 'undefined' ? v : ` ${ conditionId } -vars. ${ v } ` ;
if ( typeof values != 'undefined' ) {
let value = this . _getValueForPath ( v , values ) ;
2019-02-15 11:26:56 +00:00
if ( attr [ 'type' ] == 'checkbox' ) {
if ( value )
attr [ 'checked' ] = 'checked' ;
}
2019-02-11 20:28:48 +00:00
attr [ 'value' ] = typeof value == 'undefined' ? "" : value ;
attr [ 'on' ] = {
2019-05-13 12:12:54 +00:00
'change' : this . getEditEventListener ( )
2019-02-11 20:28:48 +00:00
} ;
} else {
2019-02-15 11:26:56 +00:00
// console.log(attr);
2019-02-11 20:28:48 +00:00
}
2019-05-13 12:12:54 +00:00
2019-02-11 20:28:48 +00:00
inputs . push (
2019-01-24 13:27:04 +00:00
crel ( 'label' ,
2019-02-11 20:28:48 +00:00
crel ( 'span' , {
'title' : attr . hasOwnProperty ( 'title' ) ? attr [ 'title' ] : ""
} , attr . hasOwnProperty ( 'label' ) ? attr [ 'label' ] : v ) ,
2019-04-26 09:51:07 +00:00
crel ( inputType , attr )
2019-02-11 20:28:48 +00:00
// crel('span', {'class': 'label-unit'}, attr.hasOwnProperty('unit') ? attr['unit'] : "" )
2019-01-23 14:26:44 +00:00
)
) ;
2019-07-04 20:05:51 +00:00
if ( attr . hasOwnProperty ( 'description' ) ) {
inputs . push ( crel ( 'div' , { 'class' : 'description' } , attr [ 'description' ] ) ) ;
}
2020-01-13 07:23:30 +00:00
2019-01-23 14:26:44 +00:00
}
2019-02-11 20:28:48 +00:00
return inputs ;
}
fillConditionFormForType ( conditionForm , type , values ) {
conditionForm . innerHTML = "" ;
let inputs = this . getConditionInputsForType ( type ) ;
for ( let i of inputs ) {
conditionForm . appendChild ( i ) ;
}
}
2019-05-13 12:12:54 +00:00
2019-02-11 20:28:48 +00:00
_getValueForPath ( path , vars ) {
path = path . split ( '.' ) ; // use vars.test to set ['vars']['test'] = value
let v = vars ;
2019-02-15 11:26:56 +00:00
let result = null ;
2019-02-11 20:28:48 +00:00
for ( let i = 0 ; i < path . length ; i ++ ) {
if ( ! isNaN ( parseInt ( path [ i ] ) ) && isFinite ( path [ i ] ) ) {
// is int, use array, instead of obj
path [ i ] = parseInt ( path [ i ] ) ;
}
v = v [ path [ i ] ] ;
2019-02-15 11:26:56 +00:00
if ( i == path . length - 1 ) {
result = v ;
}
2019-02-11 20:28:48 +00:00
if ( typeof v == 'undefined' ) {
break ;
}
}
2019-02-15 11:26:56 +00:00
return result ;
2019-02-11 20:28:48 +00:00
}
2019-05-13 12:12:54 +00:00
2019-02-11 20:28:48 +00:00
/ * *
* Save an array path ( string ) with a value to an object . Used to turn
* strings into nested arrays
* @ param string path
* @ param { any } value
* @ param array | object vars
* /
_formPathToVars ( path , value , vars ) {
path = path . split ( '.' ) ; // use vars.test to set ['vars']['test'] = value
let res = vars ;
for ( let i = 0 ; i < path . length ; i ++ ) {
if ( i == ( path . length - 1 ) ) {
res [ path [ i ] ] = value ;
} else {
if ( ! isNaN ( parseInt ( path [ i + 1 ] ) ) && isFinite ( path [ i + 1 ] ) ) {
// is int, use array, instead of obj
path [ i + 1 ] = parseInt ( path [ i + 1 ] ) ;
}
if ( typeof res [ path [ i ] ] == 'undefined' ) {
res [ path [ i ] ] = typeof path [ i + 1 ] == 'number' ? [ ] : { }
}
res = res [ path [ i ] ] ;
}
}
return vars ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
getAddConditionFormEl ( direction ) {
2019-01-23 14:26:44 +00:00
let optionEls = [ ] ;
let types = this . getConditionTypes ( ) ;
2019-01-24 13:27:04 +00:00
for ( let type in types ) {
optionEls . push ( crel ( 'option' , type ) ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
let conditionForm = crel ( 'div' , { 'class' : 'condition--vars' } ) ;
2019-01-23 14:26:44 +00:00
let g = this ;
2019-01-24 13:27:04 +00:00
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' ) ;
2019-05-13 12:12:54 +00:00
2019-02-15 11:26:56 +00:00
// checkboxes to true/false
let defs = g . getConditionTypes ( ) [ type ] ;
2019-02-25 14:56:59 +00:00
// console.log(defs);
2019-02-15 11:26:56 +00:00
for ( let field in defs ) {
2019-02-25 14:56:59 +00:00
// console.log(field);
2019-02-15 11:26:56 +00:00
if ( defs [ field ] [ 'type' ] == 'checkbox' ) {
console . info ( 'configure checkbox' , field ) ;
form . set ( field , form . has ( field ) ) ;
}
}
2019-05-13 12:12:54 +00:00
2019-01-24 13:27:04 +00:00
let vars = { } ;
for ( var pair of form . entries ( ) ) {
2019-02-15 11:26:56 +00:00
// FormData only has strings & blobs, we want booleans:
if ( pair [ 1 ] === 'true' ) pair [ 1 ] = true ;
2019-05-13 12:12:54 +00:00
if ( pair [ 1 ] === 'false' ) pair [ 1 ] = false ;
2019-02-11 20:28:48 +00:00
vars = g . _formPathToVars ( pair [ 0 ] , pair [ 1 ] , vars ) ;
2019-01-23 14:26:44 +00:00
}
2019-02-11 20:28:48 +00:00
// TODO: checkboxes
2019-02-25 14:56:59 +00:00
// console.log("Createded", vars);
2019-01-24 13:27:04 +00:00
g . addConditionForDirection ( type , label , vars , direction ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
}
} ,
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 ) ;
}
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
} , optionEls ) ,
) ,
crel ( "label" ,
crel ( 'span' , "Description" ) ,
crel ( 'input' , { 'name' : 'label' } )
) ,
conditionForm ,
crel ( 'input' , {
'type' : 'submit' ,
'value' : 'create'
} )
)
2019-01-23 14:26:44 +00:00
)
) ;
2019-01-24 13:27:04 +00:00
this . fillConditionFormForType ( conditionForm , optionEls [ 0 ] . value ) ;
2019-01-23 14:26:44 +00:00
return addConditionEl ;
}
2019-05-13 12:12:54 +00:00
2019-01-25 16:09:18 +00:00
/ * *
* remove condition from the graph or merely from the given direction
* @ param { any } condition The condition to remove
* @ param { any } direction if given , only remove from this direction
* /
rmCondition ( condition , direction ) {
2019-01-23 14:26:44 +00:00
let id = condition [ '@id' ] ;
// TODO
2019-01-24 13:27:04 +00:00
if ( typeof direction != 'undefined' ) {
2019-01-25 16:09:18 +00:00
let pos = direction [ 'conditions' ] . indexOf ( id ) ;
2019-02-11 20:28:48 +00:00
console . log ( 'delete' , id , 'on direction' ) ;
2019-01-25 16:09:18 +00:00
if ( pos > - 1 ) {
direction [ 'conditions' ] . splice ( pos , 1 ) ;
}
2019-05-13 12:12:54 +00:00
2019-01-25 16:09:18 +00:00
for ( let dir of this . directions ) {
2019-02-11 20:28:48 +00:00
// console.log('check if condition exists for dir', dir)
if ( dir [ 'conditions' ] . indexOf ( id ) > - 1 ) {
2019-01-25 16:09:18 +00:00
console . log ( "Condition still in use" ) ;
this . updateFromData ( ) ;
this . build ( ) ;
this . updateMsg ( ) ;
return ;
}
}
2019-02-11 20:28:48 +00:00
console . log ( 'No use, remove' , condition )
this . _rmNode ( condition ) ;
2019-01-25 16:09:18 +00:00
} else {
for ( let dir of this . directions ) {
let pos = dir [ 'conditions' ] . indexOf ( id ) ;
if ( pos > - 1 ) {
dir [ 'conditions' ] . splice ( pos , 1 ) ;
}
}
2019-02-11 20:28:48 +00:00
console . log ( 'remove condition?' , id )
this . _rmNode ( condition ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-25 16:09:18 +00:00
this . updateMsg ( ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
getConditionEl ( condition ) {
let conditionEl = document . createElement ( 'div' ) ;
2019-01-23 14:26:44 +00:00
return conditionEl ;
}
2019-01-24 13:27:04 +00:00
getDirectionsFrom ( msg ) {
return this . directions . filter ( d => d [ 'source' ] == msg ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
getDirectionsTo ( msg ) {
return this . directions . filter ( d => d [ 'target' ] == msg ) ;
2019-01-23 14:26:44 +00:00
}
2019-04-06 10:07:45 +00:00
addMsg ( skipRebuild ) {
2019-01-23 14:26:44 +00:00
let msg = {
2019-01-24 13:27:04 +00:00
"@id" : this . language _code . substring ( 0 , 2 ) + "-n" + Date . now ( ) . toString ( 36 ) ,
2019-01-23 14:26:44 +00:00
"@type" : "Msg" ,
"text" : "New" ,
2019-02-11 20:28:48 +00:00
"start" : false ,
"afterrunTime" : 0.5 ,
2019-06-08 14:10:46 +00:00
"light" : null ,
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
this . data . push ( msg ) ;
2019-05-13 12:12:54 +00:00
2019-04-06 10:07:45 +00:00
console . log ( "skip or not to skip?" , skipRebuild ) ;
if ( typeof skipRebuild == 'undefined' || ! skipRebuild ) {
this . updateFromData ( ) ;
this . build ( ) ;
this . selectMsg ( msg ) ;
}
2019-05-13 12:12:54 +00:00
2019-01-23 14:26:44 +00:00
return msg ;
}
2019-01-24 13:27:04 +00:00
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 ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
this . _rmNode ( msg ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
_rmNode ( node ) {
2019-01-23 14:26:44 +00:00
// remove msg/direction/condition/etc
2019-01-24 13:27:04 +00:00
let i = this . data . indexOf ( node ) ;
this . data . splice ( i , 1 ) ;
2019-01-23 14:26:44 +00:00
this . updateFromData ( ) ;
this . build ( ) ;
return this . data ;
}
2019-01-24 13:27:04 +00:00
addConditionForDirection ( type , label , vars , direction ) {
let con = this . addCondition ( type , label , vars , true ) ;
direction [ 'conditions' ] . push ( con [ '@id' ] ) ;
2019-01-23 14:26:44 +00:00
this . updateFromData ( ) ;
this . build ( ) ;
this . updateMsg ( ) ;
}
2019-01-24 13:27:04 +00:00
addCondition ( type , label , vars , skip ) {
2019-01-23 14:26:44 +00:00
let con = {
2019-01-24 13:27:04 +00:00
"@id" : this . language _code . substring ( 0 , 2 ) + "-c" + Date . now ( ) . toString ( 36 ) ,
2019-01-23 14:26:44 +00:00
"@type" : "Condition" ,
"type" : type ,
"label" : label ,
"vars" : vars
}
2019-01-24 13:27:04 +00:00
this . data . push ( con ) ;
if ( skip !== true ) {
2019-01-23 14:26:44 +00:00
this . updateFromData ( ) ;
this . build ( ) ;
}
return con ;
}
2019-01-24 13:27:04 +00:00
addDirection ( source , target ) {
2019-01-23 14:26:44 +00:00
let dir = {
2019-01-24 13:27:04 +00:00
"@id" : this . language _code . substring ( 0 , 2 ) + "-d" + Date . now ( ) . toString ( 36 ) ,
2019-01-23 14:26:44 +00:00
"@type" : "Direction" ,
"source" : source ,
"target" : target ,
"conditions" : [ ]
}
2019-01-24 13:27:04 +00:00
this . data . push ( dir ) ;
2019-05-13 12:12:54 +00:00
2019-04-06 10:07:45 +00:00
let skipDistances ;
2019-04-06 10:09:29 +00:00
// orphaned target and source has no other destinations. We can copy the vertical position:
2019-04-06 10:07:45 +00:00
if ( this . getDirectionsFrom ( source ) . length < 1 && this . getDirectionsFrom ( target ) . length < 1 && this . getDirectionsTo ( target ) . length < 1 ) {
skipDistances = true ;
let distance = this . distances [ source [ '@id' ] ] ;
2019-11-11 17:25:04 +00:00
if ( distance == null ) {
skipDistances = false ;
} else {
let d = [ distance [ 0 ] + 1 , distance [ 1 ] ] ;
// create a distance based on source's position
// this saves us from running the slow calculateDistancesFromStart
this . distances [ target [ '@id' ] ] = d ;
}
2019-04-06 10:07:45 +00:00
} else {
skipDistances = false ;
}
2019-05-13 12:12:54 +00:00
2019-04-06 10:07:45 +00:00
this . updateFromData ( skipDistances ) ;
2019-01-23 14:26:44 +00:00
this . build ( ) ;
return dir ;
}
2019-01-24 13:27:04 +00:00
rmDirection ( dir ) {
this . _rmNode ( dir ) ;
2019-01-25 16:09:18 +00:00
// todo, remove orphaned conditions
2019-01-23 14:26:44 +00:00
}
createMsg ( ) {
this . addMsg ( ) ;
2019-11-11 17:25:04 +00:00
// this.build(); // already happens in addMsg()
2019-01-23 14:26:44 +00:00
}
2019-05-13 12:12:54 +00:00
2019-01-30 17:00:40 +00:00
createConnectedMsg ( sourceMsg ) {
2019-04-06 10:07:45 +00:00
console . time ( 'createConnected' ) ;
console . time ( "Add" ) ;
let newMsg = this . addMsg ( true ) ; // skipRebuild = true, as addDirection() already rebuilds the graph
2019-02-28 17:58:03 +00:00
this . getNodeById ( newMsg [ '@id' ] ) . y = this . getNodeById ( sourceMsg [ '@id' ] ) . y ;
2019-05-13 12:12:54 +00:00
2019-02-28 17:58:03 +00:00
if ( this . getNodeById ( sourceMsg [ '@id' ] ) . hasOwnProperty ( 'color' ) ) {
2019-05-13 12:12:54 +00:00
this . getNodeById ( newMsg [ '@id' ] ) . color = this . getNodeById ( sourceMsg [ '@id' ] ) . color
2019-02-28 17:58:03 +00:00
}
2019-04-06 10:07:45 +00:00
console . timeEnd ( "Add" ) ;
2019-05-13 12:12:54 +00:00
2019-04-06 10:07:45 +00:00
console . time ( "direction" ) ;
2019-01-30 17:00:40 +00:00
this . addDirection ( sourceMsg , newMsg ) ;
2019-04-06 10:07:45 +00:00
console . timeEnd ( "direction" ) ;
console . time ( "build" ) ;
// this.build(); // build is already done in addDirection()
console . timeEnd ( "build" ) ;
2019-01-30 17:00:40 +00:00
// reselect so that overview is updated
2019-04-06 10:07:45 +00:00
console . time ( "Select" ) ;
2019-01-30 17:00:40 +00:00
this . selectMsg ( newMsg ) ;
2019-04-06 10:07:45 +00:00
console . timeEnd ( "Select" ) ;
console . timeEnd ( 'createConnected' ) ;
2019-01-30 17:00:40 +00:00
}
2019-01-23 14:26:44 +00:00
2019-01-24 13:27:04 +00:00
getNodeById ( id ) {
return this . data . filter ( node => node [ '@id' ] == id ) [ 0 ] ;
2019-01-23 14:26:44 +00:00
}
/ * *
* Use wrapper method , because for event handlers 'this' will refer to
* the input object
* /
2019-02-18 19:38:54 +00:00
getEditEventListener ( callback ) {
2019-01-23 14:26:44 +00:00
let graph = this ;
2019-01-24 13:27:04 +00:00
let el = function ( e ) {
2019-02-15 11:26:56 +00:00
console . info ( "Changed" , e ) ;
2019-05-13 12:12:54 +00:00
let parts = e . target . name . split ( '-' ) ;
2019-01-25 16:09:18 +00:00
let field = parts . pop ( ) ;
let id = parts . join ( '-' ) ;
2019-01-24 13:27:04 +00:00
let node = graph . getNodeById ( id ) ;
let path = field . split ( '.' ) ; // use vars.test to set ['vars']['test'] = value
var res = node ;
2019-05-13 12:12:54 +00:00
let value = e . target . value
if ( e . target . type == 'checkbox' ) {
value = e . target . checked ;
2019-02-15 11:26:56 +00:00
}
2019-01-24 13:27:04 +00:00
for ( var i = 0 ; i < path . length ; i ++ ) {
if ( i == ( path . length - 1 ) ) {
2019-02-15 11:26:56 +00:00
res [ path [ i ] ] = value ;
2019-01-23 14:26:44 +00:00
} else {
2019-01-24 13:27:04 +00:00
res = res [ path [ i ] ] ;
2019-01-23 14:26:44 +00:00
}
}
// node[field] = e.srcElement.value;
graph . build ( ) ;
2019-05-13 12:12:54 +00:00
2019-02-18 19:38:54 +00:00
if ( typeof callback !== 'undefined' ) {
callback ( ) ;
}
2019-01-23 14:26:44 +00:00
}
return el ;
}
getJsonString ( ) {
// recreate array to have the right order of items.
2019-05-12 12:54:37 +00:00
this . data = [ this . configuration , ... this . messages , ... this . conditions ,
2019-01-25 09:43:55 +00:00
... this . directions , ... this . diversions ]
2019-01-23 14:26:44 +00:00
let d = [ ] ;
2019-02-25 11:18:45 +00:00
// let toRemove = ['sourceX', 'sourceY', 'targetX', 'targetY', 'x', 'y', 'vx', 'vy']
2019-06-08 14:10:46 +00:00
let toRemove = [ 'sourceX' , 'sourceY' , 'targetX' , 'targetY' , 'x' , 'y' , 'vx' , 'vy' ]
2019-01-24 13:27:04 +00:00
for ( let node of this . data ) {
2019-01-23 14:26:44 +00:00
let n = { } ;
2019-02-25 14:56:59 +00:00
// console.log( node['source'] );
2019-01-24 13:27:04 +00:00
for ( let e in node ) {
if ( node . hasOwnProperty ( e ) && toRemove . indexOf ( e ) == - 1 ) {
if ( this . data . indexOf ( node [ e ] ) != - 1 ) {
2019-01-23 14:26:44 +00:00
n [ e ] = node [ e ] [ '@id' ] ;
} else {
n [ e ] = node [ e ] ;
}
}
}
2019-01-24 13:27:04 +00:00
d . push ( n ) ;
2019-01-23 14:26:44 +00:00
}
2019-02-15 11:26:56 +00:00
console . info ( "Jsonified graph:" , d ) ;
2019-01-24 13:27:04 +00:00
return JSON . stringify ( d ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-23 21:38:27 +00:00
downloadJson ( ) {
2019-01-24 13:27:04 +00:00
if ( ! this . language _code ) {
alert ( "Make sure to load a language first" )
2019-01-23 21:38:27 +00:00
}
2019-01-24 13:27:04 +00:00
var blob = new Blob ( [ this . getJsonString ( ) ] , { type : 'application/json' } ) ;
if ( window . navigator . msSaveOrOpenBlob ) {
window . navigator . msSaveBlob ( blob , "pillow_talk.json" ) ;
2019-01-23 14:26:44 +00:00
}
2019-01-24 13:27:04 +00:00
else {
var elem = window . document . createElement ( 'a' ) ;
elem . href = window . URL . createObjectURL ( blob ) ;
2019-01-23 14:26:44 +00:00
elem . download = "pillow_talk.json" ;
2019-01-24 13:27:04 +00:00
document . body . appendChild ( elem ) ;
2019-01-23 14:26:44 +00:00
elem . click ( ) ;
2019-01-24 13:27:04 +00:00
document . body . removeChild ( elem ) ;
2019-01-23 14:26:44 +00:00
}
}
2019-01-24 13:27:04 +00:00
2019-01-24 14:01:01 +00:00
saveJson ( msg _id , fileInputElement , callback ) {
2019-01-24 13:27:04 +00:00
if ( ! this . language _code ) {
alert ( "Make sure to load a language first" )
2019-01-23 21:38:27 +00:00
}
2019-01-24 13:27:04 +00:00
2019-01-23 21:38:27 +00:00
let formData = new FormData ( ) ;
2019-01-24 13:27:04 +00:00
formData . append ( "language" , this . language _code ) ;
if ( msg _id ) {
formData . append ( "message_id" , msg _id ) ;
formData . append ( "audio" , fileInputElement . files [ 0 ] ) ;
2019-01-23 21:38:27 +00:00
}
2019-01-24 13:27:04 +00:00
let blob = new Blob ( [ this . getJsonString ( ) ] , { type : "application/json" } ) ;
formData . append ( "json" , blob ) ;
2019-02-15 11:26:56 +00:00
console . info ( "Save json" , formData ) ;
2019-01-23 21:38:27 +00:00
var request = new XMLHttpRequest ( ) ;
2019-11-12 10:01:56 +00:00
request . open ( "POST" , window . location . origin + "/upload" ) ;
2019-05-13 12:12:54 +00:00
2019-01-24 14:01:01 +00:00
if ( callback ) {
request . addEventListener ( "load" , callback ) ;
}
2019-05-13 12:12:54 +00:00
2019-01-24 13:27:04 +00:00
request . send ( formData ) ;
2019-01-23 21:38:27 +00:00
}
2019-01-23 14:26:44 +00:00
2019-01-24 13:27:04 +00:00
loadData ( data , language _code ) {
2019-11-11 17:25:04 +00:00
console . time ( 'load' ) ;
2019-01-23 21:38:27 +00:00
this . language _code = language _code ;
2019-01-23 14:26:44 +00:00
this . data = data ;
2019-11-11 17:25:04 +00:00
console . time ( 'load:update' ) ;
2019-01-23 14:26:44 +00:00
this . updateFromData ( ) ;
2019-11-11 17:25:04 +00:00
console . timeEnd ( 'load:update' ) ;
console . time ( 'load:build' ) ;
2019-01-24 13:27:04 +00:00
this . build ( true ) ;
2019-11-11 17:25:04 +00:00
console . timeEnd ( 'load:build' ) ;
console . timeEnd ( 'load' ) ;
2019-01-23 14:26:44 +00:00
}
2019-04-06 10:07:45 +00:00
updateFromData ( skipDistances ) {
2019-01-24 13:27:04 +00:00
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' ) ;
2019-01-25 09:43:55 +00:00
this . diversions = this . data . filter ( ( node ) => node [ '@type' ] == 'Diversion' ) ;
2019-05-13 12:12:54 +00:00
2019-05-12 12:54:37 +00:00
let configurations = this . data . filter ( ( node ) => node [ '@type' ] == 'Configuration' ) ;
this . configuration = configurations . length > 0 ? configurations [ 0 ] : {
"@id" : "config" ,
"@type" : "Configuration"
} ;
2019-05-13 12:12:54 +00:00
2019-01-24 13:27:04 +00:00
document . getElementById ( 'current_lang' ) . innerHTML = "" ;
document . getElementById ( 'current_lang' ) . appendChild ( crel ( 'span' , {
'class' : 'flag-icon ' + this . language _code
} ) ) ;
2019-05-10 11:27:32 +00:00
let storyEl = document . getElementById ( 'story' ) ;
storyEl . classList . remove ( ... panopticon . languages . map ( ( l ) => l [ 'code' ] ) )
storyEl . classList . add ( this . language _code ) ;
2019-05-13 12:12:54 +00:00
2019-04-06 10:07:45 +00:00
if ( typeof skipDistances == 'undefined' || ! skipDistances ) {
this . distances = this . calculateDistancesFromStart ( ) ;
}
2019-01-23 14:26:44 +00:00
// save state;
2019-11-11 17:25:04 +00:00
// console.time('update:save')
// this.saveState();
// console.timeEnd('update:save')
2019-01-23 14:26:44 +00:00
}
2019-05-13 12:12:54 +00:00
2019-01-25 13:10:19 +00:00
updateHugveyStatus ( hv ) {
let els = document . getElementsByClassName ( 'beenHit' ) ;
while ( els . length > 0 ) {
els [ 0 ] . classList . remove ( 'beenHit' ) ;
}
2019-01-25 14:45:46 +00:00
if ( ! hv || typeof hv [ 'history' ] == 'undefined' ) {
return ;
}
2019-05-13 12:12:54 +00:00
2019-04-27 09:51:11 +00:00
if ( hv [ 'history' ] . hasOwnProperty ( 'messages' ) ) {
for ( let msg of hv [ 'history' ] [ 'messages' ] ) {
document . getElementById ( msg [ 0 ] [ 'id' ] ) . classList . add ( 'beenHit' ) ;
2019-05-13 12:12:54 +00:00
}
2019-01-25 13:10:19 +00:00
}
2019-04-27 09:51:11 +00:00
if ( hv [ 'history' ] . hasOwnProperty ( 'directions' ) ) {
for ( let msg of hv [ 'history' ] [ 'directions' ] ) {
document . getElementById ( msg [ 0 ] [ 'id' ] ) . classList . add ( 'beenHit' ) ;
}
2019-01-25 13:10:19 +00:00
}
}
2019-01-23 14:26:44 +00:00
2019-11-11 17:25:04 +00:00
// saveState() {
// window.localStorage.setItem( "lastState", this.getJsonString() );
// }
//
// hasSavedState() {
// return window.localStorage.getItem( "lastState" ) !== null;
// }
//
// loadFromState() {
// this.loadData( JSON.parse( window.localStorage.getItem( "lastState" ) ) );
// }
2019-01-23 14:26:44 +00:00
2019-01-24 13:27:04 +00:00
build ( isInit ) {
2019-11-11 17:25:04 +00:00
console . trace ( ) ;
2019-01-24 13:27:04 +00:00
this . simulation = d3 . forceSimulation ( this . messages )
2019-02-18 21:33:31 +00:00
. force ( "link" , d3 . forceLink ( this . directions ) . id ( d => d [ '@id' ] ) . strength ( 0 ) )
// .force( "charge", d3.forceManyBody().strength( 100 ) )
// .force( "center", d3.forceCenter( this.width / 2, this.height / 2 ) )
2019-02-25 17:07:25 +00:00
. force ( "collide" , d3 . forceCollide ( this . nodeSize * 1.5 ) . strength ( 3 ) )
2019-02-18 21:33:31 +00:00
. force ( "forceX" , d3 . forceX ( function ( m ) {
2019-02-25 16:05:14 +00:00
let fx = panopticon . graph . distances [ m [ '@id' ] ] !== null ? panopticon . graph . distances [ m [ '@id' ] ] [ 0 ] * panopticon . graph . nodeSize * 4 : 0
2019-02-25 14:56:59 +00:00
// console.log('fx', m['@id'], panopticon.graph.distances[m['@id']], fx);
2019-02-18 21:33:31 +00:00
return fx ;
} ) . strength ( 50 ) )
2019-02-25 16:05:14 +00:00
. force ( "forceY" , d3 . forceY ( function ( m ) {
2019-05-13 12:12:54 +00:00
// if(panopticon.graph.distances[m['@id']] !== null )
2019-02-25 16:05:14 +00:00
// console.log(panopticon.graph.distances[m['@id']][1]);
let fy = panopticon . graph . distances [ m [ '@id' ] ] !== null ? panopticon . graph . distances [ m [ '@id' ] ] [ 1 ] * panopticon . graph . nodeSize * 3 : 0
// console.log('fx', m['@id'], panopticon.graph.distances[m['@id']], fx);
return fy ;
} ) . strength ( 50 ) )
2019-02-25 11:18:45 +00:00
// .force( "forceY", d3.forceY(m => panopticon.graph.distances[m['@id']] !== null ? 0 : panopticon.graph.nodeSize * 3 ).strength(30))
2019-01-23 14:26:44 +00:00
;
2019-02-18 22:13:42 +00:00
this . simulation . velocityDecay ( . 99 ) ;
2019-01-23 14:26:44 +00:00
// Update existing nodes
let node = this . nodesG
2019-01-24 13:27:04 +00:00
. selectAll ( "g" )
. data ( this . messages , n => n [ '@id' ] )
;
2019-05-13 12:12:54 +00:00
2019-01-23 14:26:44 +00:00
// Update existing nodes
let newNode = node . enter ( ) ;
2019-01-24 13:27:04 +00:00
let newNodeG = newNode . append ( "g" )
. attr ( 'id' , d => d [ '@id' ] )
. on ( 'click' , function ( d ) {
this . clickMsg ( d ) ;
} . bind ( this ) )
;
let circle = newNodeG . append ( "circle" )
. attr ( 'r' , this . nodeSize )
// .text(d => d.id)
;
2019-05-13 12:12:54 +00:00
2019-01-24 13:27:04 +00:00
let textId = newNodeG . append ( "text" ) . attr ( 'class' , 'msg_id' ) ;
let textContent = newNodeG . append ( "text" ) . attr ( 'class' , 'msg_txt' ) ;
2019-01-24 14:27:22 +00:00
let statusIcon = newNodeG . append ( "image" )
. attr ( 'class' , 'status_icon' )
. attr ( 'x' , '-10' )
. attr ( 'y' , '10' )
. attr ( 'width' , '20' )
. attr ( 'height' , '20' )
;
2019-01-24 13:27:04 +00:00
// remove
node . exit ( ) . remove ( ) ;
node = node . merge ( newNodeG ) ;
2019-05-13 12:12:54 +00:00
2019-01-24 13:27:04 +00:00
// for all existing nodes:
node . attr ( 'class' , msg => {
let classes = [ ] ;
if ( this . selectedMsg == msg ) classes . push ( 'selectedMsg' ) ;
if ( msg [ 'start' ] == true ) classes . push ( 'startMsg' ) ;
2019-04-26 09:14:49 +00:00
if ( msg . hasOwnProperty ( 'chapterStart' ) && msg [ 'chapterStart' ] == true ) classes . push ( 'chapterStartMsg' ) ;
2019-01-24 13:27:04 +00:00
if ( this . getDirectionsFrom ( msg ) . length < 1 ) {
classes . push ( 'endMsg' ) ;
if ( this . getDirectionsTo ( msg ) . length < 1 ) classes . push ( 'orphanedMsg' ) ;
}
2019-01-23 14:26:44 +00:00
2019-01-24 13:27:04 +00:00
return classes . join ( ' ' ) ;
} )
2019-05-13 12:12:54 +00:00
2019-02-25 11:18:45 +00:00
. on ( ".drag" , null )
2019-05-13 12:12:54 +00:00
. call (
2019-02-25 11:18:45 +00:00
d3 . drag ( this . simulation )
. on ( "start" , function ( d ) {
if ( ! d3 . event . active ) panopticon . graph . simulation . alphaTarget ( 0.3 ) . restart ( ) ;
d . fx = d . x ;
d . fy = d . y ;
} )
. on ( 'drag' , function ( d ) {
d . fx = d3 . event . x ;
d . fy = d3 . event . y ;
} )
. on ( "end" , function ( d ) {
if ( ! d3 . event . active ) panopticon . graph . simulation . alphaTarget ( 0 ) ;
d . fx = null ;
d . fy = null ;
} )
// .container(document.getElementById('container'))
2019-05-13 12:12:54 +00:00
2019-02-25 11:18:45 +00:00
) ;
2019-05-13 12:12:54 +00:00
2019-02-28 17:58:03 +00:00
node . select ( 'circle' ) . attr ( 'style' , ( d ) => 'fill: ' + ( d . hasOwnProperty ( 'color' ) ? d [ 'color' ] : '#77618e' ) ) ;
2019-01-24 13:27:04 +00:00
let link = this . linkG
. selectAll ( "line" )
. data ( this . directions )
;
let newLink = link . enter ( )
. append ( "line" )
;
2019-05-13 12:12:54 +00:00
2019-01-24 13:27:04 +00:00
//remove
link . exit ( ) . remove ( ) ;
link = link . merge ( newLink ) ;
link . attr ( 'class' , l => { return ` link ` + ( l [ 'conditions' ] . length == 0 ? "link--noconditions" : "link--withconditions" ) ; } ) ;
link . attr ( 'id' , ( l ) => l [ '@id' ] ) ;
let formatText = ( t ) => {
if ( t . length > this . maxChars ) {
return t . substr ( 0 , this . maxChars - 3 ) + '...' ;
} else {
return t ;
}
} ;
node . selectAll ( "text.msg_id" ) . text ( d => d [ '@id' ] ) ;
2019-11-30 17:04:12 +00:00
node . selectAll ( "text.msg_txt" ) . text ( d => formatText ( ` ${ this . getLabel ( d ) } ` ) ) ;
2019-01-24 14:27:22 +00:00
node . selectAll ( "image.status_icon" ) . attr ( 'xlink:href' , d => d [ 'audio' ] ? '' : '/images/music-broken.svg' ) ;
2019-01-24 13:27:04 +00:00
// 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" , ( ) => {
2019-01-23 14:26:44 +00:00
link
2019-01-24 13:27:04 +00:00
. each ( function ( d ) {
2019-01-23 14:26:44 +00:00
let sourceX , targetX , midX , dx , dy , angle ;
// This mess makes the arrows exactly perfect.
// thanks to http://bl.ocks.org/curran/9b73eb564c1c8a3d8f3ab207de364bf4
2019-01-24 13:27:04 +00:00
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 ;
2019-01-23 14:26:44 +00:00
} else {
2019-01-24 13:27:04 +00:00
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 ;
2019-01-23 14:26:44 +00:00
}
dx = targetX - sourceX ;
dy = d . target . y - d . source . y ;
2019-01-24 13:27:04 +00:00
angle = Math . atan2 ( dx , dy ) ;
2019-01-23 14:26:44 +00:00
// Compute the line endpoint such that the arrow
// is touching the edge of the node rectangle perfectly.
2019-01-24 13:27:04 +00:00
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);
} ) ;
// this.simulation.alpha(1);
// this.simulation.restart();
2019-11-11 17:25:04 +00:00
console . time ( 'build:simulate' )
2019-02-25 11:18:45 +00:00
for ( let i = 0 , n = Math . ceil ( Math . log ( this . simulation . alphaMin ( ) ) / Math . log ( 1 - this . simulation . alphaDecay ( ) ) ) ; i < n ; ++ i ) {
this . simulation . tick ( ) ;
2019-01-23 14:26:44 +00:00
}
2019-11-11 17:25:04 +00:00
console . timeEnd ( 'build:simulate' )
2019-01-24 13:27:04 +00:00
return this . svg . node ( ) ;
}
2019-05-13 12:12:54 +00:00
2019-02-18 21:33:31 +00:00
calculateDistancesFromStart ( ) {
2019-03-25 16:45:07 +00:00
console . time ( 'calculateDistancesFromStart' ) ;
2019-02-18 21:33:31 +00:00
let starts = this . messages . filter ( m => m . hasOwnProperty ( 'start' ) && m [ 'start' ] == true ) ;
if ( starts . length < 1 ) {
console . error ( "No start set" ) ;
return ;
}
2019-05-13 12:12:54 +00:00
2019-02-18 21:33:31 +00:00
//initiate distances
let distances = { } ;
for ( let msg of this . messages ) {
2019-02-25 11:18:45 +00:00
// distances[msg['@id']] = msg === startMsg ? 0 : null;
distances [ msg [ '@id' ] ] = null ;
2019-02-18 21:33:31 +00:00
}
2019-05-13 12:12:54 +00:00
2019-02-18 22:13:42 +00:00
let targetsPerMsg = { } ;
let sourcesPerMsg = { } ;
2019-02-25 14:56:59 +00:00
// console.log("dir", this.directions);
2019-02-18 21:33:31 +00:00
for ( let direction of this . directions ) {
let from = typeof direction [ 'source' ] == "string" ? direction [ 'source' ] : direction [ 'source' ] [ '@id' ] ;
let to = typeof direction [ 'target' ] == "string" ? direction [ 'target' ] : direction [ 'target' ] [ '@id' ] ;
2019-05-13 12:12:54 +00:00
2019-02-18 22:13:42 +00:00
if ( ! targetsPerMsg . hasOwnProperty ( from ) ) {
targetsPerMsg [ from ] = [ ] ;
2019-02-18 21:33:31 +00:00
}
2019-02-18 22:13:42 +00:00
targetsPerMsg [ from ] . push ( to ) ;
2019-05-13 12:12:54 +00:00
2019-02-18 22:13:42 +00:00
if ( ! sourcesPerMsg . hasOwnProperty ( to ) ) {
sourcesPerMsg [ to ] = [ ] ;
}
sourcesPerMsg [ to ] . push ( from ) ;
2019-02-18 21:33:31 +00:00
}
2019-02-25 16:05:14 +00:00
let traverseMsg = function ( msgId , depth , goingDown , yPos ) {
2019-02-18 22:13:42 +00:00
let msgsPerMsg = goingDown ? targetsPerMsg : sourcesPerMsg ;
2019-02-25 14:56:59 +00:00
// console.log(goingDown, msgId, depth);
2019-02-18 22:13:42 +00:00
if ( ! msgsPerMsg . hasOwnProperty ( msgId ) ) {
2019-02-18 21:33:31 +00:00
// end of trail
2019-02-25 16:05:14 +00:00
return yPos ;
2019-02-18 21:33:31 +00:00
}
2019-05-13 12:12:54 +00:00
2019-02-25 17:07:25 +00:00
let i = 0 , y = 0 ;
2019-02-18 22:13:42 +00:00
for ( let childMsgId of msgsPerMsg [ msgId ] ) {
2019-04-07 14:38:15 +00:00
if ( distances [ childMsgId ] !== null ) {
continue ;
}
2019-02-25 16:05:14 +00:00
if ( distances [ childMsgId ] === null || ( goingDown && distances [ childMsgId ] [ 0 ] > depth ) ) {
2019-05-13 12:12:54 +00:00
2019-02-25 17:07:25 +00:00
if ( distances [ childMsgId ] === null ) {
if ( i > 0 ) {
yPos ++ ;
}
i ++ ;
2019-05-13 12:12:54 +00:00
2019-11-11 17:25:04 +00:00
// console.log('set for id', childMsgId, goingDown, depth, yPos);
2019-02-25 17:07:25 +00:00
distances [ childMsgId ] = [ depth , yPos ] ;
}
else {
y ++ ;
}
2019-02-18 22:13:42 +00:00
// console.log(goingDown, childMsgId, depth);
2019-02-25 16:05:14 +00:00
yPos = traverseMsg ( childMsgId , goingDown ? ( depth + 1 ) : ( depth - 1 ) , goingDown , yPos ) ;
2019-02-18 22:13:42 +00:00
}
2019-02-25 16:05:14 +00:00
else if ( ! goingDown && distances [ childMsgId ] [ 0 ] < depth ) {
2019-02-25 17:07:25 +00:00
if ( childMsgId == 'en-njsgkr4az' ) {
console . log ( 'set for id' , childMsgId , goingDown ) ;
}
if ( distances [ childMsgId ] === null ) {
distances [ childMsgId ] = [ depth , yPos ] ;
}
2019-05-13 12:12:54 +00:00
2019-02-18 22:13:42 +00:00
// console.log('a', depth);
2019-02-25 16:05:14 +00:00
yPos = traverseMsg ( childMsgId , depth - 1 , goingDown , yPos ) ;
2019-02-18 21:33:31 +00:00
} else {
// apparently, there is a loop. Don't traverse it.
}
2019-02-25 16:05:14 +00:00
2019-02-18 21:33:31 +00:00
}
2019-05-13 12:12:54 +00:00
2019-02-25 17:07:25 +00:00
// if( i == 0 && y == 1) {
// // we reached an item that branches back into the tree
// return yPos -1;
// }
// console.log('yPos',msgId,yPos);
2019-02-25 16:05:14 +00:00
return yPos ;
2019-02-18 21:33:31 +00:00
}
2019-05-13 12:12:54 +00:00
2019-02-25 16:05:14 +00:00
let yPos = 0 ;
2019-03-25 16:45:07 +00:00
console . time ( 'step1' ) ;
2019-02-25 11:18:45 +00:00
for ( let startMsg of starts ) {
2019-11-11 17:25:04 +00:00
// console.time('start: '+startMsg['@id']);
2019-02-25 11:18:45 +00:00
if ( distances [ startMsg [ '@id' ] ] === null ) {
2019-02-25 16:05:14 +00:00
distances [ startMsg [ '@id' ] ] = [ 0 , yPos ] ;
2019-02-25 11:18:45 +00:00
}
2019-02-25 16:05:14 +00:00
yPos = traverseMsg ( startMsg [ '@id' ] , 1 , true , yPos ) ;
yPos += 1 ;
2019-11-11 17:25:04 +00:00
// console.timeEnd('start: '+startMsg['@id']);
2019-02-25 11:18:45 +00:00
}
2019-03-25 16:45:07 +00:00
console . timeEnd ( 'step1' ) ;
console . time ( 'step2' ) ;
2019-02-18 22:13:42 +00:00
// now we have the formal tree, lets try to polish the rest:
for ( let msgId in distances ) {
2019-11-11 17:25:04 +00:00
// console.time('polish: '+ msgId);
2019-02-18 22:13:42 +00:00
if ( distances [ msgId ] === null ) {
continue ;
}
// let's see if there are parent nodes that are not in the distances array
// traverse up and see whether we encounter anything new
2019-02-25 16:05:14 +00:00
traverseMsg ( msgId , distances [ msgId ] [ 0 ] - 1 , false , distances [ msgId ] [ 1 ] )
2019-11-11 17:25:04 +00:00
// console.timeEnd('polish: '+ msgId);
2019-02-18 22:13:42 +00:00
}
2019-03-25 16:45:07 +00:00
console . timeEnd ( 'step2' ) ;
2019-05-13 12:12:54 +00:00
2019-02-18 22:13:42 +00:00
// let additionalsDepth = 0;
//// now the secondary strands:
// for(let msgId in distances) {
// if(distances[msgId] !== null || sourcesPerMsg.hasOwnProperty(msgId)) {
// // it is already calculated, or it has a parent node (which we should traverse instead)
// continue;
// }
// distances[msgId] = additionalsDepth;
// traverseMsg(msgId, additionalsDepth+1, true);
2019-05-13 12:12:54 +00:00
//
2019-02-18 22:13:42 +00:00
// }
2019-03-25 16:45:07 +00:00
console . timeEnd ( "calculateDistancesFromStart" ) ;
2019-02-18 21:33:31 +00:00
return distances ;
}
2019-01-23 14:26:44 +00:00
}
2019-03-27 12:36:09 +00:00
//
2019-05-13 12:12:54 +00:00
//