SessionHistory Demo






try { // TODO let session = await SessionHistory.join("test","fghij"); console.log(session); let con = await session.connectIngest(); window.model = await session.connectModel("history"); model.addEventListener("eventAdded", (e)=>console.log("Added",e)); model.addEventListener("eventUpdated", (e)=>console.log("Updated",e)); /*model.addEventListener("eventRemoved", (e)=>console.log("Removed",e)); model.addEventListener("modelChanged", (e)=>console.log("Changed Model",e));*/ let toast = await con.startEventStream("application.toastbread"); setInterval(()=>{ toast.createInstantEvent({done:true, t: Date.now()}); }, 30000); let heavyMetal = await con.startEventStream("application.metal.heavyness"); let ev = heavyMetal.createDurationEvent({valueA: 1, valueB: 2, valueC: {nested1: true, nested2: true}}); setTimeout(()=>{ ev.update({valueB:3}); setTimeout(()=>{ ev.stop({valueC:{nested3:true}}); }, 1000); }, 1000); /* let barking = await con.startEventStream("dog.bark"); let state = await con.startEventStream("dog.posture"); console.log(toast); barking.createInstantEvent({dog: "poodle"}); barking.createInstantEvent({dog: "grand danois"}); barking.createInstantEvent({dog: "poodle"}); state.createStateEvent({position: "standing"}); setTimeout(()=>{ state.createStateEvent({position: "sitting"}); setTimeout(()=>{ state.createStateEvent({position: "lying"}); },100); },100); let ev = heavyMetal.createDurationEvent({valueA: 1, valueB: 2, valueC: {nested1: true, nested2: true}}); setTimeout(()=>{ ev.update({valueB:3}); setTimeout(()=>{ ev.stop({valueC:{nested3:true}}); }, 1000); }, 1000); let danglingEvent = heavyMetal.createDurationEvent({valueA: 1, valueB: 2, valueC: {nested1: true, nested2: true}}); danglingEvent = null;*/ document.querySelector("#startScreen").addEventListener("click", async ()=>{ let userMedia = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: true }); let screenStream = await con.startMediaStream("user.screen", userMedia, {buffalo: true}); }); document.querySelector("#startCam").addEventListener("click", async ()=>{ let userMedia = await navigator.mediaDevices.getUserMedia({ video: true, audio: true }); let camStream = await con.startMediaStream("user.cam", userMedia); }); document.querySelector("#startVoice").addEventListener("click", async ()=>{ window.userVoice = await navigator.mediaDevices.getUserMedia({ video: false, audio: true }); let camStream = await con.startMediaStream("user.cam", window.userVoice); }); function stopStream(stream) { stream.getTracks().forEach(track => { track.stop(); }); console.log("Audio stream stopped."); } document.querySelector("#stopVoice").addEventListener("click", async ()=>{ con.stopMediaStream(window.userVoice); }); } catch (ex){ console.log(ex); } // --- 1. Initialize DataSets and Timeline --- // DataSets for two-way data binding const items = new vis.DataSet(); const groups = new vis.DataSet(); // Internal set to quickly track which 'event.type' groups already exist const existingGroupTypes = new Set(); // Configuration options const options = { // Show event content on hover (Full event content is set in the data conversion function) tooltip: { followMouse: true, // Tooltip follows the mouse cursor overflowMethod: 'cap' // Keep the tooltip within the timeline boundaries }, // Set a default start/end range (optional, can be adjusted) start: Date.now() - (1000 * 60 * 2), end: Date.now() + (1000 * 60 * 1) }; // DOM element where the Timeline will be attached const container = document.getElementById('timeline-container'); // Create the Timeline const timeline = new vis.Timeline(container, items, groups, options); // --- 2. Helper Functions for Data Conversion and Group Management --- /** * Converts a raw event from the model into a vis.js Item object. * @param {object} event - The raw event object (must have id, type, and start). * @returns {object} A vis.js compatible item. */ function eventToTimelineItem(event) { // start is guaranteed (Java's System.getCurrentTimeMillis()) const item = { id: event.id, group: event.type, // Group by event.type (the "track") start: new Date(event.start), content: `Event: ${event.type}`, // 3) Shows the full content of the events when hovered title: JSON.stringify(event, null, 2).replace(/\n/g, "<br/>"), // Full JSON content in the tooltip className: event.tentative ? 'tentative' : '' }; // 'end' is not guaranteed, so only add it if present and greater than start if (event.end && event.end > event.start) { item.end = new Date(event.end); item.type = 'range'; // Display as a range/box } else { item.type = 'point'; // Display as a point } return item; } /** * Adds a new vis.js Group based on the event's type if it doesn't exist. * @param {string} eventType - The type of the event, used as the group ID and content. */ function ensureGroupExists(eventType) { if (!existingGroupTypes.has(eventType)) { groups.add({ id: eventType, content: eventType // Label for the track }); existingGroupTypes.add(eventType); } } // --- 3. Model Event Listeners for Dynamic Updates --- // The 'model' is assumed to be an existing object that emits events window.model.addEventListener("eventAdded", (event) => { // 2) Add "tracks" based on event.type when it spots new ones ensureGroupExists(event.type); // Add the new item to the vis.DataSet (Timeline updates automatically) const item = eventToTimelineItem(event); items.add(item); }); window.model.addEventListener("eventUpdated", (event) => { // Group must exist before item is added/updated ensureGroupExists(event.type); // Update the existing item in the vis.DataSet const item = eventToTimelineItem(event); items.update(item); }); window.model.addEventListener("eventRemoved", (event) => { // The event payload might just contain {id: '...'} or the full event. // The only thing needed for removal is the ID items.remove(event.id); // NOTE: Group removal logic is complex (only remove group if it has no items) // and often omitted, but it could be implemented here. }); video { background: grey; max-width: 100%; }