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
2020-02-23 19:48:16 +00:00
this . previousSearch = "" ;
this . searchStep = 0 ;
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 ( ) {
2020-02-23 19:48:16 +00:00
console . log ( d3 . event ) ;
2019-01-24 13:27:04 +00:00
c . attr ( "transform" , d3 . event . transform ) ;
2019-01-23 14:26:44 +00:00
}
2020-02-23 19:48:16 +00:00
this . zoom = d3 . zoom ( ) ;
this . svg . call ( this . 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 ( ) ; } ) ;
2020-02-23 19:48:16 +00:00
document . getElementById ( 'search' ) . addEventListener ( 'submit' , function ( e ) { e . preventDefault ( ) ; graph . searchSubmit ( ) ; } ) ;
}
searchSubmit ( ) {
let query = document . getElementById ( 'search-field' ) . value ;
if ( query . length < 1 ) {
console . log ( 'no query' ) ;
return ;
}
if ( query == this . previousSearch ) {
this . searchStep ++ ;
} else {
this . searchStep = 0 ;
}
this . previousSearch = query ;
let results = this . findMessages ( query ) ;
if ( results . length < 1 ) {
alert ( "No message found" ) ;
return ;
}
let ri = this . searchStep % results . length ;
let msg = results [ ri ] ;
console . log ( msg ) ;
this . zoom . scaleTo ( this . svg , 1 ) ;
this . zoom . translateTo ( this . svg , msg . x + 1280 / 2 , msg . y + 1024 / 2 ) ;
this . selectMsg ( msg ) ;
document . getElementById ( "search-go" ) . value = "Search (" + ( ri + 1 ) + "/" + results . length + ")" ;
}
findMessages ( query ) {
let results = [ ] ;
for ( let msg of this . messages ) {
if ( msg [ '@id' ] == query || msg [ '@id' ] . indexOf ( query ) > - 1 ) {
results . push ( msg ) ;
}
if ( msg [ 'text' ] == query || ( msg . hasOwnProperty ( 'label' ) && msg [ 'label' ] == query ) ) {
results . push ( msg ) ;
}
}
// second loop for priority
for ( let msg of this . messages ) {
if ( msg [ 'text' ] . indexOf ( query ) > - 1 || ( msg . hasOwnProperty ( 'label' ) && msg [ 'label' ] . indexOf ( query ) > - 1 ) ) {
results . push ( msg ) ;
}
}
return results ;
}
findMessage ( query , matchNr ) {
if ( typeof matchNr == 'undefined' ) {
matchNr = 0 ;
}
let results = this . findMessages ( query )
if ( results . length ) {
return results [ matchNr % results . length ] ;
}
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' ) ,
2020-02-21 22:39:50 +00:00
crel (
'label' , {
'title' : "Since Moscow: apparently the interval at which Google returns interim results differs per language, or we have anther cause of irregular results, either way, this screws up the short waitTimes that are crucial for the replyContains condition/diversion. Therefore, have a story configuration option with which we can extra delay to the timings (if non-zero)"
} ,
"ReplyContains timing delay:" ,
crel ( 'input' , {
'type' : 'number' ,
'on' : {
'change' : function ( e ) {
panopticon . graph . configuration [ 'time_extra_delay' ] = parseFloat ( e . target . value )
}
} ,
'value' : this . configuration . hasOwnProperty ( 'time_extra_delay' ) ? this . configuration . time _extra _delay : 0 ,
'step' : 0.01
} )
) , 's' , 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
//