// jshint esversion: 6 let gui = new lil.GUI(); let hideTimeout = false; let svg; let zoom = 1; let oldZoom = 1; let panZoomPanther; let foundAnimate = 1; let foundScale = 1.05; let myPanZoomTimer = null; let historytime = + new Date(); let fel1 = false; let fel2 = false; const panZoomTime = 500; const facilities = ['']; const names = []; const serviceareanames = []; const alias = []; let maxZoom = 230; const status = []; const point = {x: 0, y: 0}; const pa = document.location.search.replace('?','').split('&'); const parameters = {}; const state = document.location.search.indexOf('ps')>-1; const vlv = document.location.search.indexOf('vlv')>-1; const vlvs = []; for (let i=0; i<pa.length; i++) {const p = pa[i].split('='); parameters[p[0]] = p[1];} const machineCaseSensitive = document.location.search.indexOf('machine=')>-1? document.location.search.split('machine=')[1].split('&')[0]: 'elettra'; const machine = machineCaseSensitive.toLowerCase(); //https://puma-01.elettra.eu/rchan.php?json&valueOnly&src=srv-tango-srf-01.fcs.elettra.trieste.it:20000/f/access_control/safety/Undulator_access_state -> 5 FEL1, 6 FEL2 if (machine.indexOf('fermi')>-1) { fetch(conf.rchan+'srv-tango-srf-01:20000/f/access_control/safety/Undulator_access_state').then((response) => {return response.json();}).then((fel) => { fel1 = fel[5] == 1; fel2 = fel[6] == 1; }); } const latticeFile = document.location.href.split('?')[0].split('/').slice(0,-1).join('/')+'/'+machine+'_lattice.json'; const params = {machine: machineCaseSensitive.toLowerCase(), search: '', backgroundColor: '#333333'}; gui.title('PAnTHer - controls'); // if (navigator.userAgent.indexOf('Firefox/63')>-1) {$( "body" ).append('<button id="starter" style="align: center;height: 500px; width: 95%;background-color: #449944; font-size: 100px;" onClick="mystart()">START</button>');} function mystart() { panZoomPanther = svgPanZoom('#panther', {beforeZoom: myZoom, fit: false, contain: false}); $("#sname").on("keydown", searchText); $('#starter').hide(); } // Polyfill for parentNode.replaceChildren() if (typeof Element.prototype.replaceChildren !== 'function') { Object.defineProperty(Element.prototype, 'replaceChildren', { configurable: true, writable: true, value: function replaceChildren(...nodes) { // Remove all existing child nodes while (this.firstChild) { this.removeChild(this.firstChild); } // Append new DOM objects this.append(...nodes); } }); } function toggleMachine(machine) { document.location = document.location.href.split('?')[0] + '?machine='+machine; /*if (document.location.search.indexOf('machine=')==-1) document.location = document.location.href + (document.location.href.indexOf('?')==-1? '?': '&') + 'machine='+machine; let search = document.location.search.split('machine=')[1].split('&')[0]; document.location = document.location.href.replace('machine='+search, 'machine='+machine);*/ } function compLink(event) { console.log('compLink()', event, document.getElementById("compdb").href); window.open(document.getElementById("compdb").href, '_blank').focus(); event.stopPropagation(); return false; } function searchText(e) { console.log('searchText()', e, e.keyCode || e.which, $("#sname").val().toUpperCase().replace('.','_')); if (e.keyCode == 13) findComponent($("#sname").val().toUpperCase().replace('.','_')); // return -1; } // if (navigator.userAgent.indexOf('Firefox/63')==-1) {gui.add(params, 'machine', conf.machineList).onChange(function() {toggleMachine(params.machine);});} gui.add(params, 'machine', conf.machineList).onChange(function() {toggleMachine(params.machine);}); gui.add(params, 'search'); gui.addColor(params, 'backgroundColor').onChange(function() {toggleParam('backgroundColor');}); params.vlv = document.location.search.indexOf('vlv')>-1; gui.add(params, 'vlv').name('vlv & bst').onChange(function() {toggleParam('vlv');}); params.ps = document.location.search.indexOf('ps')>-1; gui.add(params, 'ps').onChange(function() {toggleParam('ps');}); const sstring = $('.controller.string').children().eq(1).children().eq(0); sstring.attr('id', 'sname'); sstring.attr('name', 'sname'); sstring.addClass("form-control sname"); function findComponent(name) { let servicearea = false; console.log('lattice', lattice); if (lattice.servicearea) for (let i in lattice.servicearea.sections) { for (let j in lattice.servicearea.sections[i].components) { if (name.replace('.', '_')==lattice.servicearea.sections[i].components[j].name.replace('.', '_')) {servicearea = true;} for (let k in lattice.servicearea.sections[i].components[j].embedded) { if (name==lattice.servicearea.sections[i].components[j].embedded[k]) {servicearea = true;} } } } console.trace(name, servicearea, document.location.search.indexOf('servicearea'), document.location.search.indexOf('search='+name)); // return; if ((servicearea != document.location.search.indexOf('servicearea')>-1) && document.location.search.indexOf('search='+name)==-1) { document.location = './panther2d.php?machine='+machine+'&search='+name+(servicearea? '&servicearea': ''); } if (name==null) name = document.getElementById('sname').value; for (let i=0; i<alias.length; i++) if (alias[i][1]==name) name = alias[i][0]; document.getElementById('sname').value = name; if (typeof $('#'+name)[0] == 'undefined') return; console.log(name, window.innerWidth/2, $('#'+name)[0].getCTM().e, $('#'+name)[0].getCTM().f); // panZoomPanther.zoomAtPoint(10, {x: window.innerWidth/2 - $('#'+name)[0].getCTM().e, y: window.innerHeight/2 - $('#'+name)[0].getCTM().f}) panZoomPanther.zoom(10); // leave a delay between zoom and pan setTimeout(mypan, 1200, name); } function mypan(name) { $('.label').show(); const x = window.innerWidth/2 - $('#'+name)[0].getCTM().e + panZoomPanther.getPan().x; const y = window.innerHeight/2 - $('#'+name)[0].getCTM().f + panZoomPanther.getPan().y; const m = $("svg")[0].getTransformToElement($('#'+name)[0]); panZoomPanther.pan({x: x, y: y}); setTimeout(pinhide, 100, name, $('#'+name).eq(0).attr('transform')); } function pinhide(name, transform) { console.log(foundAnimate, foundScale); foundAnimate *= foundScale; if (foundAnimate>1.5) foundScale = 0.95; if (foundAnimate<1) {foundScale = 1.05; foundAnimate = 1;} else setTimeout(pinhide, 100, name, transform); $('#'+name).eq(0).attr('transform',transform+',scale('+foundAnimate+')'); } // $(function() {$(".sname").autocomplete({source: names, close: function(event, ui) {findComponent($('#sname').val()); }, select: function(event, ui) {findComponent(ui.item.value); return false;}});}); $(function() {$(".sname").autocomplete({source: names, select: function(event, ui) {findComponent(ui.item.value); return false;}});}); function toggleParam(name) { if (name=='backgroundColor') {$('body').css('backgroundColor', params.backgroundColor); return;} const urlparam = {}; let search = document.location.search.replace('?', '').split('&'); if (search[0]=='') search.splice(0,1); const i=search.indexOf(name); if (i==-1) search.push(name); else search.splice(i,1); const res = search.join('&'); document.location = document.location.href.split('?')[0]+(res.length? '?'+res: ''); } async function subscribe() { let response = await fetch("./misc/talk.php?read"); if (response.status == 502) { await subscribe(); } else if (response.status != 200) { // An error - let's show it mylog('subscribe() ERROR ', response.statusText); // Reconnect in one second await new Promise(resolve => setTimeout(resolve, 1000)); await subscribe(); } else { // Get and show the message let message = await response.text(); mylog('subscribe()', message); highlightobjects(message); // Call subscribe() again to get the next message await subscribe(); } } // subscribe(); function mylog(...args) { if (document.location.search.indexOf('debug')>-1) $('#debug').html($('#debug').html()+JSON.stringify(args).replaceAll(':',': ').replaceAll(',',', ')+'\n'); else {console.trace.apply(null, args);} } function initIndex(lattice) { const index = []; for (let l in lattice.conf.index) { if (l=='start') continue; const cmd = "findComponent('"+lattice.conf.index[l].replace('.','_')+"')"; index.push('<button onclick="'+cmd+'">'+l+'</button>'); } $('body').append('<div style="position: absolute; left: 5px; bottom: 5px;">'+index.join(' ')+'</div>'); } function init() { fetch(latticeFile).then((response) => {return response.json();}).then((flattice) => { lattice = flattice; if (Object.keys(lattice).length>0) { for (let i in lattice) {if (i!='conf') facilities.push(i);} for (let i in lattice) { if (i == 'conf') continue; // logic XOR https://stackoverflow.com/questions/2335979/is-there-anyway-to-implement-xor-in-javascript if ((document.location.search.indexOf('servicearea')==-1) != (i=='servicearea')) initLattice(lattice[i].sections, i); else initSearch(lattice[i].sections, i); } bpmInit(facilities); if (document.location.search.indexOf('servicearea')>-1) initSearch(lattice.servicearea.sections, 'servicearea'); if (typeof blm != 'undefined') blmMenu(lattice, facilities, params); if (typeof bpmData != 'undefined') bpmMenu(lattice, facilities, params); params.gotoAdmin = function() {document.location = './admin.php';}; gui.add(params, 'gotoAdmin').name('Admin'); params.goto3D = function() {document.location = './panther.php?machine='+params.machine;}; gui.add(params, 'goto3D').name('3D'); if (lattice.conf && lattice.conf.index) initIndex(lattice); } $('.scale').attr('transform', "scale(2)"); // {$('<div><iframe style="width: 100%;height:250px;" src="../misc/gauge.html?dark&r1only=1&r=100&max=360&throttlingPeriod=50&apply=rotate&val='+0+'"></iframe></div>').insertBefore('.function');} // panZoomPanther = svgPanZoom('#panther', {beforeZoom: myZoom, fit: false, contain: false}); // https://github.com/bastienmoulia/svg-pan-zoom-rotate // if( /Android|webOS|iPhone|iPad|Mac|Macintosh|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) { if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) { panZoomPanther = svgPanZoom('#panther', { zoomEnabled: true, controlIconsEnabled: false, fit: 0, center: 1, maxZoom: maxZoom*2, customEventsHandler: eventsHandler, //beforeZoom: myZoom, }); } else { panZoomPanther = svgPanZoom('#panther', { zoomEnabled: true, zoomScaleSensitivity: 0.3, controlIconsEnabled: false, fit: false, center: false, minZoom: 0.2, maxZoom: maxZoom, beforePan: myPan, beforeZoom: myZoom, }); } if (document.location.search.indexOf('debug')>-1) { $('#tooltip').show(); $('#tooltip').css('width', '100%'); $('#tooltip').html('<textarea id="debug" style="height: 100px; width:100%;"></textarea>'); } let pan = [window.innerWidth/2, window.innerHeight/2]; if (document.location.search.indexOf('pan=')>-1) pan = document.location.search.split('pan=')[1].split('&')[0].split(','); if (document.location.search.indexOf('search=')>-1) findComponent(document.location.search.split('search=')[1].split('&')[0]); else { const zoom = document.location.search.indexOf('zoom=')>-1? document.location.search.split('zoom=')[1].split('&')[0]-0: 0.5; panZoomPanther.zoom(zoom); setTimeout(panZoomPanther.pan, 600, {x: pan[0], y: pan[1]}); } if (state) { fetch(conf.stateSrcUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => { const statSrc = data.toUpperCase().split('.').join('_').split(','); for (let j=0; j<statSrc.length; j++) { // if (statSrc[j].split('/')[2]=='POWER_SUPPLY' || statSrc[j].split('/')[2]=='INJECTION' || statSrc[j].split('/')[2]=='EXTRACTION') statSrc[j] = statSrc[j].split('/')[3].replace('PS', ''); } for (let i=0; i<status.length; i++) { if (status[i].name.indexOf('KICK')>-1) console.log('KICK state', i, status[i].name, statSrc); for (let j=0; j<statSrc.length; j++) { if (status[i].name.indexOf(statSrc[j])>-1) {status[i].statsrc = state; status[i].statindex = j;} } } // console.log('statSrc', data, statSrc, status); }); setInterval(updateStatus, 1000); } if (vlv) { for (let i in conf.bstmap) { if (i.indexOf('.')>-1) conf.bstmap[i.replace('.','_')] = conf.bstmap[i];} fetch(conf.vlvSrcUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => { const vlvSrc = data.toUpperCase().substring(14).split(','); for (let i=0; i<vlvs.length; i++) { for (let j=0; j<vlvSrc.length; j++) { const vlva = vlvSrc[j].split('/'); if (vlva.length < 4) continue; const name = vlva[3].replace('.','_'); if (vlvs[i].name.replace('.','_').indexOf(name)>-1) {vlvs[i].vlvsrc = state; vlvs[i].vlvindex = j; vlvs[i].type = 'vlv';} if (vlvSrc[j].split('/')[2].indexOf(conf.bstmap.base)>-1) { if (conf.bstmap[vlvs[i].name] && conf.bstmap[vlvs[i].name].indexOf(vlvSrc[j].split('/')[4])>-1) { vlvs[i].vlvsrc = 'bst'; vlvs[i].vlvindex = j; vlvs[i].type = 'bst'; } } } } //console.log('vlvSrc', data, vlvSrc, vlvs); }); setInterval(updateVlv, 1000); } $("#sname").on("keydown", searchText); }); } function showStatus(i, stat) { if (stat == 0 || stat == 'null' || stat == '' || stat == 'ON' || stat == 'RUNNING' || (!fel1 && $('#'+status[i].name)[0].classList[0]=='fel1') || (!fel2 && $('#'+status[i].name)[0].classList[0]=='fel2')) {$('#'+status[i].name).hide();} else {$('#'+status[i].name).show(); document.getElementById(status[i].name).style.fill = conf.stateLabelColor[stat];} // console.log(i, status[i], stat); } function clearStatus() { $('.ps').hide(); } function updateStatus() { fetch(conf.stateUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => { const statVal = data.split(';'); // console.log(conf.stateUrl, statVal, status); for (let i=0; i<status.length; i++) { if (status[i].statsrc==state) { if (status[i].statindex) showStatus(i, statVal[status[i].statindex]); // console.log(statVal, status, status[i].name, i, status[i].statindex, statVal[status[i].statindex]); } } setTimeout(clearStatus, 600); }); } function updateVlv() { fetch(conf.vlvUrl, {cache: "no-store"}).then((response) => {return response.text();}).then((data) => { const vlvVal = data.split(':')[1].split(';'); // console.log('updateVlv()', conf.vlvUrl, vlvVal, vlvs); for (let i=0; i<vlvs.length; i++) { if (vlvs[i].type=='vlv') $('#'+vlvs[i].name).css('fill', vlvVal[vlvs[i].vlvindex]=='CLOSED'? 'yellow': (vlvVal[vlvs[i].vlvindex]=='OPENED'? 'limegreen': 'grey')); if (vlvs[i].type=='bst') { if (typeof vlvVal[vlvs[i].vlvindex] != 'string') continue; const val = vlvVal[vlvs[i].vlvindex].split(','); // console.log('updateVlv(): ', i, vlvs[i], vlvs[i].type, vlvs[i].vlvindex, vlvVal[vlvs[i].vlvindex], '#'+vlvs[i].name, vlvs[i].comp, val[0]=='true'? 'limegreen': (val[1]=='true'? 'yellow': 'grey')); $('#'+vlvs[i].name).css('fill', val[0]=='true'? 'limegreen': (val[1]=='true'? 'yellow': 'grey')); } } }); } function rescale(x) {return x;} String.prototype.replaceAt = function(index, replacement) { return this.substring(0, index) + replacement + this.substring(index + replacement.length); }; function evalId(base) { if (base.indexOf('.')>-1) return base; return base.replaceAt(base.lastIndexOf('_'), '.').replace('RTBPM','BPM'); } function openTooltip(event) { if (document.location.search.indexOf('debug')==-1) { const type = this.href? this.href.baseVal.replace('#',''): (this.id.indexOf('BLM')>-1 || this.id.indexOf('BERGOZ')>-1? 'blm': '???'); mylog('openTooltip()',type, this.id, event, event.clientY); $('#tooltip').css('left', event.clientX+30); $('#tooltip').css('top', event.clientY+30); document.getElementById('tooltipFrame').src = conf.tooltipApp+'?s='+type.replace('fast', '')+'¶m='+this.id; console.log('openTooltip(event)', type.toLowerCase(), type.toLowerCase().indexOf('beamline')); document.getElementById('tooltip').style.display = 'block'; if (hideTimeout!==false) clearTimeout(hideTimeout); hideTimeout = setTimeout(hideTooltip, 120000); document.getElementById('compdb').style.display = (type.indexOf('rv')==0 || type.indexOf('rc')==0 || type.indexOf('rid')==0 || type.indexOf('rd')==0 || type.indexOf('rps')==0 || type.indexOf('plc')==0)? 'none': 'block'; const id = evalId(this.id); if (document.getElementById('compname')) document.getElementById('compname').innerHTML = id; document.getElementById('compdb').href = conf.compdb + id; document.getElementById("compdb").addEventListener("click", compLink); if (type.toLowerCase().indexOf('beamline')>-1) { document.getElementById('compdb').href = 'http://adam.elettra.trieste.it/projects/blcs/beamwatch/'; document.getElementById('compdb').innerHTML = 'search '+this.id+' in ADAM Beamwatch <span id="compname"/>'; } else if (machine=='elettra') document.getElementById('compdb').setAttribute("disabled", true); event.stopPropagation(); } else { for (let l in lattice) { if (l=='confg') continue; let servicearea = false; for (let i in lattice[l].sections) { for (let j in lattice[l].sections[i].components) { if (this.id==lattice[l].sections[i].components[j].name.replace('.', '_')) {mylog('openTooltip()', lattice[l].sections[i].components[j], lattice[l].sections[i].components[j].position-document.location.search.split('offset=')[1].split('&')[0]);} } } } } } function hideTooltip() { if (hideTimeout!==false) clearTimeout(hideTimeout); hideTimeout = false; // mylog('hideTooltip'); if (document.location.search.indexOf('debug')==-1) { document.getElementById('tooltipFrame').src = ''; document.getElementById('tooltip').style.display = 'none'; } } function transformLabel(x, y, beta, labelReverse) { if (typeof labelReverse == 'object') return "translate("+rescale(x)+" "+rescale(y)+") rotate("+(beta+labelReverse[0])+") translate("+labelReverse[1]+" "+labelReverse[2]+")"; return labelReverse==180? "translate("+rescale(x)+" "+rescale(y)+") rotate("+(beta-90)+") translate(1800 100)": "translate("+rescale(x)+" "+rescale(y)+") rotate("+(beta+90)+") translate("+(labelReverse? -250: 250)+" "+(labelReverse? 200: 300)+")"; } function appendLabel(id, labelclass, display, x, y, beta, labelReverse) { if ((beta+3600)%360 <180 && typeof labelReverse != 'object') labelReverse = [-90, -250, -100]; appendSvg("text", { id: id+'label', class: labelclass, x:0, y:0, style:"display: "+display, fill:"white", stroke:"#eeeeee","stroke-width":10, "font-family":"Arial", "font-size":200, "font-weight":"bold", "text-anchor": (labelReverse? "end": "start"), transform: transformLabel(x, y, beta, labelReverse) }, false, id); } function appendLabel2(param, labelclass, display, x, y, beta, labelReverse) { const id = param.name; const fontsize = labelclass.indexOf('bl')>-1? 800: 500; // console.log("appendLabel2()",param, labelclass, display, x, y, beta, labelReverse); if (labelclass.indexOf('bl')>-1 && typeof labelReverse != 'object') { labelReverse = [180, 17000, param.type=='beamlineUp'? -500: +800]; if ((beta+3690)%360 < 180) labelReverse = [0, -14000, param.type=='beamlineUp'? 1000: -300]; } else if ((beta+3600)%360 < 180 && typeof labelReverse != 'object') labelReverse = [-90, -250, -100]; appendSvg("text", { id: id+'label', class: labelclass, x:0, y:0, style:"display: block", fill:"red", stroke:"pink","stroke-width":10, "font-family":"Arial", "font-size":fontsize, "font-weight":"bold", "text-anchor": (labelReverse? "end": "start"), transform: transformLabel(x, y, beta, labelReverse) }, false, id); } function appendSvg(tagName, attrib, onClickCall=false, text=false, myclass=false) { const elem = document.createElementNS("http://www.w3.org/2000/svg", tagName); if (onClickCall) elem.addEventListener("click", onClickCall, false); if (text) { const textNode = document.createTextNode(text); elem.appendChild(textNode); } const jelem = $(elem); if (myclass) {/*mylog(elem, jelem, myclass);*/ elem.classList.add(myclass);} for (let i in attrib) { jelem.attr(i, attrib[i]); } $("svg").append(jelem); } function appendSearch(component, facility) { // mylog('appendSearch()',component, facility); if (component) { const comp = component.type.replace('booster', ''); if (comp && comp!=' ' && $('#'+comp)[0]) { const id = extractId(component.name); names.push(id[0]); if (facility=='servicearea') serviceareanames.push(id[0]); if (component.embedded) { for (let j=0; j<component.embedded.length; j++) {names.push(component.embedded[j]); alias.push([id[0],component.embedded[j]]);} } } } } function appendComponent(components, x0, y0, x1, y1, facility) { const dx = x1 - x0; const dy = y1 - y0; const d = Math.sqrt(dx*dx + dy*dy); const beta = 180*Math.atan2(y0-y1, x0-x1)/Math.PI; if (components) for (let i=0; i<components.length; i++) { const comp = components[i].type.replace('booster', ''); let x = x0+components[i].position/d*dx; let y = y0+components[i].position/d*dy; if (components[i].offset2d) {x += components[i].offset2d[0]; y += components[i].offset2d[1];} if (typeof blm != 'undefined' && comp=='blm') { const name = components[i].name.replace('BPM','BLM'); // mylog('appendComponent(), ', name, blm.obj); blm.obj.push(name); blm.dir.push(beta); appendSvg("rect", {id:name, name:name, x:0, y:0, width:40, height:40, rx:20, ry:20, transform:"translate("+rescale(x)+" "+rescale(y)+") rotate("+(name.indexOf('_L')>-1? beta + 180: beta)+")"}, openTooltip, false, "blm"); } if (typeof bpmData != 'undefined' && comp=='bpm') { // mylog('appendComponent()',components[i], x0, y0, x1, y1, beta, facility, 180*Math.atan2(y0-y1, x0-x1)/Math.PI); bpmData[facility].obj.push(components[i].name); bpmData[facility].dir.push(beta); bpmData[facility].pos.push([rescale(x), rescale(y)]); } if (typeof bpmData != 'undefined' && comp=='corrector') { corr[facility].obj.push(components[i].name); corr[facility].dir.push(beta); corr[facility].pos.push([x, y]); } if ($('#'+comp)[0]) { // mylog('components['+i+']',components[i]); const id = extractId(components[i].name); const section = components[i].name.indexOf('_')>-1? components[i].name.split('_')[1].split('.')[0]+' ': ''; const offset = components[i].type == 'beamlineUp'? "-2100 -3500": "0 -200"; const transform = "translate("+rescale(x)+" "+rescale(y)+") rotate("+(beta+180)+") translate("+offset+")"; appendSvg("use", {href:"#"+comp, id: id[0], name:components[i].name, class: comp+' '+section+facility, style:"cursor: pointer", transform:transform}, openTooltip); if (components[i].type == 'label' || components[i].type == 'beamlineDown' || components[i].type == 'beamlineUp') appendLabel2(components[i], section+facility, 'none', x, y, beta, components[i].labelReverse); else appendLabel(components[i].name, comp+' label '+section+facility, 'none', x, y, beta, components[i].labelReverse); names.push(id[0]); if (components[i].embedded) { for (let j=0; j<components[i].embedded.length; j++) {names.push(components[i].embedded[j]); alias.push([id[0],components[i].embedded[j]]);} } if (id[1]) {names.push(id[1]); alias.push(id);} if (state) { if (components[i].ps) { for (let pi=0; pi<components[i].ps.length; pi++) { const name = components[i].ps[pi].replace('PS','').replace('.','_') + '_status'; status.push({name: name}); appendSvg('circle', {id: name, class: facility+' ps', style:"display: none", cx: 150+pi*100, cy: 200, transform:transform, r: 80, stroke: 10, strokeColor: 'blue', fill: 'gray'}, false, false, 'status'); } } else { status.push({name: id[0]+'_status'}); appendSvg('circle', {id: id[0]+'_status', class: facility+' ps', style:"display: none", cx: 200, cy: 200, transform:transform, r: 80, stroke: 10, strokeColor: 'blue', fill: 'gray'}, false, false, 'status'); } if (components[i].name.toUpperCase().indexOf('KICK')>-1) console.log('PS: ', components[i].name, components[i].name.toUpperCase().indexOf('KICK'), status); } if (vlv && (components[i].type == 'vlv' || components[i].type == 'bst')) { // appendSvg('rect', {id: id[0]+'_disable', style:"display: none", x: x+75, y: y-90, width: 50, height: 200, stroke: 10, fill: 'black'}, false, false, 'vlvs'); vlvs.push({name: id[0], type: components[i].type}); } } } } if (!String.prototype.replaceAll) { String.prototype.replaceAll = function(search, replace) { return this.split(search).join(replace); }; } function extractId(name) { if (name.indexOf('(')>-1) { let tok = name.split('('); return [tok[0].replaceAll('.','_').replaceAll(' ','_').replace(/_+$/, ''), tok[1].replaceAll('.','_').replaceAll(' ','_').replaceAll(')','').replace(/_+$/, '')]; } // .replace(/_+$/, '') => https://stackoverflow.com/questions/8141718/how-to-trim-specific-characters-from-the-end-of-the-javascript-string return [name.replaceAll('.','_').replaceAll(' ','_').replace(/_+$/, ''), false]; } function highlightobjects(objclass) { $("use").css('opacity',0.3); $("text").css('opacity',0.3); $("."+objclass).css('opacity',1); } function magnifyobjects(objclass) { $("text").css('opacity',0.15); $("text."+objclass).css('opacity',1); $('.scale').attr('transform', "scale(1) translate(90,100)"); let x = -40; let y = -40; let s = 3; if (objclass=="vlv") {x = -50; y = -50; s = 5;} $('.'+objclass+'scale').attr('transform', "scale("+s+") translate("+x+","+y+")"); } function initSearch(sections, facility) { console.log('initSearch()', sections, facility); for (i=0; i<sections.length; i++) { // if(i>1) break; if (sections[i].bending && sections[i].bending.type) { console.log('names.push()', sections[i].bending.name); names.push(sections[i].bending.name.replace('.','_')); } if (typeof sections[i].components == 'object') for (let j=0; j<sections[i].components.length; j++) appendSearch(sections[i].components[j], facility); } } function initLattice(sections, facility) { initSearch(sections, facility); if (document.location.search.indexOf('facility=')>-1 && facility!=document.location.search.split('facility=')[1].split('&')[0]) return; if (typeof bpmData != 'undefined') { bpmData[facility] = {obj: [], dir: [], pos: []}; corr[facility] = {obj: [], dir: [], pos: []}; } // vacuum chamber let d = ''; let m = true; let wall = false; for (let i=0; i<sections.length; i++) { d = d + (m? 'M ': ' L ')+rescale(sections[i].start.x)+' '+rescale(sections[i].start.z); if (i<sections.length-1 && typeof sections[i+1].chamber == 'undefined') {m = true;} else if (i<sections.length-1 && sections[i+1].chamber.type=="chamber") {m = false;} if (i<sections.length-1 && typeof sections[i+1].chamber != 'undefined' && sections[i+1].chamber.type=="wall") wall = true; } if (sections[0].chamber && sections[0].chamber.type=='chamber') d = d + ' Z'; console.log('sections', sections); if (d!='') { if (wall) appendSvg("path", {d: d, fill: "none", stroke: "#990000", "stroke-dasharray": "200 200", "stroke-width": 50, id:"wall"+facility}); else appendSvg("path", {d: d, fill: "none", stroke: "#999999", "stroke-width": 20, id:"chamber"+facility}); } let j = 0; let i = sections.length - 1; let k = sections.length - 2; let alpha = 180/Math.PI*Math.atan2(sections[j].start.z-sections[i].start.z, sections[j].start.x-sections[i].start.x); for (i=0; i<sections.length; i++) { // if(i>1) break; j = (i + 1) % sections.length; k = (i + sections.length - 1) % sections.length; let beta = 180/Math.PI*Math.atan2(sections[j].start.z-sections[i].start.z, sections[j].start.x-sections[i].start.x); let gamma = (beta+alpha)/2; if (sections[k] && sections[j].start.z<sections[i].start.z && sections[i].start.z>sections[k].start.z) gamma = gamma + 180; if (alpha>beta && gamma<0) gamma = gamma + 180; if (sections[i].bending && sections[i].bending.type) { const section = sections[i].bending.name.indexOf('_')>-1? sections[i].bending.name.split('_')[1].split('.')[0]+' ': ''; appendSvg("use", {href:"#"+sections[i].bending.type.replace('dipolefermi','bending'), id:sections[i].bending.name.replace('.','_'), class: 'bending '+section+facility, style:"cursor: pointer", transform:"translate("+rescale(sections[i].start.x)+" "+rescale(sections[i].start.z)+") rotate("+gamma+") translate(-600 -200)"}, openTooltip); appendLabel(sections[i].bending.name, 'bending '+section+facility, 'block', sections[i].start.x, sections[i].start.z, gamma+180, sections[i].bending.labelReverse); // appendSvg("text", {id:sections[i].bending.name+'label', class: 'bending '+section+facility, x:0, y:0, fill:"white", stroke:"#888888","stroke-width":10, "font-family":"Arial", "font-size":300, "font-weight":"bold", transform:"translate("+rescale(sections[i].start.x)+" "+rescale(sections[i].start.z)+") rotate("+gamma+") translate(-600 -250)"}, false, sections[i].bending.name); } alpha = beta; appendComponent(sections[i].components, sections[i].start.x, sections[i].start.z, sections[j].start.x, sections[j].start.z, facility); // if (typeof bpmData != 'undefined') bpmInit(facility); } } $(document).ready(function() { $("svg").attr('height', window.innerHeight+'px'); init(); }); var eventsHandler; let myhammer; eventsHandler = { haltEventListeners: ['touchstart', 'touchend', 'touchmove', 'touchleave', 'touchcancel','tap', 'pinch','pinchstart','pinchmove'], init: function(options) { var instance = options.instance, initialScale = 1, pannedX = 0, pannedY = 0; // mylog('init', options, panZoomPanther); myhammer = Hammer(options.svgElement, { inputClass: Hammer.SUPPORT_POINTER_EVENTS ? Hammer.PointerEventInput : Hammer.TouchInput }); myhammer.get('pinch').set({enable: true}); myhammer.on('panstart panmove', function(ev){ if (ev.type === 'panstart') { pannedX = 0; pannedY = 0; } panZoomPanther.panBy({x: ev.deltaX - pannedX, y: ev.deltaY - pannedY}); pannedX = ev.deltaX; pannedY = ev.deltaY; }); myhammer.on('pinchstart pinchmove', function(ev){ if (ev.type === 'pinchstart') { initialScale = panZoomPanther.getZoom(); panZoomPanther.zoomAtPoint(initialScale * ev.scale, {x: ev.center.x, y: ev.center.y}, zoom, ev.scale); // mylog(''); mylog('panZoomPanther.zoomAtPoint('+(initialScale * ev.scale)+', {x: '+ev.center.x+', y: '+ev.center.y+'})'); } if (ev.type === 'pinchmove') { oldZoom = zoom; zoom = initialScale * ev.scale; // mylog('zoom', zoom, ev.center.x); point.x = ev.center.x; visibleX = 0 - point.x/zoom; visibleWidth = document.getElementById('panther').clientWidth/zoom; } panZoomPanther.zoomAtPoint(initialScale * ev.scale, {x: ev.center.x, y: ev.center.y}); zoom = panZoomPanther.getZoom(); if (zoom>2) $('.label').show(); else $('.label').hide(); // mylog('panZoomPanther.zoomAtPoint('+(initialScale * ev.scale)+', {x: '+ev.center.x+', y: '+ev.center.y+'})', zoom, ev.scale); }); options.svgElement.addEventListener('touchmove', function(e){e.preventDefault(); }); }, destroy: function(){ myhammer.destroy(); } }; function myPanZoomDelayed(event) { if (event) event.preventDefault(); visibleX = 0 - point.x/zoom; visibleWidth = document.getElementById('panther').clientWidth/zoom; // mylog('myPanZoomDelayed, event', event, 'point', point, 'zoom:', zoom, 'visibleX', visibleX, 'visibleWidth', visibleWidth); myPanZoomTimer = null; oldZoom = zoom; } let firstUrl = true; function setUrl(name, value) { let t = + new Date(); if (t - historytime < 100) return; historytime = t; parameters[name] = value; const pp = []; for (let i in parameters) {pp.push(i+'='+parameters[i]);} const url = document.location.origin+document.location.pathname+'?'+pp.join('&').replaceAll('=undefined', '').replace('search=','s='); // console.log('setUrl()',name, value, url); if (firstUrl) window.history.pushState({"html":'panther2d.php',"pageTitle":'PAnTHer'},"", url); else window.history.replaceState({"html":'panther2d.php',"pageTitle":'PAnTHer'},"", url); firstUrl = false; } function myPan(oldPan, newPan) { setUrl('pan', Math.round(newPan.x*100)/100+','+Math.round(newPan.y*100)/100); point.x = newPan.x; point.y = newPan.y; if (myPanZoomTimer == null && document.location.search.indexOf('&autoPanZoom')==-1) myPanZoomTimer = setTimeout(myPanZoomDelayed, panZoomTime); // mylog('myPan', p,q); } function myZoom(oldScale, newScale) { setUrl('zoom', Math.round(newScale*100)/100); zoom = newScale; if (newScale>2) $('.label').show(); else $('.label').hide(); if (myPanZoomTimer == null && document.location.search.indexOf('&autoPanZoom')==-1) myPanZoomTimer = setTimeout(myPanZoomDelayed, panZoomTime); // mylog('myZoom', zoom); } /* https://github.com/dagrejs/dagre-d3/issues/202 */ SVGElement.prototype.getTransformToElement = SVGElement.prototype.getTransformToElement || function(elem) { return elem.getScreenCTM().inverse().multiply(this.getScreenCTM()); };