class BnDetail { constructor(drawOptions = {}) { const DEFAULT_OPTS = { drawFrame:false, drawChangeBar:true } this.drawOptions = Object.assign({}, drawOptions) Object.keys(DEFAULT_OPTS).forEach(key => { if (this.drawOptions[key] == undefined) this.drawOptions[key] = DEFAULT_OPTS[key] }) } make(root) { this.root = root || n('div.bnDetail', n('script', {src: 'https://code.jquery.com/jquery-3.4.1.slim.min.js'}), n('script', {src: sitePath('/_/js/arrows.js')}), n('script', {src: sitePath('/_/js/bn.js')}), n('div.controls', // n('button.downloadpng', 'Download As PNG'), // n('button.savesnapshot', 'Save Snapshot'), n('button.downloadsvg', 'Download As SVG'), // n('button.downloadbase64', 'Download As Base64'), // n('label', // 'Scale Image', // n('select.scaleimage', // n('option', "1x", {value:1}), // n('option', "2x", {value:2}), // n('option', "3x", {value:3}), // ), // ), // n('button.save', 'Save to My Library'), n('label', 'Influence Frame', n('input.influence-as-frame', {type:"checkbox"}) ), n('label', 'Only Show Target Node', n('input.influence-target-node', {type:"checkbox"}) ), n('button.frozen-mode', 'Frozen Mode'), n('button.publish', 'Publish to Public Library'), n('span.gap'), n('span.scenarioControls', n('span', 'Scenario:'), this.scenarioBox = n('select.scenario', n('option.none', 'None'), ), n('button.saveScenario', 'Save'), n('button.removeScenario', 'Remove'), n('button.renameScenario', 'Rename'), ), ), n('div', {class: 'bigContainer'}, n('div.bnView', ), n('div.influenceContainer', {id:"verbalBox", class: 'influenceContainer'}, n('p', 'Summary: What all the findings contribute', {class: 'verbalTitle'}), n('p', { class: 'introSentence'}), n('p', { class: 'influenceList'}), n('p', { class: 'overallSentence'}) ), ), n('div.infoWindows', n("div", {class:"evidence-scale"}, n("div", {class:"evidence-scale-header"}, "Contribution Scale"), // n("div", "Colour scale showing the influence of evidence on the target."), n("table", {class:"influencelegend"} , n("tr", n("td", "greatly increases (31-100%)", {class:`influence-idx0`})), n("tr", n("td", "moderately increases (16-30%)", {class:`influence-idx1`})), n("tr", n("td", "slightly increases (1-15%)", {class:`influence-idx2`})), n("tr", n("td", "doesn't changes (0%)", {class:`influence-idx3`})), n("tr", n("td", "slightly reduces (1-15%)", {class:`influence-idx4`})), n("tr", n("td", "moderately reduces (16-30%)", {class:`influence-idx5`})), n("tr", n("td", "greatly reduces (31-100%)", {class:`influence-idx6`})), ), n("div", {style:"text-align:center; padding:3px;"}, "probability of"), n("div", n("div", {class:"target"}, "target state")) ), /* n('div.help', n('h2', 'Help'), n('div.tip.infoContent', n('p', `Hover over a node and click 'E' to set an effect, which will display the causal information with all other nodes below.`), n('p', `To see the effect of a `, n('em', 'combined'), ` set of causes, click 'C' (Cause) on one or more other nodes, which will display an information window below.`), n('p', `To focus on the causal information for just specific states, click the checkbox next to the state name. To select multiple such states, hold down 'Shift'.`), ), ), */ ), ); this.titleEl = this.root.querySelector('.title'); this.bnView = this.root.querySelector('.bnView'); } toHtml() { return this.root.outerHTML; } $handleUpdate(m) { let barMax = 100; //px if (m.title) { /// XXX Hack: Find a way of getting the page component console.log('m.title:', m.title) if (document.body) { q('h1 .text').innerHTML = m.title; } else { this.titleEl.textContent = m.title; } } if (m.model) { // console.log('m.model:', m.model) this.bnView.querySelectorAll('.node').forEach(n => n.remove()); let nodes = m.model.map(node => n('div.node', {dataName: node.name}, /* Use transform, so that alignment is the same as what GeNIe thinks it is (i.e. based on centre point) */ // {style: `left: ${node.pos[0]}px; top: ${node.pos[1]}px; transform:translate(-50%,-50%)`}, /* MK: The example files don't have an offset */ {style: `left: ${node.pos[0]}px; top: ${node.pos[1]}px; `}, n('div.controls', n('a.setCause', {href: 'javascript:void(0)'}, 'C'), n('a.setEffect', {href: 'javascript:void(0)'}, 'E'), //n('a.menu', {href: 'javascript:void(0)'}, '\u22EF'), ), n('h3', node.name), n('div.states', node.states.map((s,i) => n('div.state', {dataIndex: i}, // n('span.target', n('input', {type: 'checkbox'})), n('div.cellProbability', n('div.propWrapper', // This show the checkbox n('div.hiddencheckboxcontainer.target', n('input', {type: 'checkbox'},{class:`hiddencheckbox`}) ), n('span.label', s), n('span.prob', Math.trunc(Math.round(node.beliefs[i]*100).toFixed(1))), ) ), n('div.cellBar', n('div.barParent', n('span.bar', {style: `width: ${node.beliefs[i]*100}%`}), n('span.barchange') ) ), )) // states.map ) )); // model.map this.bnView.append(...nodes); this.bnView.append(n('script', ` bn.model = ${JSON.stringify(m.model)}; bn.drawArcs(); `)); } if (m.nodeBeliefs) { for (let [nodeName,beliefs] of Object.entries(m.nodeBeliefs)) { let nodeEl = this.bnView.querySelector(`div.node[data-name=${nodeName}]`); nodeEl.querySelectorAll('.state').forEach((state,i) => { state.querySelector('.prob').textContent = Math.trunc(Math.round(beliefs[i]*100).toFixed(1)); state.querySelector('.bar').style.width = (beliefs[i]*barMax)+'%'; }); } console.log('m.nodeBeliefs:', m.nodeBeliefs) } /** != null is false only if value is null or undefined **/ if (m.scenariosEnabled != null) { this.root.querySelector('.scenarioControls').style.display = m.scenariosEnabled ? 'inline' : 'none'; } if (m.scenarios) { for (let scenario of m.scenarios) { this.scenarioBox.append(n('option', scenario.name, {value: scenario.id, dataScenario: JSON.stringify(scenario)})); } } if (m.temporary != null) { this.root.querySelector('button.publish').style.display = m.temporary ? 'none' : 'inline'; if (!m.temporary) { this.root.querySelector('button.save').textContent = 'Save'; } } if (m.visibility != null) { this.root.querySelector('button.publish').style.display = m.visibility == 'public' ? 'none' : 'inline'; } if (m.updateFrameMode != null) { // clear all influences first, as we set them anyway Array.from(this.bnView.querySelectorAll(`span.barchange`)).forEach(node => { Array.from(node.classList).forEach(classname => { if (classname.indexOf("influence-") == 0) { node.classList.remove(classname); let boxidx = classname.indexOf('-box') if (this.drawFrame) { node.classList.add('frame'); if (boxidx < 0) classname = classname + '-box' } else { node.classList.remove('frame'); if (boxidx > 0) classname = classname.substring(0, boxidx) } node.classList.add(classname); } }) }) } // else { // // clear all influences first, as we set them anyway // Array.from(this.bnView.querySelectorAll(`span.barchange`)).forEach(node => { // Array.from(node.classList).forEach(classname => { // if (classname.indexOf("influence-") == 0) { // node.classList.remove(classname); // } // }) // node.classList.remove('frame'); // }) // } if (m.updateShowBarChange != null) { let evidencenodes = this.bnView.querySelectorAll(`div.node.hasEvidence`) Array.from(evidencenodes).forEach(node=> Array.from(node.querySelectorAll('span.barchange')) .forEach(e=>e.style.display = this.onlyTargetNode ? 'none' : "inline-block") ) } // Update all evidence nodes and show the influence (as a bar overlayed of the evidence state) // they have on the target node Array.from(this.bnView.querySelectorAll(`.barchange`)).forEach(node=>node.style.width = "0%") Array.from(this.bnView.querySelectorAll(`.cellProbability`)).forEach(node=>{ Array.from(node.classList).forEach(classname => { if (classname.indexOf("influence-") == 0) { node.classList.remove(classname); node.classList.remove('frame'); } }) }) // NODES if (m.influences) { console.log('________________________m.arcInfluence:________________________', m.arcInfluence) let influencesFocus = m.influencesWithoutFocusEvidence let beliefsFocus = m.beliefsWithoutFocusEvidence let asFrame = true; // console.log("updating influences"); let listTargetNodes = {} let entries = Object.entries(m.influences) // console.log('m.influences', m.influences) let verbalBox = this.root.querySelector('.influenceContainer'); if (verbal){ verbalBox.style.opacity = 1; verbalBox.style.display = 'inline-block'; } let verbalIntroSentence = this.root.querySelector('.introSentence'); let verbalListDisplay = this.root.querySelector('.influenceList'); let displayDetail = false; let verbalTitle = this.root.querySelector('.verbalTitle'); let verbalOverallSentence = this.root.querySelector('.overallSentence'); let globalTargetNodeName = ''; let globalTargetNodeState = ''; // console.log('entries.length:', entries.length) if (entries.length == 0) { verbalListDisplay.innerHTML = ''; verbalIntroSentence.innerHTML = ''; verbalBox.style.display = 'none'; globalTargetNodeName = ''; globalTargetNodeState = ''; verbalOverallSentence.innerHTML = ''; displayDetail = false; reset(m.arcInfluence, bn, this.bnView); } else { // console.log('entries:', entries) verbalListDisplay.innerHTML = ''; let arcsContribution = []; let numsEntries = entries.length; let preAnimation = numsEntries > 1; let evidenceContributions = {} entries.forEach(([evidenceNodeName, value]) => { verbalIntroSentence.innerHTML = ''; if (evidenceNodeName == 'overall') return; // console.log('this.bnView:', this.bnView) // Activate Evidence - Flash Node - Shining Node // console.log('displayDetail:', displayDetail) let focusEvidence = this.bnView.querySelector('div.node.focusEvidence'); // console.log('focusEvidence:', focusEvidence) // console.log('-----focuse Evidence----- :',focusEvidence) let focusEvidenceName = '' let focusEvidenceState = '' if (focusEvidence && !displayDetail) { focusEvidenceName = focusEvidence.getAttribute('data-name') console.log('focusEvidenceName:', focusEvidenceName) console.log('evidenceNodeName:', evidenceNodeName) console.log('influencesFocus[evidenceNodeName]:', influencesFocus[evidenceNodeName]) let focusEvidenceIndex = m.nodeBeliefs[focusEvidenceName]?.indexOf(1); let focusEvidenceStateElem = focusEvidence?.querySelector(`.state[data-index="${focusEvidenceIndex}"] .label`); focusEvidenceState = focusEvidenceStateElem ? focusEvidenceStateElem.textContent : "Unknown"; displayDetail = true; verbalTitle.innerHTML = ''; let { evidenceTense } = inferTenseFromArcInfluence(m.arcInfluence, focusEvidenceName, "Dermascare"); verbalTitle.appendChild(n('p', `Detail: How finding out ${focusEvidenceName} ${evidenceTense} `, n('span', `${focusEvidenceState}`, {style: 'font-style: italic'}), ' contributes')); } else if (focusEvidence === null){ verbalTitle.innerHTML = ''; verbalTitle.appendChild( n( 'p', 'Summary: What all the findings contribute' ) ); } let targetBeliefs = value['targetBeliefs']; let evidenceNode = this.bnView.querySelector(`div.node[data-name=${evidenceNodeName}]`) // console.log('evidenceNode:', evidenceNode) // console.log('evidenceNode:', evidenceNode) // evidenceNodeLabels.add(evidenceNode.getAttribute('data-name')) let evidenceStateIdx = m.nodeBeliefs[evidenceNodeName].indexOf(1); Object.entries(targetBeliefs).forEach(([targetNodeName, beliefs]) => { let targetNode = this.bnView.querySelector(`div.node[data-name=${targetNodeName}]`) let targetStateElem = targetNode.querySelector(".state.istarget"); let targetStateIdx = targetStateElem.dataset.index; let targetBaseModel = m.origModel.find(item => item.name == targetNodeName) listTargetNodes[targetNodeName] = {targetStateElem: targetStateElem, index: targetStateIdx, model: targetBaseModel} let baseBelief = targetBaseModel.beliefs[targetStateIdx] // let currentBelief = preAnimation // ? m.beliefsWithoutFocusEvidence[targetNodeName][targetStateIdx] // : m.nodeBeliefs[targetNodeName][targetStateIdx]; let currentBelief = m.nodeBeliefs[targetNodeName][targetStateIdx]; // calculate the relative change this evidence had on the target // and set the change color accordingly let targetStateColor = getTargetStateColor( baseBelief, currentBelief ); // console.log('targetStateColor:', targetStateColor) // let relativeBeliefChange = (m.nodeBeliefs[targetNodeName][targetStateIdx] - beliefs[targetStateIdx]) / m.nodeBeliefs[targetNodeName][targetStateIdx]; let relativeBeliefChange = m.nodeBeliefs[targetNodeName][targetStateIdx] - beliefs[targetStateIdx]; let absChange = Math.abs(relativeBeliefChange * 100); let stateElem = evidenceNode.querySelector(`div.state[data-index="${evidenceStateIdx}"]`); let stateName = stateElem.querySelector('.label').textContent; let targetStateName = targetStateElem.querySelector('.label').textContent; globalTargetNodeName = targetNodeName; globalTargetNodeState = targetStateName; // let barchangeElem = stateElem.querySelector(`span.barchange`); let cellProbabilityElem = stateElem.querySelector(`.cellProbability`); let colorClass = getColor(relativeBeliefChange); // COLOR STATE BAR EVIDENCE NODE if (preAnimation && influencesFocus[evidenceNodeName]) { let preAnimationBelief = m.beliefsWithoutFocusEvidence[targetNodeName][targetStateIdx] - baseBelief; let preAnimationColorClass = getColor(preAnimationBelief); console.log('preAnimationColorClass:', preAnimationColorClass) cellProbabilityElem.classList.add(preAnimationColorClass); } else { cellProbabilityElem.classList.add(colorClass); } evidenceContributions[evidenceNodeName] = { contribution: absChange, color: colorClass, } let findingOutSentence = buildFindingOutSentence(numsEntries, evidenceNodeName, stateName, colorClass, targetNodeName, targetStateName ,displayDetail, bn.arcInfluence, bn.activePaths); // let outputSentence = (displayDetail && (numsEntries == 1)) ? findingOutSentence + ', by direct connection.' : findingOutSentence; // console.log('outputSentence:', ) // console.log('findingOutSentence:', findingOutSentence) if (!displayDetail) { verbalListDisplay.appendChild(findingOutSentence) if (numsEntries >= 2) { verbalIntroSentence.appendChild(buildSummarySentence(numsEntries, evidenceNodeName, targetStateColor, targetNodeName, targetStateName, bn.arcInfluence)); } } // console.log('colorClass:', colorClass) // set colour and width of the barchange element of finding // if (this.drawOptions.drawChangeBar) { // barchangeElem.style.width = absChange+"%"; // barchangeElem.style.marginLeft = "-"+absChange+"%"; // // barchangeElem.style.left = `${100 - absChange}%`; // Array.from(barchangeElem.classList).forEach(classname => { // if (classname.indexOf("influence-idx") == 0) { // cellProbabilityElem.classList.remove(classname); // barchangeElem.classList.remove(classname); // barchangeElem.classList.remove(colorClass+"-box"); // barchangeElem.classList.remove("frame"); // } // }) // } // for all elements not being part of the bar set backgroundcolor // Array.from(stateElem.querySelectorAll(":scope>span:not(.barParent)")).forEach(elem=> { // Array.from(elem.classList).forEach(classname=> { // if (classname.indexOf("influence-idx") == 0) // elem.classList.remove(classname); // }); // elem.classList.add(colorClass); // }) }) // console.log('listTargetNodes:', listTargetNodes) // console.log('m.origModel:', m.origModel) // Build up the arcs for the influence for verbal part if (m.arcInfluence && m.activePaths && m.classifiedPaths) { let onlyFirstOrder = false; let activeNodes = extractActiveNodes(m.classifiedPaths, onlyFirstOrder); const sortedArcInfluence = sortArcInfluenceByDiff( m.arcInfluence, m.nodeBeliefs, evidenceNodeName ); sortedArcInfluence.forEach((arcEntry) => { let parentNodeState = getNodeState(arcEntry.parent, globalTargetNodeName, globalTargetNodeState, m, this.bnView); let childNodeState = getNodeState(arcEntry.child, globalTargetNodeName, globalTargetNodeState, m, this.bnView); if (activeNodes.has(arcEntry.child) && activeNodes.has(arcEntry.parent)) { arcsContribution.push({ from: arcEntry.parent, fromState: parentNodeState, to: arcEntry.child, toState: childNodeState, color: arcEntry.color, }); } }); } }) // color target bar every time a new evidence is added, use focus evidence is true colorTargetBar(listTargetNodes, m, preAnimation) // console.log('----------influencesFocus:', influencesFocus) // console.log('----------beliefsFocus:', beliefsFocus) // console.log('----------m.influences:', m.influences) if (preAnimation) { doPreAnimation(m, this.bnView) } // Generate detailed explaination for the focus node if (m.classifiedPaths && displayDetail) { generateDetailedExplanations(m.activePaths, arcsContribution, m.colliders, verbalListDisplay, bn.arcInfluence, m.focusEvidence); // Animation when new focus node is added if (window.animation) { // I need to show the arc contribution without the focus node here // how to show the arc contribution without the focus node? // how to show the arc contribution? if (!preAnimation) { reset(m.arcInfluence, bn, this.bnView); resetTargetBar(listTargetNodes) } const activeNodes = extractActiveNodes(m.classifiedPaths); fadeNodes(activeNodes, this.bnView); fadeAllArrows(activeNodes, m.arcInfluence) let animationOrderBN = generateAnimationOrder(m.classifiedPaths); const arcColorDict = getArcColors(m.arcInfluence, m.nodeBeliefs) for (let index = 0; index < animationOrderBN.length; index++) { setTimeout(() => { const path = animationOrderBN[index]; if (path.type == 'arrow') { const { arcParent, arcChildren } = getArcEndpoints(path); const color = arcColorDict[`${arcParent}, ${arcChildren}`]; colorArrows(arcParent, arcChildren, path.direction, color); } else if (path.type == 'node') { let nextPath = animationOrderBN[index + 1]; // temporary solution colorNodeByColorArrow(path.name, nextPath, arcColorDict, m); } else if (path.type == 'target') { colorTargetBarByFocusEvidence(m, evidenceContributions, m.focusEvidence, listTargetNodes, preAnimation); } }, (index + 1) * 850); // start index at 1 so the flashing go first } } } else { // Summary mode displayAllNodes(this.bnView) displayAllArrows(m.arcInfluence) uncolorAllArrows(m.arcInfluence) } } } } saveSnapshot() { let btnOK = n("button", "OK", {type:'button', on:{click: () => { // let snapshotNode = document.querySelector('.bnView .snapshots') let snapshots = {}; let bnView = document.querySelector('.bnView') if (bnView.dataset.snapshots != undefined) { snapshots = JSON.parse(atob(bnView.dataset.snapshots)) } let snapshotname = document.getElementById("snapshotname").value snapshots[snapshotname] = { model : bn.model, nodeBeliefs : bn.beliefs, influences: bn.influences, arcInfluence: bn.arcInfluence } let encoded = btoa(JSON.stringify(snapshots)); bnView.setAttribute("data-snapshots",encoded); ui.dismissDialog(dlg) }}}) let btnCANCEL = n("button", "Canel", {type:'button', on:{click: () => { ui.dismissDialog(dlg) }}}) let dlg = ui.popupDialog([ n('h2', "Enter Snapshot Name"), n('input', {id:"snapshotname", autofocus:""}) ], {buttons:[btnOK, btnCANCEL]}) document.getElementById("snapshotname").focus() // dlg.querySelector('.controls').append(n('button', 'Cancel', {type: 'button', on: {click: ui.dismissDialogs}})); } } class Example { /// Runs on both server and client /// (Therefore, inline event functions won't work) make(root) { this.root = root ?? n('div', // Inline style just as example. Styles should normally be in CSS files. n('style', ` .popup { display: none; position: fixed; background: white; padding: 5px; border: solid 1px #ccc; box-shadow: 1px 1px 2px #cccc; } `), n('h3.heading'), n('ul.nodeList'), n('div.popup'), ); } /// Runs on both server and client /// (Therefore, inline event functions won't work) $handleUpdate(m) { if (m.title) { this.updateHeading(m.title); } if (m.nodeList) { let nl = this.root.querySelector('.nodeList'); m.nodeList.forEach(node => nl.append(n('li', node.title, {dataDescription: node.description}))); } } /// Runs on client ONLY /// (Event functions WILL work.) setupEvents() { this.root.querySelector('.nodeList').addEventListener('mouseover', event => { let li = event.target.closest('li'); if (li) { this.showPopup(li.dataset.description, event.clientX, event.clientY); } }); this.root.querySelector('.nodeList').addEventListener('mouseout', event => { let li = event.target.closest('li'); if (li) { this.hidePopup(); } }); } /// Can run on client or server updateHeading(title) { this.root.querySelector('.heading').textContent = title; } /// Can run on client or server (but since it's a popup, usually only client) showPopup(text, x, y) { let popup = this.root.querySelector('.popup'); popup.textContent = text; popup.style.display = text ? 'block' : 'none'; popup.style.top = `${y}px`; popup.style.left = `${x}px`; } /// Can run on client or server (but since it's a popup, usually only client) hidePopup() { this.root.querySelector('.popup').style.display = 'none'; } } class CbnList { make() { this.root = n('div.mainPage', n('div.sideBar', n('div.catIntro.box', n('h2', 'What is CAT?'), n('p', `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`), n('div.controls', n('button', {href: sitePath('/what_is_cat')}, 'Read more...'), ), ), n('div.upload.box', n('h2', 'Load a Causal BN'), n('div.uploadChoice', n('div.dragAndDrop', n('div', 'Drag and Drop'), ), n('div.divider', n('span','')), n('form', {method:'post', action:'/upload', enctype:'multipart/form-data'}, n('div.chooseFile', n('div', 'Or browse:'), n('button', 'Upload...', {type:'button', onclick: `document.querySelector('input[name=uploadCbn]').click()`}), n('input', {type: 'file', name: 'uploadCbn', style: 'display:none', onchange: `this.form.submit()`}), ), ), ), n('div.uploadInfo', 'CAT can accept any files supported by GeNIe (including Netica and HUGIN files)', ), ), ), n('div.mainBns', this.myBnList = n('div.cbnList.box.myBns', n('h2', 'My Causal BNs'), this.myBnListContents = n('div.cbnListContents'), ), this.cbnList = n('div.cbnList.box', n('h2', 'Public Library of Causal BNs'), this.cbnListContents = n('div.cbnListContents'), ), ), ); } toHtml() { return this.root.outerHTML; } makeBnEntry(bn) { return n('div.cbn.box', n('a', n('h2', {href: sitePath(`bn?id=${bn.id}`)}, bn.name)), n('p.description', bn.description), n('div.fields', bn.author && n('div.field', n('label', 'Author:'), n('span', bn.author), ), bn.keywords && n('div.field', n('label', 'Keywords:'), n('span', bn.keywords), ), ), n('div.controls', n('button', 'Open', {href: sitePath(`bn?id=${bn.id}`)}), ), ); } $handleUpdate(m) { if (m.publicBns) { while (this.cbnListContents.hasChildNodes()) { this.cbnListContents.firstChild.remove(); } this.cbnListContents.append( ...m.publicBns.map(bn => this.makeBnEntry(bn)), ); } if (m.myBns) { while (this.myBnListContents.hasChildNodes()) { this.myBnListContents.firstChild.remove(); } this.myBnListContents.append( ...m.myBns.map(bn => this.makeBnEntry(bn)), ); } /*if (m.hasUser != null) { this.root.classList.add('noUser'); }*/ } } class Login { make(root, data) { if (data.step == 1) { this.root = n('div.userLogin', n('form.login.form.active', {method: 'post', action: '/login?step=2'}, n('div.field', n('label', 'Username:'), n('span', n('input', {name: 'username'})), ), n('div.field', n('label', 'Password:'), n('span', n('input', {name: 'password', type: 'password'})), ), n('div.controls', n('a', {onclick: 'changeUserLogin("create", this)'}, 'Create Account'), n('button.login', 'Login'), ), ), n('form.register.form', {method: 'post', action: '/login?step=2®ister=1'}, n('div.field', n('label', 'Username:'), n('span', n('input', {name: 'username'})), ), n('div.field', n('label', 'Email:'), n('span', n('input', {name: 'email'})), ), n('div.field', n('label', 'Password:'), n('span', n('input', {name: 'password', type: 'password'})), ), n('div.field', n('label', 'Confirm password:'), n('span', n('input', {name: 'password2', type: 'password'})), ), n('div.controls', n('a', {onclick: 'changeUserLogin("login", this)'}, 'Back to Login'), n('button.register', 'Register'), ), ), ); } else if (data.step == 2) { if (data.register) { this.root = n('div', 'User registered'); } } } } class Upload { make(root, data) { if (data.step == 1) { this.root = n('form', {method:'post', action:'/upload?step=2'}, n('div.field', n('label', 'Name:'), n('input', {type:'text', name:'name'}), ), n('div.field', n('label', 'Description:'), n('textarea', {name: 'description'}), ), n('input', {type:'hidden', name: 'key'}), n('button', {type:'submit'}, 'Next'), ); } else if (data.step == 2) { this.root = n('p', 'Uploaded!'); } else if (data.step == 'error') { this.root = n('div', n('h2', 'The BN upload encountered an error'), n('p', `It looks like our site can't (yet) handle the BN you tried to upload. At the moment, the site can only handle basic Bayesian networks, with no extensions.`), n('p.errorMessage', n('label','Our validation gave the following error: '), (data.msg ? data.msg : n('b', 'The validation process timed out.'))), n('p', `Here are some things you can try:`), n('ul', n('li', 'Keep the BN as simple as possible - include only discrete CPT nodes and nothing else'), n('li', 'Convert all continuous nodes to discrete'), n('li', 'Remove any constant nodes'), n('li', 'Remove utility nodes. Convert decision nodes to ordinary nodes'), n('li', 'Remove all comments or other floating text'), n('li', 'You may have better luck if the file is in GeNIe (.xdsl) format'), ), n('p', n('a', {href:'/'}, html`← Home`), ), ); } } $handleUpdate(data) { if (data.step == 1) { this.root.querySelector('[name=name]').setAttribute('value', data.name); this.root.querySelector('[name=description]').setAttribute('value', data.description); this.root.querySelector('[name=key]').setAttribute('value', data.key); } } } class WhatIsCat { make() { this.root = n('div', n('h2', 'What is CAT?'), n('p', `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`), ); } } class allocation {} class page {}