"),template}};const events={preventDefaultCTXM:()=>document.addEventListener("contextmenu",e=>e.preventDefault()),sidebarClick:id=>{document.getElementById(id).addEventListener("click",e=>{console.log(e),e.target&&["free","tran","rot"].includes(e.target.id)&&(app.build={mode:e.target.id,continue:e.shiftKey},app.instruct.innerHTML="Select first node; [ESC] to cancel"),e.target&&"drive"===e.target.id&&(app.build={mode:e.target.id},app.reset(),app.instruct.innerHTML="Select a constraint to add a drive to; [ESC] to cancel"),!e.target||"addnode"!==e.target.id&&"addbasenode"!==e.target.id||(app.build={mode:e.target.id,continue:e.shiftKey},app.instruct.innerHTML="Left-click on the canvas to place a new node; [ESC] to cancel",document.body.style.cursor="crosshair")})},navbarClick:id=>{document.getElementById(id).addEventListener("click",e=>{e.target&&"newModel"===e.target.id&&app.newModel(),e.target&&e.target.id.includes("nav-example-")&&app.newModel(JSON.parse(JSON.stringify(examples[e.target.id.replace("nav-example-","")]))),e.target&&"export"===e.target.id&&app.saveToJSON(),e.target&&"import"===e.target.id&&(app.importConfirmed=!!confirm("All unsaved changes will be lost! Continue?")||(e.preventDefault(),!1)),e.target&&"dragmode"===e.target.id&&(app.dragMove=!app.dragMove,app.dragMove||app.reset()),e.target&&"model-edit"===e.target.id&&app.modelModal.show(),e.target&&"nav-purgeelement"===e.target.id&&(app.build={mode:e.target.id.replace("nav-","")},app.instruct.innerHTML="Left-click on an element to delete it and all its dependants; [ESC] to cancel"),!e.target||"nav-addnode"!==e.target.id&&"nav-addbasenode"!=e.target.id||(app.build={mode:e.target.id.replace("nav-","")},app.instruct.innerHTML="Left-click on the canvas to place a new node; [ESC] to cancel",document.body.style.cursor="crosshair"),e.target&&["nav-free","nav-tran","nav-rot","nav-spring"].includes(e.target.id)&&(app.build={mode:e.target.id.replace("nav-","")},app.instruct.innerHTML="select first node; [ESC] to cancel"),e.target&&"nav-drive"===e.target.id&&(app.build={mode:e.target.id.replace("nav-","")},app.instruct.innerHTML="Select a constraint to add a drive to; [ESC] to cancel"),e.target&&"nav-force"===e.target.id&&(app.build={mode:e.target.id.replace("nav-","")},app.instruct.innerHTML="Left-click on a node to add a force; [ESC] to cancel"),!e.target||"nav-fix"!==e.target.id&&"nav-flt"!==e.target.id||(app.build={mode:e.target.id.replace("nav-","")},app.instruct.innerHTML=`Left-click on a node to add a ${app.build.mode}-shape; [ESC] to cancel`),e.target&&"nav-addview"===e.target.id&&app.initViewModal(),e.target&&"darkmode"===e.target.id&&app.toggleDarkmode(),e.target&&"resetview"===e.target.id&&app.resetView(),e.target&&"toggleNodes"===e.target.id&&(app.model.graphics.linkage.nodes=!app.model.graphics.linkage.nodes,app.notify("render")),e.target&&"toggleConstraints"===e.target.id&&(app.model.graphics.linkage.constraints=!app.model.graphics.linkage.constraints,app.notify("render")),e.target&&"toggleNodeLabels"===e.target.id&&(app.model.graphics.labels.nodes=!app.model.graphics.labels.nodes,app.tempElm.labelState&&(app.tempElm.labelState.nodes=app.model.graphics.labels.nodes),app.notify("render")),e.target&&"toggleConstraintLabels"===e.target.id&&(app.model.graphics.labels.constraints=!app.model.graphics.labels.constraints,app.tempElm.labelState&&(app.tempElm.labelState.constraints=app.model.graphics.labels.constraints),app.notify("render")),e.target&&"toggleLoadLabels"===e.target.id&&(app.model.graphics.labels.loads=!app.model.graphics.labels.loads,app.tempElm.labelState&&(app.tempElm.labelState.loads=app.model.graphics.labels.loads),app.notify("render")),e.target&&"run"===e.target.id&&("active"===app.state?app.idle():app.run()),e.target&&"stop"===e.target.id&&app.stop(),e.target&&"reset"===e.target.id&&app.reset(),e.target&&"toggle-g"===e.target.id&&(app.model.gravity.active=!app.model.gravity.active,app.updateg())})},navbarChange:id=>{document.getElementById(id).addEventListener("change",e=>app.loadFromJSON(e.target.files,!0))},keyboardDown:()=>{document.addEventListener("keydown",e=>{console.log(`Key pressed: ${e.key}`),"true"===document.getElementById("modelModal").attributes["aria-hidden"].value&&"true"===document.getElementById("viewModal").attributes["aria-hidden"].value&&("Escape"===e.key&&app.build&&(app.resetApp(),document.body.style.cursor="default"),"e"===e.key&&app.modelModal.show(),"r"===e.key&&app.resetView(),"g"===e.key&&(app.model.gravity.active=!app.model.gravity.active,app.updateg()),"v"===e.key&&app.initViewModal(),"i"===e.key&&(app.dragMove=!app.dragMove,app.dragMove||app.reset()),"p"===e.key&&(app.build={mode:"purgeelement"},app.instruct.innerHTML="Left-click on an element to delete it and all its dependants; [ESC] to cancel"))})},ctxmClick:id=>{document.getElementById(id).addEventListener("click",e=>{console.log("ctxmClick fired");e.target&&"node-trash"===e.target.id&&(app.model.removeNode(app.model.nodeById(app.tempElm.old.id))?(app.updateg(),app.hideCtxm()):(app.instruct.innerHTML='Node has dependencies.',setTimeout(()=>{app.instruct.innerHTML=""},2400))),e.target&&"constraint-trash"===e.target.id&&(app.model.removeConstraint(app.model.constraintById(app.tempElm.old.id))?(app.removeInput(app.tempElm.old.id),app.updateg(),app.hideCtxm()):(app.instruct.innerHTML='Constraint has dependencies.',setTimeout(()=>{app.instruct.innerHTML=""},2400))),e.target&&"force-trash"===e.target.id&&(app.model.removeLoad(app.model.loadById(app.tempElm.old.id))?(app.updateg(),app.hideCtxm()):(app.instruct.innerHTML='Node has dependencies.',setTimeout(()=>{app.instruct.innerHTML=""},2400))),e.target&&"spring-trash"===e.target.id&&(app.model.removeLoad(app.model.loadById(app.tempElm.old.id))?(app.updateg(),app.hideCtxm()):(app.instruct.innerHTML='Node has dependencies.',setTimeout(()=>{app.instruct.innerHTML=""},2400))),e.target&&"ori-input"===e.target.id&&(e.target.checked&&!app.tempElm.new.ori.input?app.tempElm.new.ori.input=!0:!e.target.checked&&app.tempElm.new.ori.input&&delete app.tempElm.new.ori.input,app.tempElm.replace=!0),e.target&&"len-input"===e.target.id&&(e.target.checked&&!app.tempElm.new.len.input?app.tempElm.new.len.input=!0:!e.target.checked&&app.tempElm.new.len.input&&delete app.tempElm.new.len.input,app.tempElm.replace=!0)})},ctxmInput:id=>{document.getElementById(id).addEventListener("input",e=>{if(console.log("ctxmInput fired"),e.target&&"ori-drive-Dt"===e.target.id&&(app.tempElm.new.ori.Dt=e.target.valueAsNumber,app.tempElm.replace=!0),e.target&&"len-drive-Dt"===e.target.id&&(app.tempElm.new.len.Dt=e.target.valueAsNumber,app.tempElm.replace=!0),e.target&&"ori-drive-Dw"===e.target.id&&(app.tempElm.new.ori.Dw=e.target.valueAsNumber,app.tempElm.replace=!0),e.target&&"len-drive-Dr"===e.target.id&&(app.tempElm.new.len.Dr=e.target.valueAsNumber,app.tempElm.replace=!0),e.target&&"node-x"===e.target.id){let node=app.model.nodeById(app.tempElm.old.id);node.x0=node.x=e.target.valueAsNumber,app.updDependants(node),app.notify("render")}if(e.target&&"node-y"===e.target.id){let node=app.model.nodeById(app.tempElm.old.id);node.y0=node.y=e.target.valueAsNumber,app.updDependants(node),app.notify("render")}e.target&&"node-base"===e.target.id&&(app.model.nodeById(app.tempElm.old.id).base=!!e.target.checked,app.notify("render")),e.target&&"force-value"===e.target.id&&(app.model.loadById(app.tempElm.old.id).value=mec.from_N(e.target.valueAsNumber),app.notify("render")),e.target&&"spring-len0"===e.target.id&&(app.model.loadById(app.tempElm.old.id).len0=e.target.valueAsNumber,app.notify("render")),e.target&&"spring-k"===e.target.id&&(app.model.loadById(app.tempElm.old.id).k=mec.from_N_m(e.target.valueAsNumber),app.notify("render"))})},ctxmChange:id=>{document.getElementById(id).addEventListener("change",e=>{if(console.log("ctxmChange fired"),app.tempElm){let ctxmdirty=!1,doftypechanged=!1;e.target&&"select-p1"===e.target.id&&(app.tempElm.new.p1=e.target.value,ctxmdirty=!0,app.tempElm.replace=!0),e.target&&"select-p2"===e.target.id&&(app.tempElm.new.p2=e.target.value,ctxmdirty=!0,app.tempElm.replace=!0),e.target&&"select-ori-type"===e.target.id&&(app.tempElm.new.ori.type=e.target.value,ctxmdirty=!0,app.tempElm.replace=!0,doftypechanged||(doftypechanged=[]),doftypechanged.push("ori")),e.target&&"select-len-type"===e.target.id&&(app.tempElm.new.len.type=e.target.value,ctxmdirty=!0,app.tempElm.replace=!0,doftypechanged||(doftypechanged=[]),doftypechanged.push("len")),e.target&&"select-ori-ref"===e.target.id&&(app.tempElm.new.ori.ref=e.target.value,ctxmdirty=!0,app.tempElm.replace=!0),e.target&&"select-len-ref"===e.target.id&&(app.tempElm.new.len.ref=e.target.value,ctxmdirty=!0,app.tempElm.replace=!0),e.target&&"select-force-node"===e.target.id&&(console.log(e.target),app.model.loadById(app.tempElm.old.id).p=app.model.nodeById(e.target.value),app.notify("render"),ctxmdirty=!0),e.target&&"select-force-mode"===e.target.id&&(app.model.loadById(app.tempElm.old.id).mode=e.target.value,app.notify("render"),ctxmdirty=!0),ctxmdirty&&app.updateCtxm(app.tempElm.new,app.tempElm.type,doftypechanged)}})},modalShown:id=>{document.getElementById(id).addEventListener("shown.bs.modal",e=>{app.jsonEditor.setValue(app.model.asJSON())})},modalAccept:id=>{document.getElementById(id).addEventListener("click",e=>{let newmodel;try{newmodel=JSON.parse(app.jsonEditor.getValue())}catch(error){alert(`Your JSON code is not valid! \n\n${error}`)}newmodel&&(app.model=newmodel,app.init())})},resize:()=>{window.onresize=(e=>{let c=document.getElementById("canvas"),main=document.getElementById("main");c.width=main.clientWidth,c.height=main.clientHeight-30;let rangewidth=(c.width-350)/2;for(const drive in app.model.inputs)document.getElementById(app.model.inputs[drive]).slider.style.width=`${rangewidth}px`;app.notify("render")})},viewModalChange:id=>{document.getElementById(id).addEventListener("change",e=>{let skipUpdate=!0;e.target&&"input-view-id"===e.target.id&&app.updateTempElmNew("id",e.target.value),e.target&&"select-view-type"===e.target.id&&(app.updateTempElmNew("type",e.target.value),skipUpdate=!1),e.target&&"select-view-p"===e.target.id&&app.updateTempElmNew("p",e.target.value),e.target&&"select-view-elem"===e.target.id&&(app.updateTempElmNew("elem",e.target.value),skipUpdate=!1),e.target&&"select-view-value"===e.target.id&&app.updateTempElmNew("value",e.target.value),e.target&&"view-stroke-color"===e.target.id&&app.updateTempElmNew("stroke",e.target.value),e.target&&"view-fill-color"===e.target.id&&app.updateTempElmNew("fill",e.target.value),skipUpdate||(app.viewModal.setContent(ctxm.viewModal()),document.getElementById("view-fill-color-btn")&&(document.getElementById("view-fill-color-btn").style.backgroundColor=document.getElementById("view-fill-color").disabled?"transparent":"#e9ecef"),app.viewModal.update())})},viewModalClick:id=>{document.getElementById(id).addEventListener("click",e=>{e.target&&"view-accept"===e.target.id&&app.addViewFromModal(),e.target&&"view-fill-color-btn"===e.target.id&&app.toggleViewfill()})},viewModalHide:id=>{document.getElementById(id).addEventListener("hide.bs.modal",e=>{app.resetApp()})}},examples={crankrocker:{id:"crank-rocker",gravity:!1,nodes:[{id:"A0",x:100,y:100,base:!0},{id:"A",x:100,y:150},{id:"B",x:350,y:220},{id:"B0",x:300,y:100,base:!0},{id:"C",x:250,y:320,m:1}],constraints:[{id:"a",p1:"A0",p2:"A",len:{type:"const"}},{id:"b",p1:"A",p2:"B",len:{type:"const"}},{id:"c",p1:"B0",p2:"B",len:{type:"const"}},{id:"d",p1:"B",p2:"C",ori:{type:"ref",ref:"b"},len:{type:"const"}}],views:[{id:"view1",type:"trace",p:"C",fill:"rgba(255,235,13,.5)"},{id:"ia",type:"info",elem:"a",value:"w"}]},slidercrank:{id:"slider-crank",gravity:!1,nodes:[{id:"A0",x:100,y:100,base:!0},{id:"A",x:100,y:170},{id:"B",x:350,y:80},{id:"B0",x:500,y:80,base:!0}],constraints:[{id:"a",p1:"A0",p2:"A",len:{type:"const"},ori:{type:"drive",Dw:2*Math.PI,input:!0}},{id:"b",p1:"A",p2:"B",len:{type:"const"}},{id:"c",p1:"B0",p2:"B",ori:{type:"const"}}]},"7r":{id:"Stephenson-II",gravity:!1,nodes:[{id:"A0",x:100,y:100,base:!0},{id:"A",x:100,y:200},{id:"B1",x:350,y:200},{id:"B2",x:450,y:200},{id:"C1",x:350,y:150},{id:"C2",x:450,y:150},{id:"C0",x:400,y:100,base:!0}],constraints:[{id:"a",p1:"A0",p2:"A",len:{type:"const"}},{id:"a1",p1:"A",p2:"B1",len:{type:"const"}},{id:"a2",p1:"B1",p2:"B2",len:{type:"const"},ori:{type:"ref",ref:"a1"}},{id:"b1",p1:"B1",p2:"C2",len:{type:"const"}},{id:"b2",p1:"B2",p2:"C1",len:{type:"const"}},{id:"c1",p1:"C0",p2:"C1",len:{type:"const"}},{id:"c2",p1:"C0",p2:"C2",len:{type:"const"}},{id:"c3",p1:"C1",p2:"C2",len:{type:"const"}}]},"9r":{id:"9r",gravity:!1,nodes:[{id:"A0",x:100,y:100,base:!0},{id:"A",x:100,y:175},{id:"B",x:400,y:175},{id:"B0",x:400,y:100,base:!0},{id:"C",x:175,y:350},{id:"C0",x:250,y:400,base:!0},{id:"D",x:200,y:200},{id:"E",x:300,y:200},{id:"F",x:250,y:300}],constraints:[{id:"a",p1:"A0",p2:"A",len:{type:"const"}},{id:"b",p1:"B0",p2:"B",len:{type:"const"}},{id:"c",p1:"C0",p2:"C",len:{type:"const"}},{id:"d",p1:"A",p2:"D",len:{type:"const"}},{id:"e",p1:"B",p2:"E",len:{type:"const"}},{id:"f",p1:"C",p2:"F",len:{type:"const"}},{id:"g",p1:"D",p2:"E",len:{type:"const"}},{id:"h",p1:"E",p2:"F",len:{type:"const"}},{id:"i",p1:"F",p2:"D",len:{type:"const"}}]},basictruss:{id:"truss-basic",gravity:!1,nodes:[{id:"A",x:100,y:100,base:!0},{id:"B",x:200,y:100,base:!0},{id:"C",x:150,y:200}],constraints:[{id:"a",p1:"A",p2:"C",len:{type:"const"}},{id:"b",p1:"B",p2:"C",len:{type:"const"}}],loads:[{type:"force",id:"F",p:"C",mode:"push",value:2e3}],views:[{id:"F1",type:"info",elem:"a",value:"forceAbs"},{id:"F2",type:"info",elem:"b",value:"forceAbs"}]},truss:{id:"truss",gravity:!0,nodes:[{id:"A",x:100,y:100,base:!0},{id:"B",x:200,y:100},{id:"C",x:300,y:100},{id:"D",x:400,y:100},{id:"E",x:500,y:100,base:!0},{id:"G",x:150,y:200},{id:"H",x:250,y:200},{id:"I",x:350,y:200},{id:"J",x:450,y:200}],constraints:[{id:"a",p1:"A",p2:"B",len:{type:"const"}},{id:"b",p1:"B",p2:"C",len:{type:"const"}},{id:"c",p1:"C",p2:"D",len:{type:"const"}},{id:"d",p1:"D",p2:"E",len:{type:"const"}},{id:"e",p1:"A",p2:"G",len:{type:"const"}},{id:"f",p1:"G",p2:"B",len:{type:"const"}},{id:"g",p1:"B",p2:"H",len:{type:"const"}},{id:"h",p1:"H",p2:"C",len:{type:"const"}},{id:"i",p1:"C",p2:"I",len:{type:"const"}},{id:"j",p1:"I",p2:"D",len:{type:"const"}},{id:"k",p1:"D",p2:"J",len:{type:"const"}},{id:"l",p1:"J",p2:"E",len:{type:"const"}},{id:"m",p1:"G",p2:"H",len:{type:"const"}},{id:"n",p1:"H",p2:"I",len:{type:"const"}},{id:"o",p1:"I",p2:"J",len:{type:"const"}}],loads:[{type:"force",id:"F",p:"H",mode:"push",value:2e3,w0:-Math.PI/2}],views:[{id:"F1",type:"info",elem:"a",value:"forceAbs"},{id:"F2",type:"info",elem:"b",value:"forceAbs"},{id:"F3",type:"info",elem:"c",value:"forceAbs"},{id:"F4",type:"info",elem:"d",value:"forceAbs"},{id:"F5",type:"info",elem:"e",value:"forceAbs"},{id:"F6",type:"info",elem:"f",value:"forceAbs"},{id:"F7",type:"info",elem:"g",value:"forceAbs"},{id:"F8",type:"info",elem:"h",value:"forceAbs"},{id:"F9",type:"info",elem:"i",value:"forceAbs"},{id:"F10",type:"info",elem:"j",value:"forceAbs"},{id:"F11",type:"info",elem:"k",value:"forceAbs"},{id:"F12",type:"info",elem:"l",value:"forceAbs"},{id:"F13",type:"info",elem:"m",value:"forceAbs"},{id:"F14",type:"info",elem:"n",value:"forceAbs"},{id:"F15",type:"info",elem:"o",value:"forceAbs"}]},pendulums:{id:"chaos-pendulums",gravity:!0,nodes:[{id:"A0",x:200,y:400,base:!0},{id:"A1",x:280,y:480,m:5},{id:"A2",x:360,y:560,m:4},{id:"A3",x:440,y:640,m:2},{id:"B1",x:280,y:480,m:5},{id:"B2",x:360,y:560,m:4},{id:"B3",x:440,y:640,m:2.01},{id:"C1",x:280,y:480,m:5},{id:"C2",x:360,y:560,m:4},{id:"C3",x:440,y:640,m:1.99},{id:"D1",x:280,y:480,m:5},{id:"D2",x:360,y:560,m:4.01},{id:"D3",x:440,y:640,m:1.99}],constraints:[{id:"a1",p1:"A0",p2:"A1",len:{type:"const"}},{id:"a2",p1:"A1",p2:"A2",len:{type:"const"}},{id:"a3",p1:"A2",p2:"A3",len:{type:"const"}},{id:"b1",p1:"A0",p2:"B1",len:{type:"const"}},{id:"b2",p1:"B1",p2:"B2",len:{type:"const"}},{id:"b3",p1:"B2",p2:"B3",len:{type:"const"}},{id:"c1",p1:"A0",p2:"C1",len:{type:"const"}},{id:"c2",p1:"C1",p2:"C2",len:{type:"const"}},{id:"c3",p1:"C2",p2:"C3",len:{type:"const"}},{id:"d1",p1:"A0",p2:"D1",len:{type:"const"}},{id:"d2",p1:"D1",p2:"D2",len:{type:"const"}},{id:"d3",p1:"D2",p2:"D3",len:{type:"const"}}],views:[{id:"view1",type:"trace",p:"A3",stroke:"rgba(255,0,0,.5)"},{id:"view2",type:"trace",p:"B3",stroke:"rgba(0,255,0,.5)"},{id:"view3",type:"trace",p:"C3",stroke:"rgba(255,255,0,.5)"},{id:"view4",type:"trace",p:"D3",stroke:"rgba(255,0,255,.5)"}]}},tooltip=document.getElementById("info"),actcontainer=document.getElementById("actuators-container"),runSymbol=document.getElementById("run-symbol"),statusbar=document.getElementById("statbar"),sbMode=document.getElementById("sbMode"),sbCoords=document.getElementById("sbCoords"),sbCartesian=document.getElementById("sbCartesian"),sbBtn=document.getElementById("sbBtn"),sbDbtn=document.getElementById("sbDbtn"),sbFPS=document.getElementById("sbFPS"),sbState=document.getElementById("sbState"),sbDragging=document.getElementById("sbDragging"),sbDragmode=document.getElementById("sbDragmode"),sbDOF=document.getElementById("sbDOF"),sbGravity=document.getElementById("sbGravity"),editor=g2.editor(),pi=Math.PI,svgplay="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z",svgpause="M144 479H48c-26.5 0-48-21.5-48-48V79c0-26.5 21.5-48 48-48h96c26.5 0 48 21.5 48 48v352c0 26.5-21.5 48-48 48zm304-48V79c0-26.5-21.5-48-48-48h-96c-26.5 0-48 21.5-48 48v352c0 26.5 21.5 48 48 48h96c26.5 0 48-21.5 48-48z",origin=g2().beg({lc:"round",lj:"round",ls:()=>mec.darkmode?"silver":"slategray",fs:"darkgray"}).p().m({x:21,y:0}).l({x:0,y:0}).l({x:0,y:21}).stroke().p().m({x:35,y:0}).l({x:21,y:-2.6}).a({dw:pi/3,x:21,y:2.6}).z().m({x:0,y:35}).l({x:2.6,y:21}).a({dw:pi/3,x:-2.6,y:21}).z().drw().cir({x:0,y:0,r:2.5,fs:"#ccc"}).end().beg({ls:()=>mec.darkmode?"silver":"slategray",font:"14px roboto"}).txt({str:"x",x:38,y:4}).txt({str:"y",x:6,y:30}).end(),gravvec=(cartesian=!0)=>{const ytxt=cartesian?-20:-15;return g2().beg({w:-pi/2,lw:2,ls:()=>mec.darkmode?"silver":"slategray",fs:"darkgray"}).p().m({x:0,y:0}).l({x:50,y:0}).stroke().p().m({x:50,y:0}).l({x:32.5,y:-3.5}).a({dw:pi/3,x:32.5,y:3.5}).z().drw().end().beg({ls:()=>mec.darkmode?"silver":"slategray",font:"14px roboto"}).txt({str:"g",x:-15,y:ytxt}).end()},App={create(){const o=Object.create(this.prototype);return o.constructor.apply(o,arguments),o},prototype:Object.assign({constructor(){this.model={id:"linkage"},this.VERSION="0.6.0",this.evt={dx:0,dy:0,dbtn:0},this.view={x:150,y:150,scl:1,cartesian:!0},this.cnv=document.getElementById("canvas"),this.ctx=this.cnv.getContext("2d"),this.instruct=document.getElementById("instructions"),this.ctxmenu=document.getElementById("contextMenu"),this.ctxmenuheader=document.getElementById("contextMenuHeader"),this.ctxmenubody=document.getElementById("contextMenuBody"),this.build=!1,this.tempElm=!1,this.devmode=!1,this.importConfirmed=!1,this.dragMove=!0,this.nodeInfoValues=["acc","accAbs","vel","velAbs","force","forceAbs"],this.constraintInfoValues=["w","wt","wtt","r","rt","rtt","forceAbs","moment"],this.nodeVectorValues=["acc","vel","force"],this.g=g2(),this.registerEventsFor(this.ctx.canvas).on(["pointer","drag","buttondown","buttonup","click"],e=>{this.g.exe(editor.on(this.pntToUsr(Object.assign({},e)))).exe(this.ctx)}).on(["pointer","drag","pan","fps","buttondown","buttonup","click","pointerenter","pointerleave"],()=>this.showStatus()).on("drag",e=>{this.dragMove||(editor.curElm.x0=editor.curElm.x,editor.curElm.y0=editor.curElm.y),this.showTooltip(e)}).on("pan",e=>{this.pan(e),this.g.exe(this.ctx)}).on("pointer",e=>this.showTooltip(e)).on(["buttonup","click"],()=>this.hideTooltip()).on("buttondown",()=>{this.build&&(["addnode","addbasenode"].includes(this.build.mode)&&this.addNode(),"purgeelement"===this.build.mode&&this.purgeElement(editor.curElm),["free","tran","rot"].includes(this.build.mode)&&this.addConstraint(),"drive"===this.build.mode&&this.addDrive(editor.curElm),"force"===this.build.mode&&this.addForce(),"spring"===this.build.mode&&this.addSpring(),["fix","flt"].includes(this.build.mode)&&this.addSupportShape())}).on("render",()=>this.g.exe(this.ctx)).on("tick",e=>this.tick(e)),this.state="created"},get cartesian(){return this.view.cartesian},get height(){return this.ctx.canvas.height},get dragging(){return!!(editor.curState&g2.DRAG)},showStatus(){let{x:x,y:y}=this.pntToUsr({x:this.evt.x,y:this.evt.y});sbCoords.innerHTML=`x=${x}, y=${y}`,sbDragmode.innerHTML=`dragmode=${this.dragMove?"move":"edit"}`,sbDOF.innerHTML=`dof=${this.model.dof}`,sbFPS.innerHTML=`fps=${this.fps}`,this.devmode&&(sbGravity.innerHTML=`gravity=${this.model.hasGravity?"on":"off"}`,sbMode.innerHTML=`mode=${this.evt.type}`,sbCartesian.innerHTML=`cartesian=${this.cartesian}`,sbBtn.innerHTML=`btn=${this.evt.btn}`,sbDbtn.innerHTML=`dbtn=${this.evt.dbtn}`,sbState.innerHTML=`state=${g2.editor.state[editor.curState]}`,sbDragging.innerHTML=`dragging=${this.dragging}`)},showTooltip(e){const info=this.model.info;tooltip.style.left=e.clientX+15+"px",tooltip.style.top=e.clientY-50+"px",editor.dragInfo&&!this.dragMove?(tooltip.innerHTML=editor.dragInfo,tooltip.style.display="inline"):info&&this.dragMove?(tooltip.innerHTML=info,tooltip.style.display="inline"):this.hideTooltip()},hideTooltip(){tooltip.style.display="none"},tick(e){this.model&&(this.dragging?this.dragMove?this.model.pose():this.updDependants(editor.curElm):"active"===this.state?(this.model.tick(e.dt),this.model.isActive||this.stop()):"input"===this.state&&this.model.tick(0),this.g.exe(this.ctx))},init(){for(mec.model.extend(this.model),this.model.init().asmPos(),this.updateg(),this.model.inputs=[];actcontainer.lastChild;)actcontainer.removeChild(actcontainer.lastChild);let drv,prv=!1;for(;drv=this.driveByInput(prv);){let id=drv.constraint.id+"-"+drv.value,max="ori"===drv.value?Math.round(180*drv.constraint.ori.Dw/pi):Math.round(drv.constraint.len.Dr);actcontainer.appendChild(this.createInputSlider(id,(this.cnv.width-150)/2,max));let elm=document.getElementById(id);mecESlider.RegisterElm(elm),console.log(prv),elm.initEventHandling(this,id,this.model.constraintById(drv.constraint.id)[drv.value].inputCallbk),this.model.inputs.push(id),prv=drv}"undefined"!=typeof t&&null!==t||this.startTimer(),this.state=this.model.inputs.length>0?"input":"initialized"},run(){this.state="active",runSymbol.setAttribute("d",svgpause)},idle(){this.state=this.model.inputs.length>0?"input":"idle",runSymbol.setAttribute("d",svgplay)},stop(){this.model.stop(),this.idle()},reset(){this.model.reset(),this.model.asmPos();for(const drive in this.model.inputs){let ident=this.model.inputs[drive].split("-");this.model.constraintById(ident[0])[ident[1]].inputCallbk({target:{value:0}}),document.getElementById(ident[0]+"-"+ident[1]).value=0,this.notify(ident[0]+"-"+ident[1],0)}this.notify("render"),this.idle()},updDependants(elm){let dependants=[];for(const constraint of this.model.constraints)constraint.dependsOn(elm)&&dependants.push(constraint);dependants.forEach(el=>{el.init(this.model),"ctrl"!==el.type||"drive"!==el.ori.type&&"drive"!==el.len.type||(el.ori.repeat&&(el.ori.Dt/=el.ori.repeat),el.len.repeat&&(el.len.Dt/=el.len.repeat))})},toggleDevmode(){this.devmode=!this.devmode,this.devmode||(sbGravity.innerHTML=sbMode.innerHTML=sbCartesian.innerHTML=sbBtn.innerHTML=sbDbtn.innerHTML=sbState.innerHTML=sbDragging.innerHTML=""),this.showStatus()},toggleDarkmode(){mec.darkmode=!mec.darkmode,this.jsonEditor.setOption("theme",`${mec.darkmode?"lucario":"mdn-like"}`),this.cnv.style.backgroundColor=mec.darkmode?"#344c6b":"rgb(250, 253, 242)",this.notify("render")},resetView(){this.view.x=150,this.view.y=150,this.view.scl=1,this.view.cartesian=!0,this.notify("render")},createInputSlider(actuated,width,max){let template=document.createElement("template");return template.innerHTML=``,template.content.firstChild},updateg(){let apphasmodel=!("object"!=typeof this.model||!Object.keys(this.model).length);this.g=g2().clr().view(this.view).grid({color:()=>mec.darkmode?"rgba(255, 255, 255, 0.1)":"rgba(0, 0, 0, 0.1)",size:100}).grid({color:()=>mec.darkmode?"rgba(255, 255, 255, 0.1)":"rgba(0, 0, 0, 0.1)",size:20}).p().m({x:()=>-this.view.x/this.view.scl,y:0}).l({x:()=>(this.cnv.width-this.view.x)/this.view.scl,y:0}).m({x:0,y:()=>-this.view.y/this.view.scl}).l({x:0,y:()=>(this.cnv.height-this.view.y)/this.view.scl}).z().stroke({ls:()=>mec.darkmode?"rgba(255, 255, 255, 0.3)":"rgba(0, 0, 0, 0.2)",lw:2}).use({grp:origin,x:()=>(10-this.view.x)/this.view.scl,y:()=>(10-this.view.y)/this.view.scl,scl:()=>this.view.scl}),apphasmodel&&this.model.hasGravity&&(this.cartesian?this.g.use({grp:gravvec(!0),x:()=>(this.cnv.width-15-this.view.x)/this.view.scl,y:()=>(this.cnv.height-15-this.view.y)/this.view.scl,scl:()=>this.view.scl}):this.g.use({grp:gravvec(!1),x:()=>(this.cnv.width-15-this.view.x)/this.view.scl,y:()=>(69-this.view.y)/this.view.scl,scl:()=>this.view.scl})),apphasmodel&&this.model.draw(this.g),this.notify("render")},resetApp(){this.build=!1,this.tempElm=!1,this.instruct.innerHTML="",this.notify("render")},addNode(){if(void 0===editor.curElm||!editor.curElm.hasOwnProperty("m")){{let{x:x,y:y}=this.pntToUsr({x:this.evt.x,y:this.evt.y}),node={id:this.getNewChar(),x:x,y:y};"addbasenode"===this.build.mode&&(node.base=!0),this.model.addNode(mec.node.extend(node)),node.init(this.model),this.updateg()}this.build.continue||(document.body.style.cursor="default",this.resetApp())}},removeInput(id){for(let dof of["-len","-ori"])this.model.inputs.includes(id+dof)&&(actcontainer.removeChild(document.getElementById(id+dof)),this.model.inputs.splice(this.model.inputs.findIndex(el=>el.id===id),1))},purgeElement(elem){if(elem){if(["node","ctrl"].includes(elem.type)){let dependants=this.model.dependentsOf(elem).constraints;if(dependants.length>0)for(let dep of dependants)(this.model.inputs.includes(dep.id+"-ori")||this.model.inputs.includes(dep.id+"-len"))&&this.removeInput(dep.id)}if("node"===elem.type)this.model.purgeNode(elem);else if(["free","tran","rot","ctrl"].includes(elem.type))(this.model.inputs.includes(elem.id+"-ori")||this.model.inputs.includes(elem.id+"-len"))&&this.removeInput(elem.id),this.model.purgeConstraint(elem);else if(["force","spring"].includes(elem.type))this.model.purgeLoad(elem);else{if(!["vector","trace","info"].includes(elem.type))return;this.model.purgeView(elem)}this.updateg(),document.body.style.cursor="default",this.resetApp()}},replaceConstraint(oldC,newC){this.reset();let rebindorilistener=!1,rebindlenlistener=!1,oridrv=!1,lendrv=!1;if("drive"===newC.ori.type&&(oridrv={newC:newC,value:"ori"}),"drive"===newC.len.type&&(lendrv={newC:newC,value:"len"}),oldC.ori&&oldC.ori.input&&document.getElementById(oldC.id+"-ori")&&(document.getElementById(oldC.id+"-ori").removeEventListener("input",this.model.constraintById(oldC.id).ori.inputCallbk,!1),newC.ori.input&&(rebindorilistener=!0)),oldC.len&&oldC.len.input&&document.getElementById(oldC.id+"-len")&&(document.getElementById(oldC.id+"-len").removeEventListener("input",this.model.constraintById(oldC.id).len.inputCallbk,!1),newC.len.input&&(rebindlenlistener=!0)),this.model.constraints.splice(this.model.constraints.indexOf(this.model.constraintById(oldC.id)),1),this.model.addConstraint(mec.constraint.extend(newC)),newC.init(this.model),this.updateg(),this.model.pose(),lendrv&&!this.model.inputs.includes(newC.id+"-len")||oridrv&&!this.model.inputs.includes(newC.id+"-ori")){let drv,prv=!1;for(;drv=this.driveByInput(prv);){let id=drv.constraint.id+"-"+drv.value,max="ori"===drv.value?Math.round(180*drv.constraint.ori.Dw/pi):Math.round(drv.constraint.len.Dr);actcontainer.appendChild(this.createInputSlider(id,(this.cnv.width-150)/2,max));let elm=document.getElementById(id);mecESlider.RegisterElm(elm),elm.initEventHandling(this,id,this.model.constraintById(drv.constraint.id)[drv.value].inputCallbk),this.model.inputs.push(id),prv=drv}window.dispatchEvent(new Event("resize"))}else!newC.ori.input&&this.model.inputs.includes(newC.id+"-ori")?(actcontainer.removeChild(document.getElementById(newC.id+"-ori")),this.model.inputs.splice(this.model.inputs.findIndex(el=>el.id===newC.id),1)):!newC.len.input&&this.model.inputs.includes(newC.id+"-len")&&(actcontainer.removeChild(document.getElementById(newC.id+"-len")),this.model.inputs.splice(this.model.inputs.findIndex(el=>el.id===newC.id),1));if(oldC.ori&&oldC.ori.Dw&&newC.ori&&newC.ori.Dw&&oldC.ori.Dw!==newC.ori.Dw){let mecslider=document.getElementById(newC.id+"-ori");mecslider.max=`${Math.round(180*newC.ori.Dw/Math.PI)}`,mecslider.childNodes[2].max=`${Math.round(180*newC.ori.Dw/Math.PI)}`}if(oldC.len&&oldC.len.Dr&&newC.len&&newC.len.Dr&&oldC.len.Dr!==newC.len.Dr){let mecslider=document.getElementById(newC.id+"-len");mecslider.max=mecslider.children[1]=`${Math.round(newC.len.Dr)}`}rebindorilistener&&document.getElementById(oridrv.newC.id+"-ori").initEventHandling(this,oridrv.newC.id,this.model.constraintById(oridrv.newC.id)[oridrv.value].inputCallbk),rebindlenlistener&&document.getElementById(lendrv.newC.id+"-len").initEventHandling(this,lendrv.newC.id,this.model.constraintById(lendrv.newC.id)[lendrv.value].inputCallbk),this.state=this.model.inputs.length>0?"input":"initialized"},driveByInput(prev=!1){let found=!1,start=!prev;for(const constraint of this.model.constraints)if(constraint.ori&&"drive"===constraint.ori.type&&constraint.ori.input&&(start?this.model.inputs.includes(constraint.id+"-ori")||(found={constraint:constraint,value:"ori"}):start=constraint.ori===prev.constraint.ori),constraint.len&&"drive"===constraint.len.type&&constraint.len.input&&!found&&(start?this.model.inputs.includes(constraint.id+"-len")||(found={constraint:constraint,value:"len"}):start=constraint.len===prev.constraint.len),found)return found;return!1},addConstraint(){if(this.build.firstnode){if(!editor.curElm||!editor.curElm.hasOwnProperty("m"))return;{if(editor.curElm.id===this.build.firstnode.id)return this.instruct.classList.add("blink"),void setTimeout(()=>{this.instruct.classList.remove("blink")},1400);let tmplen=!1,tmpori=!1;switch(this.build.mode){case"free":break;case"tran":tmpori={type:"const"};break;case"rot":tmplen={type:"const"};break;default:console.log("something went wrong while adding constraint...")}let constraint={id:this.getNewChar("constraint"),p1:this.build.firstnode.id,p2:editor.curElm.id};tmplen&&(constraint.len=tmplen),tmpori&&(constraint.ori=tmpori),this.model.addConstraint(mec.constraint.extend(constraint)),constraint.init(this.model),this.updateg()}this.build.continue?(delete this.build.firstnode,this.instruct.innerHTML="Select first node; [ESC] to cancel"):this.resetApp()}else{if(!editor.curElm||!editor.curElm.hasOwnProperty("m"))return;this.build.firstnode=editor.curElm,this.instruct.innerHTML="Select second node; [ESC] to cancel"}},getNewChar(x="node"){let name,obj,maxChar,char,charArr=[];if("node"===x&&this.model.nodes.length>0||"constraint"===x&&this.model.constraints.length>0){"node"===x?(obj=this.model.nodes,name="node",maxChar=90):(obj=this.model.constraints,name="constraint",maxChar=122);for(let i=0;i{this.instruct.innerHTML="Select a constraint to add a drive to; [ESC] to cancel"},2400)}},addSupportShape(){if(editor.curElm&&editor.curElm.hasOwnProperty("m")){{let shape={type:this.build.mode,p:editor.curElm.id};this.model.addShape(mec.shape.extend(shape)),shape.init(this.model),this.updateg()}document.body.style.cursor="default",this.resetApp()}},addForce(){if(editor.curElm&&editor.curElm.hasOwnProperty("m")){{let i=0;this.model.loads.forEach(load=>{"force"===load.type&&i++}),i+=1;let force={type:this.build.mode,id:`F${i}`,p:editor.curElm.id};this.model.addLoad(mec.load.extend(force)),force.init(this.model),this.updateg()}document.body.style.cursor="default",this.resetApp()}},addSpring(){if(this.build.firstnode){if(!editor.curElm||!editor.curElm.hasOwnProperty("m"))return;{let i=0;this.model.loads.forEach(load=>{"spring"===load.type&&i++}),i+=1;let spring={type:this.build.mode,id:`S${i}`,p1:this.build.firstnode.id,p2:editor.curElm.id};this.model.addLoad(mec.load.extend(spring)),spring.init(this.model),this.updateg()}this.resetApp()}else{if(!editor.curElm||!editor.curElm.hasOwnProperty("m"))return;this.build.firstnode=editor.curElm,this.instruct.innerHTML="Select second node; [ESC] to cancel"}},initViewModal(){this.tempElm||(this.tempElm={new:{id:"",type:"trace"}}),this.viewModal.setContent(ctxm.viewModal()),document.getElementById("view-fill-color-btn").style.backgroundColor="transparent",this.viewModal.show()},closeViewModal(){this.tempElm=!1},addViewFromModal(){0===this.tempElm.new.id.length&&(this.tempElm.new.id=`view${this.model.views.length+1}`),this.model.addView(mec.view.extend(this.tempElm.new)),this.tempElm.new.init(this.model),["trace","vector"].includes(this.tempElm.new.type)&&this.updateg(),this.resetApp(),this.viewModal.hide()},initCtxm(elm){console.log(elm.type),this.tempElm={},this.tempElm.replace=!1,this.tempElm.type=["free","rot","tran","ctrl"].includes(elm.type)?"constraint":elm.type,this.tempElm.old=JSON.parse(elm.asJSON()),this.tempElm.new=JSON.parse(elm.asJSON()),this.tempElm.new.ori||(this.tempElm.new.ori={type:"free"}),this.tempElm.new.len||(this.tempElm.new.len={type:"free"}),this.tempElm.labelState={nodes:this.model.graphics.labels.nodes,constraints:this.model.graphics.labels.constraints,loads:this.model.graphics.labels.loads},this.model.graphics.labels.nodes||(this.model.graphics.labels.nodes=!0),this.model.graphics.labels.constraints||(this.model.graphics.labels.constraints=!0),this.model.graphics.labels.loads||(this.model.graphics.labels.loads=!0),this.notify("render"),this.updateCtxm(this.tempElm.old,this.tempElm.type),this.showCtxm()},showCtxm(){this.ctxmenu.style.display="block",this.ctxmenu.style.left=`${this.evt.clientX}px`,this.ctxmenu.style.top=`${this.evt.clientY}px`},hideCtxm(){this.ctxmenu.style.display="none",this.tempElm.new&&this.tempElm.replace&&"constraint"===this.tempElm.type&&this.replaceConstraint(this.tempElm.old,this.tempElm.new),this.tempElm.labelState.nodes!==this.model.graphics.labels.nodes&&(this.model.graphics.labels.nodes=this.tempElm.labelState.nodes),this.tempElm.labelState.constraints!==this.model.graphics.labels.constraints&&(this.model.graphics.labels.constraints=this.tempElm.labelState.constraints),this.tempElm.labelState.loads!==this.model.graphics.labels.loads&&(this.model.graphics.labels.loads=this.tempElm.labelState.loads),this.tempElm=!1},updateCtxm(elm,type,doftypechanged=!1){if(console.log(elm),console.log(`doftypechanged: ${doftypechanged}`),doftypechanged)for(let dof of doftypechanged)if(elm[dof]){if("free"===elm[dof].type&&(elm[dof]={type:"free"}),"const"===elm[dof].type&&(elm[dof]={type:"const"}),"ref"===elm[dof].type){for(let prop of["Dt","Dw","Dr","input","bounce","repeat","func","ratio","t0"])elm[dof].hasOwnProperty(prop)&&delete elm[dof][prop];elm[dof].ref=this.model.constraints[0].id}if("drive"===elm[dof].type)for(let prop of["ref","refval"])elm[dof].hasOwnProperty(prop)&&delete elm[dof][prop]}for(;this.ctxmenubody.lastChild;)this.ctxmenubody.removeChild(this.ctxmenubody.lastChild);this.ctxmenuheader.innerHTML=ctxm.header(elm,type),"constraint"===type&&(this.ctxmenubody.innerHTML+=ctxm.sectionTitle("orientation"),this.ctxmenubody.innerHTML+=ctxm.oriType(elm),elm.ori&&"drive"===elm.ori.type&&(this.tempElm.new.ori.hasOwnProperty("Dt")||(this.tempElm.new.ori.Dt=1),this.ctxmenubody.innerHTML+=ctxm.Dt(elm,"ori"),this.tempElm.new.ori.hasOwnProperty("Dw")||(this.tempElm.new.ori.Dw=2*pi),this.ctxmenubody.innerHTML+=ctxm.Dw(elm,"ori")),elm.ori&&"ref"===elm.ori.type&&(this.ctxmenubody.innerHTML+=ctxm.ref(elm,"ori",elm.ori.ref)),this.ctxmenubody.innerHTML+=ctxm.sectionTitle("length"),this.ctxmenubody.innerHTML+=ctxm.lenType(elm),elm.len&&"drive"===elm.len.type&&(this.tempElm.new.len.hasOwnProperty("Dt")||(this.tempElm.new.len.Dt=1),this.ctxmenubody.innerHTML+=ctxm.Dt(elm,"len"),this.tempElm.new.len.hasOwnProperty("Dr")||(this.tempElm.new.len.Dr=100),this.ctxmenubody.innerHTML+=ctxm.Dr(elm,"len")),elm.len&&"ref"===elm.len.type&&(this.ctxmenubody.innerHTML+=ctxm.ref(elm,"len",elm.len.ref)),this.ctxmenubody.innerHTML+=ctxm.sectionTitle("nodes"),this.ctxmenubody.innerHTML+=ctxm.nodes(elm),this.ctxmenubody.innerHTML+=ctxm.removeConstraintButton()),"node"===type&&(this.ctxmenubody.innerHTML+=ctxm.nodeCoordinates(elm),this.ctxmenubody.innerHTML+=ctxm.nodeBase(elm)),"force"===type&&(this.ctxmenubody.innerHTML+=ctxm.forceValue(elm),this.ctxmenubody.innerHTML+=ctxm.forceMode(elm),this.ctxmenubody.innerHTML+=ctxm.forceNode(elm)),"spring"===type&&(this.ctxmenubody.innerHTML+=ctxm.springNodes(elm),this.ctxmenubody.innerHTML+=ctxm.springLen(elm),this.ctxmenubody.innerHTML+=ctxm.springK(elm))},loadFromJSON(files){let model,file=files[0],fr=new FileReader;fr.onload=(()=>e=>{model=JSON.parse(e.target.result),this.newModel(model),this.importConfirmed=!1})(),fr.readAsText(file)},saveToJSON(){let a=document.createElement("a"),file=new Blob([this.model.asJSON()],{type:"application/json"});a.href=URL.createObjectURL(file),a.download=this.model.id&&this.model.id.length>0?`${this.model.id}.json`:"linkage.json",document.body.appendChild(a),a.click(),document.body.removeChild(a)},newModel(model={}){if("object"!=typeof this.model||this.importConfirmed||confirm("All unsaved changes will be lost! Continue?")){for(;actcontainer.lastChild;)actcontainer.removeChild(actcontainer.lastChild);delete this.model,this.model=model,this.init(),this.updateg()}},updateTempElmNew(key,value){this.tempElm.new[key]=value},toggleViewfill(){let fill=document.getElementById("view-fill-color"),fillBtn=document.getElementById("view-fill-color-btn");fill.disabled=!fill.disabled,fillBtn.style.backgroundColor=fill.disabled?"transparent":"#e9ecef",fill.disabled&&this.tempElm.new.hasOwnProperty("fill")?delete this.tempElm.new.fill:fill.disabled||this.tempElm.new.hasOwnProperty("fill")||(this.tempElm.new.fill="#009900")}},mixin.observable,mixin.pointerEventHdl,mixin.tickTimer,mixin.zoomPan)};let app;window.onload=(()=>{(app=App.create()).init(),app.modelModal=new Modal(document.getElementById("modelModal"),{backdrop:"static",keyboard:!0}),app.viewModal=new Modal(document.getElementById("viewModal"),{backdrop:"static"}),app.jsonEditor=CodeMirror.fromTextArea(document.getElementById("modalTextarea"),{mode:"javascript",theme:"lucario",lineNumbers:!0,matchBrackets:!0,viewportMargin:1/0,lineWrapping:!1}),new Draggabilly(document.getElementById("contextMenu"),{containment:".main-container",handle:".card-header"}),new Modal(document.getElementById("aboutModal"),{keyboard:!0,content:`
`}),new Modal(document.getElementById("keysModal"),{keyboard:!0}),events.navbarClick("navcollapse"),events.navbarChange("import"),events.sidebarClick("sb-l"),events.keyboardDown(),events.ctxmClick("contextMenu"),events.ctxmInput("contextMenu"),events.ctxmChange("contextMenu"),events.resize(),events.modalShown("modelModal"),events.modalAccept("modalAccept"),events.viewModalChange("viewModal"),events.viewModalClick("viewModal"),events.viewModalHide("viewModal"),app.toggleDarkmode(),window.dispatchEvent(new Event("resize"))});
\ No newline at end of file
+const mixin={observable:{notify(key,val){if(this.signals&&this.signals[key])for(let hdl of this.signals[key])hdl(val);return this},on(key,handler){if(Array.isArray(key))for(let k of key)this.on(k,handler);else((this.signals||(this.signals={}))&&this.signals[key]||(this.signals[key]=[])).push(handler);return this},remove(key,handler){let idx=this.signals&&this.signals[key]?this.signals[key].indexOf(handler):-1;idx>=0&&this.signals[key].splice(idx,1)}},pointerEventHdl:{registerEventsFor(elm){return elm.addEventListener("mousemove",this,!1),elm.addEventListener("mousedown",this,!1),elm.addEventListener("mouseup",this,!1),elm.addEventListener("mouseenter",this,!1),elm.addEventListener("mouseleave",this,!1),elm.addEventListener("wheel",this,!1),elm.addEventListener("touchmove",this,!1),elm.addEventListener("touchstart",this,!1),elm.addEventListener("touchend",this,!1),this},handleEvent(e){if(e.type in this){let evt=this.getEventData(e);this.isDefaultPreventer(e.type)&&e.preventDefault(),this[e.type](evt),this.tick?this.cumulate(evt):(this.save(evt),this.notify(evt.type,this))}},getEventData(e){let bbox=e.target.getBoundingClientRect&&e.target.getBoundingClientRect()||{left:0,top:0},touch=e.changedTouches&&e.changedTouches[0],x=(touch&&touch.clientX||e.clientX)-Math.floor(bbox.left),y=(touch&&touch.clientY||e.clientY)-Math.floor(bbox.top);return{type:e.type,basetype:e.type,x:x,y:this.cartesian?this.height-y:y,dx:0,dy:0,clientX:e.clientX,clientY:e.clientY,btn:void 0!==e.buttons?e.buttons:e.button||e.which,ctrlKey:e.ctrlKey,delta:Math.max(-1,Math.min(1,e.deltaY||e.wheelDelta))||0}},mousemove(e){switch(e.dx=e.x-this.evt.xi,e.dy=e.y-this.evt.yi,e.btn){case 1:e.type=e.ctrlKey?"pan":this.dragging?"drag":"pointer";break;case 4:e.type="pan";break;default:e.type="pointer"}},mousedown(e){e.type="buttondown"},mouseup(e){e.type=0===this.evt.dx&&0===this.evt.dy?"click":"buttonup"},mouseenter(e){e.type="pointerenter"},mouseleave(e){e.type="pointerleave"},wheel(e){e.type="wheel"},touchmove(e){e.dx=e.x-this.evt.xi,e.dy=e.y-this.evt.yi,e.type="pan"},touchstart(e){e.type="buttondown"},touchend(e){e.type=0===this.evt.dx&&0===this.evt.dy?"click":"buttonup"},save(e){this.evt.type=e.type,this.evt.basetype=e.basetype,this.evt.dx=e.dx,this.evt.dy=e.dy,this.evt.clientX=e.clientX,this.evt.clientY=e.clientY,this.evt.x=this.evt.xi=e.x,this.evt.y=this.evt.yi=e.y,this.evt.dbtn=e.btn-this.evt.btn,this.evt.btn=e.btn,this.evt.delta=e.delta||0},cumulate(e){this.evt.unused&&["pointer","drag","wheel","pan"].includes(e.type)?(this.evt.dx+=e.dx,this.evt.dy+=e.dy,this.evt.xi=e.x,this.evt.yi=e.y,this.evt.delta+=e.delta||0):(this.save(e),this.evt.unused=!0)},isDefaultPreventer:type=>["touchstart","touchend","touchmove"].includes(type)},tickTimer:{startTimer(){return this.timerTick.ptr=this.timerTick.bind(this),this.fps="?",this.frames=0,this.notify("timerStart",this),this.timerTick(this.time0=this.fpsOrigin=performance.now()),this},endTimer(){return cancelAnimationFrame(this.rafid),this.notify("timerEnd",this.t/1e3),this},timerTick(time){return this.fpsCount(time),this.evt.type&&(this.evt.t=time,this.evt.dt=(time-this.t)/1e3,this.notify(this.evt.type,({x:x,y:y,t:t,dt:dt,btn:btn,type:type}=this.evt)),this.evt.unused=this.evt.type=!1),this.notify("tick",{t:time,dt:(time-this.t)/1e3}),this.t=time,this.rafid=requestAnimationFrame(this.timerTick.ptr),this},fpsCount(time){if(time-this.fpsOrigin>1e3){let fps=~~(1e3*this.frames/(time-this.fpsOrigin)+.5);fps!==this.fps&&this.notify("fps",this.fps=fps),this.fpsOrigin=time,this.frames=0}this.frames++}},zoomPan:{pan:function({dx:dx,dy:dy}){this.view.x+=dx,this.view.y+=dy},zoom:function({x:x,y:y,scl:scl}){this.view.x=x+scl*(this.view.x-x),this.view.y=y+scl*(this.view.y-y),this.view.scl*=scl,this.notify("view",this.view)},pntToUsr:function(p){let vw=this.view;return p.x=(p.x-vw.x)/vw.scl,p.y=(p.y-vw.y)/vw.scl,p}}};"use strict";function mecESlider(elm){return elm.constructor()}mecESlider.prototype={constructor:function(){this.width=+this.getAttribute("width")||100,this.min=+this.getAttribute("min")||0,this.max=+this.getAttribute("max")||100,this.step=+this.getAttribute("step")||1,this.value=+this.getAttribute("value")||0,this.frac=Math.max(0,Math.ceil(-Math.log10(this.step))),this.anistep=this.step,this.innerHTML=this.html,this.setAttribute("style",this.getAttribute("style")||""),this.slider=this.querySelector("input"),this.output=this.querySelector("output"),this.forward=this.querySelector(".forward"),this.reverse=this.querySelector(".reverse")},fwdsym:"▷",revsym:"◁",stopsym:"☐",get html(){return`
"),template}};const events={preventDefaultCTXM:()=>document.addEventListener("contextmenu",e=>e.preventDefault()),sidebarClick:id=>{document.getElementById(id).addEventListener("click",e=>{app.build&&(app.resetApp(),document.body.style.cursor="default"),e.target&&["free","tran","rot"].includes(e.target.id)&&(app.build={mode:e.target.id,continue:e.shiftKey},app.instruct.innerHTML="Select first node; [ESC] to cancel"),e.target&&"drive"===e.target.id&&(app.build={mode:e.target.id},app.reset(),app.instruct.innerHTML="Select a constraint to add a drive to; [ESC] to cancel"),!e.target||"addnode"!==e.target.id&&"addbasenode"!==e.target.id||(app.build={mode:e.target.id,continue:e.shiftKey},app.instruct.innerHTML="Left-click on the canvas to place a new node; [ESC] to cancel",document.body.style.cursor="crosshair")})},navbarClick:id=>{document.getElementById(id).addEventListener("click",e=>{app.build&&(app.resetApp(),document.body.style.cursor="default"),e.target&&"newModel"===e.target.id&&app.newModel(),e.target&&e.target.id.includes("nav-example-")&&app.newModel(JSON.parse(JSON.stringify(examples[e.target.id.replace("nav-example-","")]))),e.target&&"export"===e.target.id&&app.saveToJSON(),e.target&&"import"===e.target.id&&(app.importConfirmed=!!confirm("All unsaved changes will be lost! Continue?")||(e.preventDefault(),!1)),e.target&&"dragmode"===e.target.id&&(app.dragMove=!app.dragMove,app.dragMove||app.reset()),e.target&&"model-edit"===e.target.id&&app.modelModal.show(),e.target&&"nav-purgeelement"===e.target.id&&(app.build={mode:e.target.id.replace("nav-","")},app.instruct.innerHTML="Left-click on an element to delete it and all its dependants; [ESC] to cancel"),!e.target||"nav-addnode"!==e.target.id&&"nav-addbasenode"!=e.target.id||(app.build={mode:e.target.id.replace("nav-","")},app.instruct.innerHTML="Left-click on the canvas to place a new node; [ESC] to cancel",document.body.style.cursor="crosshair"),e.target&&["nav-free","nav-tran","nav-rot","nav-spring"].includes(e.target.id)&&(app.build={mode:e.target.id.replace("nav-","")},app.instruct.innerHTML="select first node; [ESC] to cancel"),e.target&&"nav-drive"===e.target.id&&(app.build={mode:e.target.id.replace("nav-","")},app.instruct.innerHTML="Select a constraint to add a drive to; [ESC] to cancel"),e.target&&"nav-force"===e.target.id&&(app.build={mode:e.target.id.replace("nav-","")},app.instruct.innerHTML="Left-click on a node to add a force; [ESC] to cancel"),!e.target||"nav-fix"!==e.target.id&&"nav-flt"!==e.target.id||(app.build={mode:e.target.id.replace("nav-","")},app.instruct.innerHTML=`Left-click on a node to add a ${app.build.mode}-shape; [ESC] to cancel`),e.target&&"nav-addview"===e.target.id&&app.initViewModal(),e.target&&"darkmode"===e.target.id&&app.toggleDarkmode(),e.target&&"resetview"===e.target.id&&app.resetView(),e.target&&"toggleNodes"===e.target.id&&(app.model.graphics.linkage.nodes=!app.model.graphics.linkage.nodes,app.notify("render")),e.target&&"toggleConstraints"===e.target.id&&(app.model.graphics.linkage.constraints=!app.model.graphics.linkage.constraints,app.notify("render")),e.target&&"toggleNodeLabels"===e.target.id&&(app.model.graphics.labels.nodes=!app.model.graphics.labels.nodes,app.tempElm.labelState&&(app.tempElm.labelState.nodes=app.model.graphics.labels.nodes),app.notify("render")),e.target&&"toggleConstraintLabels"===e.target.id&&(app.model.graphics.labels.constraints=!app.model.graphics.labels.constraints,app.tempElm.labelState&&(app.tempElm.labelState.constraints=app.model.graphics.labels.constraints),app.notify("render")),e.target&&"toggleLoadLabels"===e.target.id&&(app.model.graphics.labels.loads=!app.model.graphics.labels.loads,app.tempElm.labelState&&(app.tempElm.labelState.loads=app.model.graphics.labels.loads),app.notify("render")),e.target&&"run"===e.target.id&&("active"===app.state?app.idle():app.run()),e.target&&"stop"===e.target.id&&app.stop(),e.target&&"reset"===e.target.id&&app.reset(),e.target&&"toggle-g"===e.target.id&&(app.model.gravity.active=!app.model.gravity.active,app.updateg())})},navbarChange:id=>{document.getElementById(id).addEventListener("change",e=>app.loadFromJSON(e.target.files))},keyboardDown:()=>{document.addEventListener("keydown",e=>{console.log(`Key pressed: ${e.key}`),"true"===document.getElementById("modelModal").attributes["aria-hidden"].value&&"true"===document.getElementById("viewModal").attributes["aria-hidden"].value&&("Escape"===e.key&&app.build&&(app.resetApp(),document.body.style.cursor="default"),"e"===e.key&&app.modelModal.show(),"r"===e.key&&app.resetView(),"g"===e.key&&(app.model.gravity.active=!app.model.gravity.active,app.updateg()),"v"===e.key&&app.initViewModal(),"i"===e.key&&(app.dragMove=!app.dragMove,app.dragMove||app.reset()),"p"===e.key&&(app.build={mode:"purgeelement"},app.instruct.innerHTML="Left-click on an element to delete it and all its dependants; [ESC] to cancel"))})},ctxmClick:id=>{document.getElementById(id).addEventListener("click",e=>{console.log("ctxmClick fired");e.target&&"node-trash"===e.target.id&&(app.model.removeNode(app.model.nodeById(app.tempElm.old.id))?(app.updateg(),app.hideCtxm()):(app.instruct.innerHTML='Node has dependencies.',setTimeout(()=>{app.instruct.innerHTML=""},2400))),e.target&&"constraint-trash"===e.target.id&&(app.model.removeConstraint(app.model.constraintById(app.tempElm.old.id))?(app.removeInput(app.tempElm.old.id),app.updateg(),app.hideCtxm()):(app.instruct.innerHTML='Constraint has dependencies.',setTimeout(()=>{app.instruct.innerHTML=""},2400))),e.target&&"force-trash"===e.target.id&&(app.model.removeLoad(app.model.loadById(app.tempElm.old.id))?(app.updateg(),app.hideCtxm()):(app.instruct.innerHTML='Node has dependencies.',setTimeout(()=>{app.instruct.innerHTML=""},2400))),e.target&&"spring-trash"===e.target.id&&(app.model.removeLoad(app.model.loadById(app.tempElm.old.id))?(app.updateg(),app.hideCtxm()):(app.instruct.innerHTML='Node has dependencies.',setTimeout(()=>{app.instruct.innerHTML=""},2400))),e.target&&"ori-input"===e.target.id&&(e.target.checked&&!app.tempElm.new.ori.input?app.tempElm.new.ori.input=!0:!e.target.checked&&app.tempElm.new.ori.input&&delete app.tempElm.new.ori.input,app.tempElm.replace=!0),e.target&&"len-input"===e.target.id&&(e.target.checked&&!app.tempElm.new.len.input?app.tempElm.new.len.input=!0:!e.target.checked&&app.tempElm.new.len.input&&delete app.tempElm.new.len.input,app.tempElm.replace=!0)})},ctxmInput:id=>{document.getElementById(id).addEventListener("input",e=>{if(console.log("ctxmInput fired"),e.target&&"ori-drive-Dt"===e.target.id&&(app.tempElm.new.ori.Dt=e.target.valueAsNumber,app.tempElm.replace=!0),e.target&&"len-drive-Dt"===e.target.id&&(app.tempElm.new.len.Dt=e.target.valueAsNumber,app.tempElm.replace=!0),e.target&&"ori-drive-Dw"===e.target.id&&(app.tempElm.new.ori.Dw=e.target.valueAsNumber,app.tempElm.replace=!0),e.target&&"len-drive-Dr"===e.target.id&&(app.tempElm.new.len.Dr=e.target.valueAsNumber,app.tempElm.replace=!0),e.target&&"node-x"===e.target.id){let node=app.model.nodeById(app.tempElm.old.id);node.x0=node.x=e.target.valueAsNumber,app.updDependants(node),app.notify("render")}if(e.target&&"node-y"===e.target.id){let node=app.model.nodeById(app.tempElm.old.id);node.y0=node.y=e.target.valueAsNumber,app.updDependants(node),app.notify("render")}e.target&&"node-base"===e.target.id&&(app.model.nodeById(app.tempElm.old.id).base=!!e.target.checked,app.notify("render")),e.target&&"force-value"===e.target.id&&(app.model.loadById(app.tempElm.old.id).value=mec.from_N(e.target.valueAsNumber),app.notify("render")),e.target&&"spring-len0"===e.target.id&&(app.model.loadById(app.tempElm.old.id).len0=e.target.valueAsNumber,app.notify("render")),e.target&&"spring-k"===e.target.id&&(app.model.loadById(app.tempElm.old.id).k=mec.from_N_m(e.target.valueAsNumber),app.notify("render"))})},ctxmChange:id=>{document.getElementById(id).addEventListener("change",e=>{if(console.log("ctxmChange fired"),app.tempElm){let ctxmdirty=!1,doftypechanged=!1;e.target&&"select-p1"===e.target.id&&(app.tempElm.new.p1=e.target.value,ctxmdirty=!0,app.tempElm.replace=!0),e.target&&"select-p2"===e.target.id&&(app.tempElm.new.p2=e.target.value,ctxmdirty=!0,app.tempElm.replace=!0),e.target&&"select-ori-type"===e.target.id&&(app.tempElm.new.ori.type=e.target.value,ctxmdirty=!0,app.tempElm.replace=!0,doftypechanged||(doftypechanged=[]),doftypechanged.push("ori")),e.target&&"select-len-type"===e.target.id&&(app.tempElm.new.len.type=e.target.value,ctxmdirty=!0,app.tempElm.replace=!0,doftypechanged||(doftypechanged=[]),doftypechanged.push("len")),e.target&&"select-ori-ref"===e.target.id&&(app.tempElm.new.ori.ref=e.target.value,ctxmdirty=!0,app.tempElm.replace=!0),e.target&&"select-len-ref"===e.target.id&&(app.tempElm.new.len.ref=e.target.value,ctxmdirty=!0,app.tempElm.replace=!0),e.target&&"select-force-node"===e.target.id&&(console.log(e.target),app.model.loadById(app.tempElm.old.id).p=app.model.nodeById(e.target.value),app.notify("render"),ctxmdirty=!0),e.target&&"select-force-mode"===e.target.id&&(app.model.loadById(app.tempElm.old.id).mode=e.target.value,app.notify("render"),ctxmdirty=!0),ctxmdirty&&app.updateCtxm(app.tempElm.new,app.tempElm.type,doftypechanged)}})},modalShown:id=>{document.getElementById(id).addEventListener("shown.bs.modal",e=>{app.jsonEditor.setValue(app.model.asJSON())})},modalAccept:id=>{document.getElementById(id).addEventListener("click",e=>{let newmodel;try{newmodel=JSON.parse(app.jsonEditor.getValue())}catch(error){alert(`Your JSON code is not valid! \n\n${error}`)}newmodel&&(app.model=newmodel,app.init())})},resize:()=>{window.onresize=(e=>{let c=document.getElementById("canvas"),main=document.getElementById("main");c.width=main.clientWidth,c.height=main.clientHeight-30;let rangewidth=(c.width-350)/2;for(const drive in app.model.inputs)document.getElementById(app.model.inputs[drive]).slider.style.width=`${rangewidth}px`;app.notify("render")})},viewModalChange:id=>{document.getElementById(id).addEventListener("change",e=>{let skipUpdate=!0;e.target&&"input-view-id"===e.target.id&&app.updateTempElmNew("id",e.target.value),e.target&&"select-view-type"===e.target.id&&(app.updateTempElmNew("type",e.target.value),skipUpdate=!1),e.target&&"select-view-p"===e.target.id&&app.updateTempElmNew("p",e.target.value),e.target&&"select-view-elem"===e.target.id&&(app.updateTempElmNew("elem",e.target.value),skipUpdate=!1),e.target&&"select-view-value"===e.target.id&&app.updateTempElmNew("value",e.target.value),e.target&&"view-stroke-color"===e.target.id&&app.updateTempElmNew("stroke",e.target.value),e.target&&"view-fill-color"===e.target.id&&app.updateTempElmNew("fill",e.target.value),skipUpdate||(app.viewModal.setContent(tmpl.viewModal()),document.getElementById("view-fill-color-btn")&&(document.getElementById("view-fill-color-btn").style.backgroundColor=document.getElementById("view-fill-color").disabled?"transparent":"#e9ecef"),app.viewModal.update())})},viewModalClick:id=>{document.getElementById(id).addEventListener("click",e=>{e.target&&"view-accept"===e.target.id&&app.addViewFromModal(),e.target&&"view-fill-color-btn"===e.target.id&&app.toggleViewFill()})},viewModalHide:id=>{document.getElementById(id).addEventListener("hide.bs.modal",e=>{app.resetApp()})}},examples={crankrocker:{id:"crank-rocker",gravity:!1,nodes:[{id:"A0",x:100,y:100,base:!0},{id:"A",x:100,y:150},{id:"B",x:350,y:220},{id:"B0",x:300,y:100,base:!0},{id:"C",x:250,y:320,m:1}],constraints:[{id:"a",p1:"A0",p2:"A",len:{type:"const"}},{id:"b",p1:"A",p2:"B",len:{type:"const"}},{id:"c",p1:"B0",p2:"B",len:{type:"const"}},{id:"d",p1:"B",p2:"C",ori:{type:"ref",ref:"b"},len:{type:"const"}}],views:[{id:"view1",type:"trace",p:"C",fill:"rgba(255,235,13,.5)"},{id:"ia",type:"info",elem:"a",value:"w"}]},slidercrank:{id:"slider-crank",gravity:!1,nodes:[{id:"A0",x:100,y:100,base:!0},{id:"A",x:100,y:170},{id:"B",x:350,y:80},{id:"B0",x:500,y:80,base:!0}],constraints:[{id:"a",p1:"A0",p2:"A",len:{type:"const"},ori:{type:"drive",Dw:2*Math.PI,input:!0}},{id:"b",p1:"A",p2:"B",len:{type:"const"}},{id:"c",p1:"B0",p2:"B",ori:{type:"const"}}]},"7r":{id:"Stephenson-II",gravity:!1,nodes:[{id:"A0",x:100,y:100,base:!0},{id:"A",x:100,y:200},{id:"B1",x:350,y:200},{id:"B2",x:450,y:200},{id:"C1",x:350,y:150},{id:"C2",x:450,y:150},{id:"C0",x:400,y:100,base:!0}],constraints:[{id:"a",p1:"A0",p2:"A",len:{type:"const"}},{id:"a1",p1:"A",p2:"B1",len:{type:"const"}},{id:"a2",p1:"B1",p2:"B2",len:{type:"const"},ori:{type:"ref",ref:"a1"}},{id:"b1",p1:"B1",p2:"C2",len:{type:"const"}},{id:"b2",p1:"B2",p2:"C1",len:{type:"const"}},{id:"c1",p1:"C0",p2:"C1",len:{type:"const"}},{id:"c2",p1:"C0",p2:"C2",len:{type:"const"}},{id:"c3",p1:"C1",p2:"C2",len:{type:"const"}}]},"9r":{id:"9r",gravity:!1,nodes:[{id:"A0",x:100,y:100,base:!0},{id:"A",x:100,y:175},{id:"B",x:400,y:175},{id:"B0",x:400,y:100,base:!0},{id:"C",x:175,y:350},{id:"C0",x:250,y:400,base:!0},{id:"D",x:200,y:200},{id:"E",x:300,y:200},{id:"F",x:250,y:300}],constraints:[{id:"a",p1:"A0",p2:"A",len:{type:"const"}},{id:"b",p1:"B0",p2:"B",len:{type:"const"}},{id:"c",p1:"C0",p2:"C",len:{type:"const"}},{id:"d",p1:"A",p2:"D",len:{type:"const"}},{id:"e",p1:"B",p2:"E",len:{type:"const"}},{id:"f",p1:"C",p2:"F",len:{type:"const"}},{id:"g",p1:"D",p2:"E",len:{type:"const"}},{id:"h",p1:"E",p2:"F",len:{type:"const"}},{id:"i",p1:"F",p2:"D",len:{type:"const"}}]},basictruss:{id:"truss-basic",gravity:!1,nodes:[{id:"A",x:100,y:100,base:!0},{id:"B",x:200,y:100,base:!0},{id:"C",x:150,y:200}],constraints:[{id:"a",p1:"A",p2:"C",len:{type:"const"}},{id:"b",p1:"B",p2:"C",len:{type:"const"}}],loads:[{type:"force",id:"F",p:"C",mode:"push",value:2e3}],views:[{id:"F1",type:"info",elem:"a",value:"forceAbs"},{id:"F2",type:"info",elem:"b",value:"forceAbs"}]},truss:{id:"truss",gravity:!0,nodes:[{id:"A",x:100,y:100,base:!0},{id:"B",x:200,y:100},{id:"C",x:300,y:100},{id:"D",x:400,y:100},{id:"E",x:500,y:100,base:!0},{id:"G",x:150,y:200},{id:"H",x:250,y:200},{id:"I",x:350,y:200},{id:"J",x:450,y:200}],constraints:[{id:"a",p1:"A",p2:"B",len:{type:"const"}},{id:"b",p1:"B",p2:"C",len:{type:"const"}},{id:"c",p1:"C",p2:"D",len:{type:"const"}},{id:"d",p1:"D",p2:"E",len:{type:"const"}},{id:"e",p1:"A",p2:"G",len:{type:"const"}},{id:"f",p1:"G",p2:"B",len:{type:"const"}},{id:"g",p1:"B",p2:"H",len:{type:"const"}},{id:"h",p1:"H",p2:"C",len:{type:"const"}},{id:"i",p1:"C",p2:"I",len:{type:"const"}},{id:"j",p1:"I",p2:"D",len:{type:"const"}},{id:"k",p1:"D",p2:"J",len:{type:"const"}},{id:"l",p1:"J",p2:"E",len:{type:"const"}},{id:"m",p1:"G",p2:"H",len:{type:"const"}},{id:"n",p1:"H",p2:"I",len:{type:"const"}},{id:"o",p1:"I",p2:"J",len:{type:"const"}}],loads:[{type:"force",id:"F",p:"H",mode:"push",value:2e3,w0:-Math.PI/2}],views:[{id:"F1",type:"info",elem:"a",value:"forceAbs"},{id:"F2",type:"info",elem:"b",value:"forceAbs"},{id:"F3",type:"info",elem:"c",value:"forceAbs"},{id:"F4",type:"info",elem:"d",value:"forceAbs"},{id:"F5",type:"info",elem:"e",value:"forceAbs"},{id:"F6",type:"info",elem:"f",value:"forceAbs"},{id:"F7",type:"info",elem:"g",value:"forceAbs"},{id:"F8",type:"info",elem:"h",value:"forceAbs"},{id:"F9",type:"info",elem:"i",value:"forceAbs"},{id:"F10",type:"info",elem:"j",value:"forceAbs"},{id:"F11",type:"info",elem:"k",value:"forceAbs"},{id:"F12",type:"info",elem:"l",value:"forceAbs"},{id:"F13",type:"info",elem:"m",value:"forceAbs"},{id:"F14",type:"info",elem:"n",value:"forceAbs"},{id:"F15",type:"info",elem:"o",value:"forceAbs"}]},pendulums:{id:"chaos-pendulums",gravity:!0,nodes:[{id:"A0",x:200,y:400,base:!0},{id:"A1",x:280,y:480,m:5},{id:"A2",x:360,y:560,m:4},{id:"A3",x:440,y:640,m:2},{id:"B1",x:280,y:480,m:5},{id:"B2",x:360,y:560,m:4},{id:"B3",x:440,y:640,m:2.01},{id:"C1",x:280,y:480,m:5},{id:"C2",x:360,y:560,m:4},{id:"C3",x:440,y:640,m:1.99},{id:"D1",x:280,y:480,m:5},{id:"D2",x:360,y:560,m:4.01},{id:"D3",x:440,y:640,m:1.99}],constraints:[{id:"a1",p1:"A0",p2:"A1",len:{type:"const"}},{id:"a2",p1:"A1",p2:"A2",len:{type:"const"}},{id:"a3",p1:"A2",p2:"A3",len:{type:"const"}},{id:"b1",p1:"A0",p2:"B1",len:{type:"const"}},{id:"b2",p1:"B1",p2:"B2",len:{type:"const"}},{id:"b3",p1:"B2",p2:"B3",len:{type:"const"}},{id:"c1",p1:"A0",p2:"C1",len:{type:"const"}},{id:"c2",p1:"C1",p2:"C2",len:{type:"const"}},{id:"c3",p1:"C2",p2:"C3",len:{type:"const"}},{id:"d1",p1:"A0",p2:"D1",len:{type:"const"}},{id:"d2",p1:"D1",p2:"D2",len:{type:"const"}},{id:"d3",p1:"D2",p2:"D3",len:{type:"const"}}],views:[{id:"view1",type:"trace",p:"A3",stroke:"rgba(255,0,0,.5)"},{id:"view2",type:"trace",p:"B3",stroke:"rgba(0,255,0,.5)"},{id:"view3",type:"trace",p:"C3",stroke:"rgba(255,255,0,.5)"},{id:"view4",type:"trace",p:"D3",stroke:"rgba(255,0,255,.5)"}]}},tooltip=document.getElementById("info"),actcontainer=document.getElementById("actuators-container"),runSymbol=document.getElementById("run-symbol"),statusbar=document.getElementById("statbar"),sbMode=document.getElementById("sbMode"),sbCoords=document.getElementById("sbCoords"),sbCartesian=document.getElementById("sbCartesian"),sbBtn=document.getElementById("sbBtn"),sbDbtn=document.getElementById("sbDbtn"),sbFPS=document.getElementById("sbFPS"),sbState=document.getElementById("sbState"),sbDragging=document.getElementById("sbDragging"),sbDragmode=document.getElementById("sbDragmode"),sbDOF=document.getElementById("sbDOF"),sbGravity=document.getElementById("sbGravity"),editor=g2.editor(),pi=Math.PI,svgplay="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z",svgpause="M144 479H48c-26.5 0-48-21.5-48-48V79c0-26.5 21.5-48 48-48h96c26.5 0 48 21.5 48 48v352c0 26.5-21.5 48-48 48zm304-48V79c0-26.5-21.5-48-48-48h-96c-26.5 0-48 21.5-48 48v352c0 26.5 21.5 48 48 48h96c26.5 0 48-21.5 48-48z",origin=g2().beg({lc:"round",lj:"round",ls:()=>mec.darkmode?"silver":"slategray",fs:"darkgray"}).p().m({x:21,y:0}).l({x:0,y:0}).l({x:0,y:21}).stroke().p().m({x:35,y:0}).l({x:21,y:-2.6}).a({dw:pi/3,x:21,y:2.6}).z().m({x:0,y:35}).l({x:2.6,y:21}).a({dw:pi/3,x:-2.6,y:21}).z().drw().cir({x:0,y:0,r:2.5,fs:"#ccc"}).end().beg({ls:()=>mec.darkmode?"silver":"slategray",font:"14px roboto"}).txt({str:"x",x:38,y:4}).txt({str:"y",x:6,y:30}).end(),gravvec=(cartesian=!0)=>{const ytxt=cartesian?-20:-15;return g2().beg({w:-pi/2,lw:2,ls:()=>mec.darkmode?"silver":"slategray",fs:"darkgray"}).p().m({x:0,y:0}).l({x:50,y:0}).stroke().p().m({x:50,y:0}).l({x:32.5,y:-3.5}).a({dw:pi/3,x:32.5,y:3.5}).z().drw().end().beg({ls:()=>mec.darkmode?"silver":"slategray",font:"14px roboto"}).txt({str:"g",x:-15,y:ytxt}).end()},App={create(){const o=Object.create(this.prototype);return o.constructor.apply(o,arguments),o},prototype:Object.assign({constructor(){this.model={id:"linkage"},this.VERSION="0.6.1",this.evt={dx:0,dy:0,dbtn:0},this.view={x:150,y:150,scl:1,cartesian:!0},this.cnv=document.getElementById("canvas"),this.ctx=this.cnv.getContext("2d"),this.instruct=document.getElementById("instructions"),this.ctxmenu=document.getElementById("contextMenu"),this.ctxmenuheader=document.getElementById("contextMenuHeader"),this.ctxmenubody=document.getElementById("contextMenuBody"),this.build=!1,this.tempElm=!1,this.devmode=!1,this.importConfirmed=!1,this.dragMove=!0,this.nodeInfoValues=["acc","accAbs","vel","velAbs","force","forceAbs"],this.constraintInfoValues=["w","wt","wtt","r","rt","rtt","forceAbs","moment"],this.nodeVectorValues=["acc","vel","force"],this.g=g2(),this.registerEventsFor(this.ctx.canvas).on(["pointer","drag","buttondown","buttonup","click"],e=>{this.g.exe(editor.on(this.pntToUsr(Object.assign({},e)))).exe(this.ctx)}).on(["pointer","drag","pan","fps","buttondown","buttonup","click","pointerenter","pointerleave"],()=>this.showStatus()).on("drag",e=>{this.dragMove||(editor.curElm.x0=editor.curElm.x,editor.curElm.y0=editor.curElm.y),this.showTooltip(e)}).on("pan",e=>{this.pan(e),this.g.exe(this.ctx)}).on("pointer",e=>this.showTooltip(e)).on(["buttonup","click"],()=>this.hideTooltip()).on("buttondown",()=>{this.build&&(["addnode","addbasenode"].includes(this.build.mode)&&this.addNode(),"purgeelement"===this.build.mode&&this.purgeElement(editor.curElm),["free","tran","rot"].includes(this.build.mode)&&this.addConstraint(),"drive"===this.build.mode&&this.addDrive(editor.curElm),"force"===this.build.mode&&this.addForce(),"spring"===this.build.mode&&this.addSpring(),["fix","flt"].includes(this.build.mode)&&this.addSupportShape())}).on("render",()=>this.g.exe(this.ctx)).on("tick",e=>this.tick(e)),this.state="created"},get cartesian(){return this.view.cartesian},get height(){return this.ctx.canvas.height},get dragging(){return!!(editor.curState&g2.DRAG)},showStatus(){let{x:x,y:y}=this.pntToUsr({x:this.evt.x,y:this.evt.y});sbCoords.innerHTML=`x=${x}, y=${y}`,sbDragmode.innerHTML=`dragmode=${this.dragMove?"move":"edit"}`,sbDOF.innerHTML=`dof=${this.model.dof}`,sbFPS.innerHTML=`fps=${this.fps}`,this.devmode&&(sbGravity.innerHTML=`gravity=${this.model.hasGravity?"on":"off"}`,sbMode.innerHTML=`mode=${this.evt.type}`,sbCartesian.innerHTML=`cartesian=${this.cartesian}`,sbBtn.innerHTML=`btn=${this.evt.btn}`,sbDbtn.innerHTML=`dbtn=${this.evt.dbtn}`,sbState.innerHTML=`state=${g2.editor.state[editor.curState]}`,sbDragging.innerHTML=`dragging=${this.dragging}`)},showTooltip(e){const info=this.model.info;tooltip.style.left=e.clientX+15+"px",tooltip.style.top=e.clientY-50+"px",editor.dragInfo&&!this.dragMove?(tooltip.innerHTML=editor.dragInfo,tooltip.style.display="inline"):info&&this.dragMove?(tooltip.innerHTML=info,tooltip.style.display="inline"):this.hideTooltip()},hideTooltip(){tooltip.style.display="none"},tick(e){this.model&&(this.dragging?this.dragMove?this.model.pose():this.updDependants(editor.curElm):"active"===this.state?(this.model.tick(e.dt),this.model.isActive||this.stop()):"input"===this.state&&this.model.tick(0),this.g.exe(this.ctx))},init(){for(mec.model.extend(this.model),this.model.init().asmPos(),this.updateg(),this.model.inputs=[];actcontainer.lastChild;)actcontainer.removeChild(actcontainer.lastChild);let drv,prv=!1;for(;drv=this.driveByInput(prv);){let id=drv.constraint.id+"-"+drv.value,max="ori"===drv.value?Math.round(180*drv.constraint.ori.Dw/pi):Math.round(drv.constraint.len.Dr);actcontainer.appendChild(this.createInputSlider(id,(this.cnv.width-150)/2,max));let elm=document.getElementById(id);mecESlider.RegisterElm(elm),console.log(prv),elm.initEventHandling(this,id,this.model.constraintById(drv.constraint.id)[drv.value].inputCallbk),this.model.inputs.push(id),prv=drv}"undefined"!=typeof t&&null!==t||this.startTimer(),this.state=this.model.inputs.length>0?"input":"initialized"},run(){this.state="active",runSymbol.setAttribute("d",svgpause)},idle(){this.state=this.model.inputs.length>0?"input":"idle",runSymbol.setAttribute("d",svgplay)},stop(){this.model.stop(),this.idle()},reset(){this.model.reset(),this.model.asmPos();for(const drive in this.model.inputs){let ident=this.model.inputs[drive].split("-");this.model.constraintById(ident[0])[ident[1]].inputCallbk({target:{value:0}}),document.getElementById(ident[0]+"-"+ident[1]).value=0,this.notify(ident[0]+"-"+ident[1],0)}this.notify("render"),this.idle()},updDependants(elm){let dependants=[];for(const constraint of this.model.constraints)constraint.dependsOn(elm)&&dependants.push(constraint);dependants.forEach(el=>{el.init(this.model),"ctrl"!==el.type||"drive"!==el.ori.type&&"drive"!==el.len.type||(el.ori.repeat&&(el.ori.Dt/=el.ori.repeat),el.len.repeat&&(el.len.Dt/=el.len.repeat))})},toggleDevmode(){this.devmode=!this.devmode,this.devmode||(sbGravity.innerHTML=sbMode.innerHTML=sbCartesian.innerHTML=sbBtn.innerHTML=sbDbtn.innerHTML=sbState.innerHTML=sbDragging.innerHTML=""),this.showStatus()},toggleDarkmode(){mec.darkmode=!mec.darkmode,this.jsonEditor.setOption("theme",`${mec.darkmode?"lucario":"mdn-like"}`),this.cnv.style.backgroundColor=mec.darkmode?"#344c6b":"rgb(250, 253, 242)",this.notify("render")},resetView(){this.view.x=150,this.view.y=150,this.view.scl=1,this.view.cartesian=!0,this.notify("render")},createInputSlider(actuated,width,max){let template=document.createElement("template");return template.innerHTML=``,template.content.firstChild},updateg(){let apphasmodel=!("object"!=typeof this.model||!Object.keys(this.model).length);this.g=g2().clr().view(this.view).grid({color:()=>mec.darkmode?"rgba(255, 255, 255, 0.1)":"rgba(0, 0, 0, 0.1)",size:100}).grid({color:()=>mec.darkmode?"rgba(255, 255, 255, 0.1)":"rgba(0, 0, 0, 0.1)",size:20}).p().m({x:()=>-this.view.x/this.view.scl,y:0}).l({x:()=>(this.cnv.width-this.view.x)/this.view.scl,y:0}).m({x:0,y:()=>-this.view.y/this.view.scl}).l({x:0,y:()=>(this.cnv.height-this.view.y)/this.view.scl}).z().stroke({ls:()=>mec.darkmode?"rgba(255, 255, 255, 0.3)":"rgba(0, 0, 0, 0.2)",lw:2}).use({grp:origin,x:()=>(10-this.view.x)/this.view.scl,y:()=>(10-this.view.y)/this.view.scl,scl:()=>this.view.scl}),apphasmodel&&this.model.hasGravity&&(this.cartesian?this.g.use({grp:gravvec(!0),x:()=>(this.cnv.width-15-this.view.x)/this.view.scl,y:()=>(this.cnv.height-15-this.view.y)/this.view.scl,scl:()=>this.view.scl}):this.g.use({grp:gravvec(!1),x:()=>(this.cnv.width-15-this.view.x)/this.view.scl,y:()=>(69-this.view.y)/this.view.scl,scl:()=>this.view.scl})),apphasmodel&&this.model.draw(this.g),this.notify("render")},resetApp(){this.build=!1,this.tempElm=!1,this.instruct.innerHTML="",this.notify("render")},addNode(){if(void 0===editor.curElm||!editor.curElm.hasOwnProperty("m")){{let{x:x,y:y}=this.pntToUsr({x:this.evt.x,y:this.evt.y}),node={id:this.getNewChar(),x:x,y:y};"addbasenode"===this.build.mode&&(node.base=!0),this.model.addNode(mec.node.extend(node)),node.init(this.model),this.updateg()}this.build.continue||(document.body.style.cursor="default",this.resetApp())}},removeInput(id){for(let dof of["-len","-ori"])this.model.inputs.includes(id+dof)&&(actcontainer.removeChild(document.getElementById(id+dof)),this.model.inputs.splice(this.model.inputs.findIndex(el=>el.id===id),1))},purgeElement(elem){if(elem){if(["node","ctrl"].includes(elem.type)){let dependants=this.model.dependentsOf(elem).constraints;if(dependants.length>0)for(let dep of dependants)(this.model.inputs.includes(dep.id+"-ori")||this.model.inputs.includes(dep.id+"-len"))&&this.removeInput(dep.id)}if("node"===elem.type)this.model.purgeNode(elem);else if(["free","tran","rot","ctrl"].includes(elem.type))(this.model.inputs.includes(elem.id+"-ori")||this.model.inputs.includes(elem.id+"-len"))&&this.removeInput(elem.id),this.model.purgeConstraint(elem);else if(["force","spring"].includes(elem.type))this.model.purgeLoad(elem);else{if(!["vector","trace","info"].includes(elem.type))return;this.model.purgeView(elem)}this.updateg(),document.body.style.cursor="default",this.resetApp()}},replaceConstraint(oldC,newC){this.reset();let rebindorilistener=!1,rebindlenlistener=!1,oridrv=!1,lendrv=!1;if("drive"===newC.ori.type&&(oridrv={newC:newC,value:"ori"}),"drive"===newC.len.type&&(lendrv={newC:newC,value:"len"}),oldC.ori&&oldC.ori.input&&document.getElementById(oldC.id+"-ori")&&(document.getElementById(oldC.id+"-ori").removeEventListener("input",this.model.constraintById(oldC.id).ori.inputCallbk,!1),newC.ori.input&&(rebindorilistener=!0)),oldC.len&&oldC.len.input&&document.getElementById(oldC.id+"-len")&&(document.getElementById(oldC.id+"-len").removeEventListener("input",this.model.constraintById(oldC.id).len.inputCallbk,!1),newC.len.input&&(rebindlenlistener=!0)),this.model.constraints.splice(this.model.constraints.indexOf(this.model.constraintById(oldC.id)),1),this.model.addConstraint(mec.constraint.extend(newC)),newC.init(this.model),this.updateg(),this.model.pose(),lendrv&&!this.model.inputs.includes(newC.id+"-len")||oridrv&&!this.model.inputs.includes(newC.id+"-ori")){let drv,prv=!1;for(;drv=this.driveByInput(prv);){let id=drv.constraint.id+"-"+drv.value,max="ori"===drv.value?Math.round(180*drv.constraint.ori.Dw/pi):Math.round(drv.constraint.len.Dr);actcontainer.appendChild(this.createInputSlider(id,(this.cnv.width-150)/2,max));let elm=document.getElementById(id);mecESlider.RegisterElm(elm),elm.initEventHandling(this,id,this.model.constraintById(drv.constraint.id)[drv.value].inputCallbk),this.model.inputs.push(id),prv=drv}window.dispatchEvent(new Event("resize"))}else!newC.ori.input&&this.model.inputs.includes(newC.id+"-ori")?(actcontainer.removeChild(document.getElementById(newC.id+"-ori")),this.model.inputs.splice(this.model.inputs.findIndex(el=>el.id===newC.id),1)):!newC.len.input&&this.model.inputs.includes(newC.id+"-len")&&(actcontainer.removeChild(document.getElementById(newC.id+"-len")),this.model.inputs.splice(this.model.inputs.findIndex(el=>el.id===newC.id),1));if(oldC.ori&&oldC.ori.Dw&&newC.ori&&newC.ori.Dw&&oldC.ori.Dw!==newC.ori.Dw){let mecslider=document.getElementById(newC.id+"-ori");mecslider.max=mecslider.children[1].max=`${Math.round(180*newC.ori.Dw/Math.PI)}`}if(oldC.len&&oldC.len.Dr&&newC.len&&newC.len.Dr&&oldC.len.Dr!==newC.len.Dr){let mecslider=document.getElementById(newC.id+"-len");mecslider.max=mecslider.children[1].max=`${Math.round(newC.len.Dr)}`}rebindorilistener&&document.getElementById(oridrv.newC.id+"-ori").initEventHandling(this,oridrv.newC.id,this.model.constraintById(oridrv.newC.id)[oridrv.value].inputCallbk),rebindlenlistener&&document.getElementById(lendrv.newC.id+"-len").initEventHandling(this,lendrv.newC.id,this.model.constraintById(lendrv.newC.id)[lendrv.value].inputCallbk),this.state=this.model.inputs.length>0?"input":"initialized"},driveByInput(prev=!1){let found=!1,start=!prev;for(const constraint of this.model.constraints)if(constraint.ori&&"drive"===constraint.ori.type&&constraint.ori.input&&(start?this.model.inputs.includes(constraint.id+"-ori")||(found={constraint:constraint,value:"ori"}):start=constraint.ori===prev.constraint.ori),constraint.len&&"drive"===constraint.len.type&&constraint.len.input&&!found&&(start?this.model.inputs.includes(constraint.id+"-len")||(found={constraint:constraint,value:"len"}):start=constraint.len===prev.constraint.len),found)return found;return!1},addConstraint(){if(this.build.firstnode){if(!editor.curElm||!editor.curElm.hasOwnProperty("m"))return;{if(editor.curElm.id===this.build.firstnode.id)return this.instruct.classList.add("blink"),void setTimeout(()=>{this.instruct.classList.remove("blink")},1400);let tmplen=!1,tmpori=!1;switch(this.build.mode){case"free":break;case"tran":tmpori={type:"const"};break;case"rot":tmplen={type:"const"};break;default:console.log("something went wrong while adding constraint...")}let constraint={id:this.getNewChar("constraint"),p1:this.build.firstnode.id,p2:editor.curElm.id};tmplen&&(constraint.len=tmplen),tmpori&&(constraint.ori=tmpori),this.model.addConstraint(mec.constraint.extend(constraint)),constraint.init(this.model),this.updateg()}this.build.continue?(delete this.build.firstnode,this.instruct.innerHTML="Select first node; [ESC] to cancel"):this.resetApp()}else{if(!editor.curElm||!editor.curElm.hasOwnProperty("m"))return;this.build.firstnode=editor.curElm,this.instruct.innerHTML="Select second node; [ESC] to cancel"}},getNewChar(x="node"){let name,obj,maxChar,char,charArr=[];if("node"===x&&this.model.nodes.length>0||"constraint"===x&&this.model.constraints.length>0){"node"===x?(obj=this.model.nodes,name="node",maxChar=90):(obj=this.model.constraints,name="constraint",maxChar=122);for(let i=0;i{this.instruct.innerHTML="Select a constraint to add a drive to; [ESC] to cancel"},2400)}},addSupportShape(){if(editor.curElm&&editor.curElm.hasOwnProperty("m")){{let shape={type:this.build.mode,p:editor.curElm.id};this.model.addShape(mec.shape.extend(shape)),shape.init(this.model),this.updateg()}document.body.style.cursor="default",this.resetApp()}},addForce(){if(editor.curElm&&editor.curElm.hasOwnProperty("m")){{let i=0;this.model.loads.forEach(load=>{"force"===load.type&&i++}),i+=1;let force={type:this.build.mode,id:`F${i}`,p:editor.curElm.id};this.model.addLoad(mec.load.extend(force)),force.init(this.model),this.updateg()}document.body.style.cursor="default",this.resetApp()}},addSpring(){if(this.build.firstnode){if(!editor.curElm||!editor.curElm.hasOwnProperty("m"))return;{let i=0;this.model.loads.forEach(load=>{"spring"===load.type&&i++}),i+=1;let spring={type:this.build.mode,id:`S${i}`,p1:this.build.firstnode.id,p2:editor.curElm.id};this.model.addLoad(mec.load.extend(spring)),spring.init(this.model),this.updateg()}this.resetApp()}else{if(!editor.curElm||!editor.curElm.hasOwnProperty("m"))return;this.build.firstnode=editor.curElm,this.instruct.innerHTML="Select second node; [ESC] to cancel"}},initViewModal(){this.tempElm||(this.tempElm={new:{id:"",type:"trace"}}),this.viewModal.setContent(tmpl.viewModal()),document.getElementById("view-fill-color-btn").style.backgroundColor="transparent",this.viewModal.show()},addViewFromModal(){0===this.tempElm.new.id.length&&(this.tempElm.new.id=`view${this.model.views.length+1}`),this.model.addView(mec.view.extend(this.tempElm.new)),this.tempElm.new.init(this.model),["trace","vector"].includes(this.tempElm.new.type)&&this.updateg(),this.resetApp(),this.viewModal.hide()},initCtxm(elm){this.tempElm={},this.tempElm.replace=!1,this.tempElm.type=["free","rot","tran","ctrl"].includes(elm.type)?"constraint":elm.type,this.tempElm.old=JSON.parse(elm.asJSON()),this.tempElm.new=JSON.parse(elm.asJSON()),this.tempElm.new.ori||(this.tempElm.new.ori={type:"free"}),this.tempElm.new.len||(this.tempElm.new.len={type:"free"}),this.tempElm.labelState={nodes:this.model.graphics.labels.nodes,constraints:this.model.graphics.labels.constraints,loads:this.model.graphics.labels.loads},this.model.graphics.labels.nodes||(this.model.graphics.labels.nodes=!0),this.model.graphics.labels.constraints||(this.model.graphics.labels.constraints=!0),this.model.graphics.labels.loads||(this.model.graphics.labels.loads=!0),this.notify("render"),this.updateCtxm(this.tempElm.old,this.tempElm.type),this.showCtxm()},showCtxm(){this.ctxmenu.style.display="block",this.ctxmenu.style.left=`${this.evt.clientX}px`,this.ctxmenu.style.top=`${this.evt.clientY}px`},hideCtxm(){for(this.ctxmenu.style.display="none",this.tempElm.new&&this.tempElm.replace&&"constraint"===this.tempElm.type&&this.replaceConstraint(this.tempElm.old,this.tempElm.new),this.tempElm.labelState.nodes!==this.model.graphics.labels.nodes&&(this.model.graphics.labels.nodes=this.tempElm.labelState.nodes),this.tempElm.labelState.constraints!==this.model.graphics.labels.constraints&&(this.model.graphics.labels.constraints=this.tempElm.labelState.constraints),this.tempElm.labelState.loads!==this.model.graphics.labels.loads&&(this.model.graphics.labels.loads=this.tempElm.labelState.loads);this.ctxmenubody.lastChild;)this.ctxmenubody.removeChild(this.ctxmenubody.lastChild);this.tempElm=!1},updateCtxm(elm,type,doftypechanged=!1){if(doftypechanged)for(let dof of doftypechanged)if(elm[dof]){if("free"===elm[dof].type&&(elm[dof]={type:"free"}),"const"===elm[dof].type&&(elm[dof]={type:"const"}),"ref"===elm[dof].type){for(let prop of["Dt","Dw","Dr","input","bounce","repeat","func","ratio","t0"])elm[dof].hasOwnProperty(prop)&&delete elm[dof][prop];elm[dof].ref=this.model.constraints[0].id}if("drive"===elm[dof].type)for(let prop of["ref","refval"])elm[dof].hasOwnProperty(prop)&&delete elm[dof][prop]}for(;this.ctxmenubody.lastChild;)this.ctxmenubody.removeChild(this.ctxmenubody.lastChild);this.ctxmenuheader.innerHTML=tmpl.header(elm,type),"constraint"===type&&(this.ctxmenubody.innerHTML+=tmpl.sectionTitle("orientation"),this.ctxmenubody.innerHTML+=tmpl.oriType(elm),elm.ori&&"drive"===elm.ori.type&&(this.tempElm.new.ori.hasOwnProperty("Dt")||(this.tempElm.new.ori.Dt=1),this.ctxmenubody.innerHTML+=tmpl.Dt(elm,"ori"),this.tempElm.new.ori.hasOwnProperty("Dw")||(this.tempElm.new.ori.Dw=2*pi),this.ctxmenubody.innerHTML+=tmpl.Dw(elm,"ori")),elm.ori&&"ref"===elm.ori.type&&(this.ctxmenubody.innerHTML+=tmpl.ref(elm,"ori",elm.ori.ref)),this.ctxmenubody.innerHTML+=tmpl.sectionTitle("length"),this.ctxmenubody.innerHTML+=tmpl.lenType(elm),elm.len&&"drive"===elm.len.type&&(this.tempElm.new.len.hasOwnProperty("Dt")||(this.tempElm.new.len.Dt=1),this.ctxmenubody.innerHTML+=tmpl.Dt(elm,"len"),this.tempElm.new.len.hasOwnProperty("Dr")||(this.tempElm.new.len.Dr=100),this.ctxmenubody.innerHTML+=tmpl.Dr(elm,"len")),elm.len&&"ref"===elm.len.type&&(this.ctxmenubody.innerHTML+=tmpl.ref(elm,"len",elm.len.ref)),this.ctxmenubody.innerHTML+=tmpl.sectionTitle("nodes"),this.ctxmenubody.innerHTML+=tmpl.nodes(elm),this.ctxmenubody.innerHTML+=tmpl.removeConstraintButton()),"node"===type&&(this.ctxmenubody.innerHTML+=tmpl.nodeCoordinates(elm),this.ctxmenubody.innerHTML+=tmpl.nodeBase(elm)),"force"===type&&(this.ctxmenubody.innerHTML+=tmpl.forceValue(elm),this.ctxmenubody.innerHTML+=tmpl.forceMode(elm),this.ctxmenubody.innerHTML+=tmpl.forceNode(elm)),"spring"===type&&(this.ctxmenubody.innerHTML+=tmpl.springNodes(elm),this.ctxmenubody.innerHTML+=tmpl.springLen(elm),this.ctxmenubody.innerHTML+=tmpl.springK(elm))},loadFromJSON(files){let model,file=files[0],fr=new FileReader;fr.onload=(()=>e=>{console.log(e.target),model=JSON.parse(e.target.result),this.newModel(model),this.importConfirmed=!1})(),fr.readAsText(file)},saveToJSON(){let a=document.createElement("a"),file=new Blob([this.model.asJSON()],{type:"application/json"});a.href=URL.createObjectURL(file),a.download=this.model.id&&this.model.id.length>0?`${this.model.id}.json`:"linkage.json",document.body.appendChild(a),a.click(),document.body.removeChild(a)},newModel(model={}){if("object"!=typeof this.model||this.importConfirmed||confirm("All unsaved changes will be lost! Continue?")){for(;actcontainer.lastChild;)actcontainer.removeChild(actcontainer.lastChild);delete this.model,this.model=model,this.init()}},updateTempElmNew(key,value){this.tempElm.new[key]=value},toggleViewFill(){let fill=document.getElementById("view-fill-color"),fillBtn=document.getElementById("view-fill-color-btn");fill.disabled=!fill.disabled,fillBtn.style.backgroundColor=fill.disabled?"transparent":"#e9ecef",fill.disabled&&this.tempElm.new.hasOwnProperty("fill")?delete this.tempElm.new.fill:fill.disabled||this.tempElm.new.hasOwnProperty("fill")||(this.tempElm.new.fill="#009900")}},mixin.observable,mixin.pointerEventHdl,mixin.tickTimer,mixin.zoomPan)};let app;window.onload=(()=>{(app=App.create()).init(),app.modelModal=new Modal(document.getElementById("modelModal"),{backdrop:"static",keyboard:!0}),app.viewModal=new Modal(document.getElementById("viewModal"),{backdrop:"static"}),app.jsonEditor=CodeMirror.fromTextArea(document.getElementById("modalTextarea"),{mode:"javascript",theme:"mdn-like",lineNumbers:!0,matchBrackets:!0,viewportMargin:1/0,lineWrapping:!1}),app.jsonEditor.on("drop",e=>{e.setValue("")}),new Draggabilly(document.getElementById("contextMenu"),{containment:".main-container",handle:".card-header"}),new Modal(document.getElementById("aboutModal"),{keyboard:!0,content:`
`}),new Modal(document.getElementById("keysModal"),{keyboard:!0}),events.navbarClick("navcollapse"),events.navbarChange("import"),events.sidebarClick("sb-l"),events.keyboardDown(),events.ctxmClick("contextMenu"),events.ctxmInput("contextMenu"),events.ctxmChange("contextMenu"),events.resize(),events.modalShown("modelModal"),events.modalAccept("modalAccept"),events.viewModalChange("viewModal"),events.viewModalClick("viewModal"),events.viewModalHide("viewModal"),app.toggleDarkmode(),window.dispatchEvent(new Event("resize"))});
\ No newline at end of file
diff --git a/app.min.js.gz b/app.min.js.gz
index df7d088..ffcea31 100644
Binary files a/app.min.js.gz and b/app.min.js.gz differ
diff --git a/docs/app.md b/docs/app.md
new file mode 100644
index 0000000..c83a892
--- /dev/null
+++ b/docs/app.md
@@ -0,0 +1,735 @@
+## Members
+
+
+
+
+
+## mecEdit
+This is the main file of mecEdit. You can find this app on [GitHub](https://github.com/jauhl/mecEdit).
+
+**Kind**: global variable
+**Requires**: module:examples.js, module:templates.js, module:appevents.js, module:g2.editor.js, module:mixin.js, module:slider.js, module:mec2.js, module:g2.js
+**Author**: Jan Uhlig
+**License**: MIT
+**Copyright**: Jan Uhlig 2018
+
+
+## tooltip : HTMLElement
+Container for inputs.
+
+**Kind**: global constant
+
+
+## actcontainer : HTMLElement
+Container for inputs.
+
+**Kind**: global constant
+
+
+## runSymbol : HTMLElement
+SVG path container for run button.
+
+**Kind**: global constant
+
+
+## statusbar : HTMLElement
+Statusbar container for statusbar.
+
+**Kind**: global constant
+
+
+## sbMode : HTMLElement
+Statusbar Statusbar container for dragmode.
+
+**Kind**: global constant
+
+
+## sbCoords : HTMLElement
+Statusbar container for coordinates.
+
+**Kind**: global constant
+
+
+## sbCartesian : HTMLElement
+Statusbar container for coordinate mode.
+
+**Kind**: global constant
+
+
+## sbBtn : HTMLElement
+Statusbar container for mouseevent property btn.
+
+**Kind**: global constant
+
+
+## sbDbtn : HTMLElement
+Statusbar container for mouseevent property dbtn.
+
+**Kind**: global constant
+
+
+## sbFPS : HTMLElement
+Statusbar container for frames per second.
+
+**Kind**: global constant
+
+
+## sbState : HTMLElement
+Statusbar container for g2.editor state.
+
+**Kind**: global constant
+
+
+## sbDragging : HTMLElement
+Statusbar container for g2.editor state `dragging`.
+
+**Kind**: global constant
+
+
+## sbDragmode : HTMLElement
+Statusbar container for `App.prototype.dragMove`.
+
+**Kind**: global constant
+
+
+## sbDOF : HTMLElement
+Statusbar container for `model.dof`.
+
+**Kind**: global constant
+
+
+## sbGravity : HTMLElement
+Statusbar container for `App.prototype.gravity`.
+
+**Kind**: global constant
+
+
+## editor : object
+g2.editor instance.
+
+**Kind**: global constant
+
+
+## pi : number
+Pi.
+
+**Kind**: global constant
+
+
+## svgplay : string
+SVG play symbol.
+
+**Kind**: global constant
+
+
+## svgpause : string
+SVG pause symbol.
+
+**Kind**: global constant
+
+
+## model : object
+The model.
+
+**Kind**: global constant
+
+
+## VERSION : string
+mecEdit version.
+
+**Kind**: global constant
+
+
+## evt : boolean
+mixin requirement.
+
+**Kind**: global constant
+
+
+## cartesian : boolean
+Evaluates used coordinate system.
+
+**Kind**: global constant
+
+
+## height : number
+Height of the canvas.
+
+**Kind**: global constant
+
+
+## dragging : booelan
+State of g2.editor. `true` if element is being dragged.
+
+**Kind**: global constant
+
+
+## origin() ⇒ object
+Returns a origin symbol as a g2-object.
+
+**Kind**: global function
+
+
+## gravvec() ⇒ object
+Returns a gravity vector as a g2-object.
+
+**Kind**: global function
+
+
+## constructor()
+Sets properties to parent object. Call with `apply()` and pass the parent.
+
+**Kind**: global function
+
+
+## showStatus()
+Updates the contents of the statusbar.
+
+**Kind**: global function
+
+
+## showTooltip()
+Shows the tooltip.
+
+**Kind**: global function
+
+
+## hideTooltip()
+Hides the tooltip.
+
+**Kind**: global function
+
+
+## tick(e)
+Reset the model, drive inputs and the app state.
+
+**Kind**: global function
+
+| Param | Type | Description |
+| --- | --- | --- |
+| e | object | Event or Object containing the timestep. |
+| e.dt | object | Timestep. |
+
+
+
+## init()
+Initializes the app.
+
+**Kind**: global function
+
+
+## run()
+Sets the model to `active`.
+
+**Kind**: global function
+
+
+## idle()
+Pauses the model and resets the app state.
+
+**Kind**: global function
+
+
+## stop()
+Stops the model and resets the app state.
+
+**Kind**: global function
+
+
+## reset()
+Reset the model, drive inputs and the app state.
+
+**Kind**: global function
+
+
+## updDependants(elm)
+Reinitializes all dependants of the passed element.
+
+**Kind**: global function
+
+| Param | Type | Description |
+| --- | --- | --- |
+| elm | object | Element whose dependants should be reinitialized. |
+
+
+
+## toggleDevmode()
+Toggle developer mode to show additional information in the statusbar.
+
+**Kind**: global function
+
+
+## toggleDarkmode()
+Switch between dark- and lightmode.
+
+**Kind**: global function
+
+
+## resetView()
+Reset `this.view` to its initial state.
+
+**Kind**: global function
+
+
+## createInputSlider(actuated, width, max) ⇒ HTMLElement
+Create a new HTML-container for drive inputs.
+
+**Kind**: global function
+**Returns**: HTMLElement - newC - Constraint replacing.
+
+| Param | Type | Description |
+| --- | --- | --- |
+| actuated | string | Id of the new HTML-container (e.g. `a-ori`). |
+| width | number | maximal width of the new HTML-container. |
+| max | number | max value of the new HTML-range-input. |
+
+
+
+## updateg()
+Builds and updates the g2-command-queue according to the model.
+
+**Kind**: global function
+
+
+## resetApp()
+Resets the app and its stateful variables.
+
+**Kind**: global function
+
+
+## addNode()
+Adds a new node to the model.
+
+**Kind**: global function
+
+
+## removeInput(id)
+Removes all inputs of a constraint.
+
+**Kind**: global function
+
+| Param | Type | Description |
+| --- | --- | --- |
+| id | string | Id of the constraint the input belongs to. |
+
+
+
+## purgeElement(elem)
+Removes the passed component and all its dependants.
+
+**Kind**: global function
+
+| Param | Type | Description |
+| --- | --- | --- |
+| elem | object | Element to be purged from the model. |
+
+
+
+## replaceConstraint(oldC, newC)
+Replaces an old Constraint with a new one.
+
+**Kind**: global function
+
+| Param | Type | Description |
+| --- | --- | --- |
+| oldC | object | Constraint to be replaced. |
+| newC | object | Constraint replacing. |
+
+
+
+## driveByInput([prev]) ⇒ object \| boolean
+Searches for drives with inputs.
+
+**Kind**: global function
+**Returns**: object \| boolean - Drive that was found or false if none was found.
+
+| Param | Type | Default | Description |
+| --- | --- | --- | --- |
+| [prev] | object \| boolean | false | Drive to start search from. |
+
+
+
+## addConstraint()
+Adds a new Constraint to `this.model`.
+
+**Kind**: global function
+
+
+## getNewChar([x]) ⇒ string
+Generates a unique id for nodes or constraints.
+
+**Kind**: global function
+**Returns**: string - Generated id.
+
+| Param | Type | Default | Description |
+| --- | --- | --- | --- |
+| [x] | string | "node" | Type of component to generate id for `['node','constraint']`. |
+
+
+
+## addDrive()
+Adds changes dofs of the passed constraint from type `free` to `drive`.
+
+**Kind**: global function
+
+| Type | Description |
+| --- | --- |
+| object | Constraint to add drive to. |
+
+
+
+## addSupportShape()
+Adds a new shape of type `['fix'|'flt']`.
+
+**Kind**: global function
+
+
+## addForce()
+Adds a new load component of type `force`.
+
+**Kind**: global function
+
+
+## addSpring()
+Adds a new load component of type `spring`.
+
+**Kind**: global function
+
+
+## initViewModal()
+Initializes and shows a modal to add view components.
+
+**Kind**: global function
+
+
+## addViewFromModal()
+Adds a view component from the template this.tempElm.new and hides the view modal.
+
+**Kind**: global function
+
+
+## initCtxm(elm)
+Handles opening of contextmenu.
+
+**Kind**: global function
+
+| Param | Type | Description |
+| --- | --- | --- |
+| elm | object | Element to show the contextmenu for. |
+
+
+
+## showCtxm()
+Shows the contextmenu at mouseposition.
+
+**Kind**: global function
+
+
+## hideCtxm()
+Handles closing of contextmenu.
+
+**Kind**: global function
+
+
+## updateCtxm(elm, type, [doftypechanged])
+Handles opening of contextmenu.
+
+**Kind**: global function
+
+| Param | Type | Default | Description |
+| --- | --- | --- | --- |
+| elm | object | | Element to show the contextmenu for. |
+| type | string | | Type of the element. |
+| [doftypechanged] | boolean | false | Flag in case the type of a dof changed from the last invocation. |
+
+
+
+## loadFromJSON(files)
+Imports a model from a `FileList`.
+
+**Kind**: global function
+
+| Param | Type | Description |
+| --- | --- | --- |
+| files | FileList | `FileList` with the model as the first element. |
+
+
+
+## saveToJSON()
+Opens a dialogue to download teh cuurent model as a JSON file.
+
+**Kind**: global function
+
+
+## newModel([model])
+Defines a new model.
+
+**Kind**: global function
+
+| Param | Type | Default | Description |
+| --- | --- | --- | --- |
+| [model] | object | {} | Passed model or empty model. |
+
+
+
+## updateTempElmNew(key, value)
+Helper method to change properties in `tempELm`.
+
+**Kind**: global function
+
+| Param | Type | Description |
+| --- | --- | --- |
+| key | string | Key of value to be changed. |
+| value | string | Value to be changed. |
+
+
+
+## toggleViewFill()
+Toggles the background of the fill label in the
view-modal and de-/activates to input.
+
+**Kind**: global function
+
+
+## App : object
+Container for `create()` & `prototype()`.
+
+**Kind**: global typedef
+
+* [App](#App) : object
+ * [.prototype](#App.prototype) : object
+ * [.create()](#App.create) ⇒ object
+
+
+
+### App.prototype : object
+Prototype object to instantiate the app from.
+
+**Kind**: static constant of [App](#App)
+
+
+### App.create() ⇒ object
+Instantiate the app from `App.prototype`.
+
+**Kind**: static method of [App](#App)
+**Returns**: object - - Extended app object with mixins.
diff --git a/manifest.json b/manifest.json
index 0ad1af0..e0ec13b 100644
--- a/manifest.json
+++ b/manifest.json
@@ -3,7 +3,6 @@
"short_name": "mecEdit",
"description": "A lightweight editor for modeling and simulation of planar linkages.",
"start_url": "./mecEdit.html",
- "scope": "/mecEdit/",
"display": "standalone",
"background_color": "#344c6b",
"theme_color": "#343a40",
diff --git a/mecEdit.html b/mecEdit.html
index 5590e35..5a5b850 100644
--- a/mecEdit.html
+++ b/mecEdit.html
@@ -4,12 +4,13 @@
+
mecEdit
-
+
@@ -116,11 +117,11 @@