UNPKG

@d3x0r/sack-gui

Version:

SACK abstraction library exposed to JS to provide low level system services.

328 lines (275 loc) 10.3 kB
import {JSOX} from "/node_modules/jsox/lib/jsox.mjs" import {Popup,popups} from "/node_modules/@d3x0r/popups/popups.mjs" import {config as protocolConfig, protocol, MySystem} from "./protocol.js" // <link rel="stylesheet" href="../styles.css"> const style = document.createElement( "link" ); style.rel = "stylesheet"; //style.href = "/node_modules/@d3x0r/popups/styles.css"; style.href = "/node_modules/@d3x0r/popups/dark-styles.css"; document.head.insertBefore( style, document.head.childNodes[0] || null ); import {local} from "./local.js" import {TaskInfoEditor} from "./taskInfoForm.js" protocolConfig.local = local; protocolConfig.addTaskLog = addTaskLog; protocolConfig.insertBackLog = insertBackLog; protocol.on( "addTaskList", AddTaskList ) protocol.on( "addSystem", AddSystem ); protocol.on( "addTask", addTask ); protocol.on( "deleteTask", deleteTask ); protocol.on( "extern.task", addNewSystem ); protocol.on( "deleteSystem", deleteSystem ); protocol.connect(); class Display extends Popup { constructor() { super("Service Manager", document.body, { suffix: "-service-manager" }); if( local.login ) popups.makeButton( this.divCaption, "Add Task", ()=>{ new TaskInfoEditor( null, null ); }, {suffix:"add-task"} ); local.pageFrame = new popups.PagedFrame( this, {suffix:"-system"} ); local.pageFrame.on( "activate", (page)=>{ local.activePage = page; } ); local.firstPage = local.pageFrame.addPage( "Master"); local.pageFrame.activate( local.firstPage ); } } function delTime(date) { const len = date.getTime(); //console.log( "len:", len ); if( len > 1000 ) { if( len > 60000 ) { if( len > 3600000 ) { if( len > 3600000 * 24 ) { return (Math.floor(len/(24*3600000))).toString() + "day(s) " + (Math.floor(len/3600000)%24).toString().padStart( 2, "0" ) + ":" + (Math.floor(len/60000)%60).toString().padStart( 2, "0" ) + ":" + (Math.floor(len/1000)%60).toString().padStart( 2, "0" ) + "." + (len%1000).toString().padStart( 3, "0" ); } else return (Math.floor(len/3600000)).toString().padStart( 2, "0" ) + ":" + (Math.floor(len/60000)%60).toString().padStart( 2, "0" ) + ":" + (Math.floor(len/1000)%60).toString().padStart( 2, "0" ) + "." + Math.floor(len%1000).toString().padStart( 3, "0" ); } else { return (Math.floor(len/60000)%60).toString().padStart( 2, "0" ) + ":" + (Math.floor(len/1000)%60).toString().padStart( 2, "0" ) + "." + (len%1000).toString().padStart( 3, "0" ); } } else { return Math.floor(len/1000).toString() + "."+ (len%1000).toString().padStart( 3, "0" ); } } else { return "0."+ len.toString().padStart( 3, "0" ); } } function showLogClick(taskList,task) { if( local.logs[task.id] && local.logs[task.id].logFrame ){ local.logs[task.id].logFrame.show(); } else protocol.showLog( taskList, task ) } function deleteTask( taskId ) { console.log( "Deleting task..." ); const task = local.tasks[taskId]; if( task ) { delete local.tasks[taskId]; delete local.systemMap[taskId]; for( let t = 0; t < local.taskData.length; t++ ) { if( local.taskData[t].id === taskId ) { local.taskData.splice( t, 1 ); break; } } if( local.statusDisplay ){ local.statusDisplay.reinit(); local.statusDisplay.fill(); } } } function addTask( id, task ) { if( local.statusDisplay ){ local.statusDisplay.reinit(); local.statusDisplay.fill(); } } // object is 'local' field is 'tasks' // so the datagrid takes its data from "local.tasks" which is an array of tasks. // each task has a name, running, started, ended, and id. function AddTaskList(display, object, field) { const editing = { } const columns = [ {field:"name", name:"Name", className: "name", type:{edit:false} } , { field: "running", name:"Status" , className: "status" , type:{edit:false ,options:[ { text:"Running", value:true,className:"task-running" } , {text:"Stopped", value:false,className:"task-stopped"} , {text:"Failed", value:0,className:"task-failed"}] } } , { name:"Changed" , className: "started", type:{ toString(row) { if( row.running ) return row.started.toLocaleDateString() +" " + row.started.toLocaleTimeString() else if( row.ended ) return row.ended.toLocaleDateString() +" " + row.ended.toLocaleTimeString() else return "unknown time"; } } } , { field: null, name:"Run Time", className: "runtime", type:{ toString(row) { if( row.running ) { return delTime( new Date( Date.now() - row.started.getTime() ) ); } else return delTime( new Date( Date.now() - row.ended.getTime() ) ); } } } , { name:"Show Log", className: "-log", type:{suffix:" blue", click:(task)=>showLogClick(object,task), text: "LOG ✎"} } , { name:"Stop" , className: "-stop", type:{suffix:" red", click:protocol.stopTask.bind( protocol,object), text: "STOP ▢"} } , { name:"Start" , className: "-start", type:{suffix:" green", click:protocol.startTask.bind( protocol,object), text: "PLAY ▷"} } , { name:"Restart" , className: "-restart", type:{suffix:" pumpkin", click:protocol.restartTask.bind( protocol,object), text: "RESTART ↻"} } //, { name:"Edit" , className: "edit", type:{click:protocol.editTask.bind( protocol,object), text: "Edit ✎"} } ]; if( local.login ) columns.push( { name:"Edit" , className: "-edit", type:{suffix:" purple", click: async function(task) { // Define the new action or function here if( editing[task.id] ) return; const taskInfo = await protocol.getTaskInfo(task.id) editing[task.id] = true; const editor = new TaskInfoEditor( task.id, taskInfo.task ); editor.on( "close", ()=>{ delete editing[task.id] } ); }, text: "Edit ✎"} } ); const dataGrid = new popups.DataGrid( display, object, field, {//suffix:'-browse' edit:false, columns } ); let visible = false; if( object === local ) { local.statusDisplay = dataGrid; local.refresh = refresh; } //const el = document.getElementById("your-target-element"); const observer = new IntersectionObserver((entries) => { if(entries[0].isIntersecting){ visible = true; refresh(); // el is visible } else { visible = false; // el is not visible } }); observer.observe(dataGrid.el); // Asynchronous call function refresh() { dataGrid.refresh(); for( let system of local.systems ){ system.dataGrid.refresh(); } if( visible ) { if( local.statusTimer ) clearTimeout( local.statusTimer ); local.statusTimer = setTimeout( ()=>{ local.statusTimer = 0; refresh(); }, 1000 ); } else local.statusTimer = 0; } return dataGrid; } function deleteSystem( system ) { let oldsystem = local.systems.findIndex( testsystem=>testsystem.id=system ); if( oldsystem>=0 ) { if( local.systems[oldsystem].page === local.activePage ) local.pageFrame.activate( local.firstPage ); local.systems[oldsystem].page.remove(); local.systems.splice( oldsystem, 1 ); } } function addNewSystem( system ) { AddSystem( system ); } function AddSystem( system ) { let oldsystem = local.systems.find( testsystem=>testsystem.id===system.id ); if( oldsystem ){ oldsystem.updateTasks( system.tasks ); } else { system = new MySystem( system ); local.systems.push( system ); const div = document.createElement( "div" ); div.className = "System-Container"; const page = local.pageFrame.addPage( system.system ); page.appendChild( div ); const label = document.createElement( "span" ); label.className = "span-label-system-name"; label.textContent = system.system; div.appendChild( label ); system.page = page; for( let task of system.tasks ) { local.tasks[task.id] = task; local.systemMap[task.id] = system; } //system.pageFrame = div; system.dataGrid = AddTaskList( div, system, "tasks" ); //console.log( "system datagrid became:", system.dataGrid ); } } function addTaskLog( task, log ) { const logFrame = new Popup( task.name, document.body, { enableClose:true} ); const opts = { follow: true, } const follow = popups.makeCheckbox( logFrame, opts, "follow", "Follow Log" ); const logList = document.createElement( "div" ); const logEnd = document.createElement( "span" ); logList.className = "task-log-listbox"; logFrame.appendChild( logList ); logList.appendChild( logEnd ); logEnd.textContent = "-Load More-"; const loadObserver = new IntersectionObserver((entries) => { if(entries[0].isIntersecting){ if( log.at ) { console.log( "Needs load more"); local.ws.send( JSOX.stringify( {op:"log", id:task.id, at:log.at } ) ); } else { console.log( "already have the full log should just remove this..."); } //visible = true; } else { console.log( "Load Log Hidden"); //visible = false; // el is not visible } }); loadObserver.observe(logEnd); // Asynchronous call local.logs[task.id] = { logFrame, logList, logEnd, task, log, add }; for( let lineIdx = 0; lineIdx < log.log.length; lineIdx++ ){ const line = log.log[lineIdx]; add( line ); } console.log( "Should be setting at end of scroll..."); logList.scrollTop = logList.scrollHeight; function add( line, after ) { const newspan = document.createElement( "div" ); newspan.className = "outputSpan"; newspan.textContent = line.line; if( after ) logList.insertBefore( newspan, logEnd ); else logList.appendChild( newspan ); //this.output.insertBefore( newspan, this.inputPrompt ); //if( prompt ) // this.inputPrompt = newspan; if( follow.value ) { logList.scrollTop = logList.scrollHeight; } return newspan; } } function insertBackLog( log, msg ) { console.log( "backlog insert...") let firstAdd = null; for( let lineIdx = 0; lineIdx < msg.log.length; lineIdx++ ){ const line = msg.log[lineIdx]; const newline = log.add( line, log.logEnd ); if( !firstAdd ) firstAdd = newline; } const scrollat = log.logEnd.getBoundingClientRect(); log.logEnd.remove(); if( msg.at ) log.logList.insertBefore( log.logEnd, firstAdd ); log.log.at = msg.at; log.logList.scrollTop = scrollat.top; } local.display = new Display();