12 lines
No EOL
7.3 KiB
JavaScript
12 lines
No EOL
7.3 KiB
JavaScript
import{html,render}from"https://cdn.jsdelivr.net/npm/lit-html/+esm";import{asyncAppend}from"https://cdn.jsdelivr.net/npm/lit-html/directives/async-append/+esm";import{ESPLoader,Transport}from"https://unpkg.com/esptool-js@0.5.3/bundle.js";let ESP_ROM_BAUD=115200;class InstallButton extends HTMLButtonElement{static isSupported="serial"in navigator;static isAllowed=window.isSecureContext;constructor(){super(),this.baudRate=ESP_ROM_BAUD,this.dialogElements={},this.currentFlow=null,this.currentStep=0,this.currentDialogElement=null,this.device=null,this.transport=null,this.esploader=null,this.chip=null,this.dialogCssClass="install-dialog",this.connected=this.connectionStates.DISCONNECTED,this.menuTitle="Installer Menu"}init(){this.preloadDialogs()}previousButton={label:"Previous",onClick:this.prevStep,isEnabled:async()=>0<this.currentStep};nextButton={label:"Next",onClick:this.nextStep,isEnabled:async()=>this.currentStep<this.currentFlow.steps.length-1};closeButton={label:"Close",onClick:async t=>{this.closeDialog()}};defaultButtons=[this.previousButton,this.nextButton];connectionStates={DISCONNECTED:"Connect",CONNECTING:"Connecting...",CONNECTED:"Disconnect"};dialogs={notSupported:{preload:!1,closeable:!0,template:t=>html`
|
|
Sorry, <b>Web Serial</b> is not supported on your browser at this time. Browsers we expect to work:
|
|
<ul>
|
|
<li>Google Chrome 89 (and higher)</li>
|
|
<li>Microsoft Edge 89 (and higher)</li>
|
|
<li>Opera 75 (and higher)</li>
|
|
</ul>
|
|
`,buttons:[this.closeButton]},menu:{closeable:!0,template:t=>html`
|
|
<p>${this.menuTitle}</p>
|
|
<ul class="flow-menu">
|
|
${asyncAppend(this.generateMenu((t,e)=>html`<li><a href="#" @click=${this.runFlow.bind(this)} id="${t}">${e.label.replace("[version]",this.releaseVersion)}</a></li>`))}
|
|
</ul>`,buttons:[this.closeButton]}};flows={};baudRates=[115200,128e3,153600,230400,460800,921600,15e5,2e6];connectedCallback(){InstallButton.isSupported&&InstallButton.isAllowed?this.toggleAttribute("install-supported",!0):this.toggleAttribute("install-unsupported",!0),this.addEventListener("click",async t=>{t.preventDefault(),InstallButton.isSupported?await this.buttonClickHandler(t):await this.showNotSupported()})}async buttonClickHandler(t){await this.showMenu()}getUrlParams(){var e={};return location.hash&&location.hash.substr(1).split("&").forEach(function(t){e[t.split("=")[0]]=t.split("=")[1]}),e}getUrlParam(t){var e=this.getUrlParams();let s=null;return s=t in e?e[t]:s}async enabledFlowCount(){let t=0;for(var[e,s]of Object.entries(this.flows))await s.isEnabled()&&t++;return t}async*generateMenu(t){0==await this.enabledFlowCount()&&(yield html`<li>No installable options available for this board.</li>`);for(var[e,s]of Object.entries(this.flows))await s.isEnabled()&&(yield t(e,s))}preloadDialogs(){for(var[t,e]of Object.entries(this.dialogs))"preload"in e&&!e.preload||(this.dialogElements[t]=this.getDialogElement(e))}createIdFromLabel(t){return t.replace(/^[^a-z]+|[^\w:.-]+/gi,"")}createDialogElement(t,e){var s=this.querySelector("#cp-installer-"+t);s&&this.remove(s);let i=document.createElement("dialog");i.id=t,i.classList.add(this.dialogCssClass);s=document.createElement("button"),s.href="#",s.classList.add("close-button"),s.addEventListener("click",t=>{t.preventDefault(),i.close()}),i.appendChild(s),t=document.createElement("div");t.classList.add("dialog-body"),i.appendChild(t);let n=this.defaultButtons;return e&&e.buttons&&(n=e.buttons),i.appendChild(this.createNavigation(n)),document.body.appendChild(i),i}createNavigation(t){var s=document.createElement("div");s.classList.add("dialog-navigation");for(let e of t){var i=document.createElement("button");i.innerText=e.label,i.id=this.createIdFromLabel(e.label),i.addEventListener("click",async t=>{t.preventDefault(),await e.onClick.bind(this)()}),i.addEventListener("update",async t=>{"onUpdate"in e&&await e.onUpdate.bind(this)(t),"isEnabled"in e&&(t.target.disabled=!await e.isEnabled.bind(this)())}),s.appendChild(i)}return s}getDialogElement(t,e=!1){s=this.dialogs,i=t;var s,i,n=Object.keys(s).find(t=>s[t]===i);return n?n in this.dialogElements&&!e?this.dialogElements[n]:this.createDialogElement(n,t):null}updateButtons(){var t;if(this.currentDialogElement)for(t of this.currentDialogElement.querySelectorAll(".dialog-navigation button"))t.dispatchEvent(new Event("update"))}showDialog(t,e={}){var s;this.currentDialogElement&&this.closeDialog(),this.currentDialogElement=this.getDialogElement(t),this.currentDialogElement||console.error("Dialog not found"),this.currentDialogElement&&(s=this.currentDialogElement.querySelector(".dialog-body"),"template"in t&&render(t.template(e),s),"closeable"in t&&t.closeable?this.currentDialogElement.querySelector(".close-button").style.display="block":this.currentDialogElement.querySelector(".close-button").style.display="none",this.defaultButtons,"buttons"in t&&t.buttons,this.updateButtons(),this.currentDialogElement.showModal())}closeDialog(){this.currentDialogElement.close(),this.currentDialogElement=null}errorMsg(t){t=this.stripHtml(t),console.error(t),this.showError(t)}logMsg(t,e=!1){console.info(this.stripHtml(t)),e&&console.trace()}updateEspConnected(t){Object.values(this.connectionStates).includes(t)&&(this.connected=t,this.updateButtons())}stripHtml(t){var e=document.createElement("div");return e.innerHTML=t,e.textContent||e.innerText||""}formatMacAddr(t){return t.map(t=>t.toString(16).toUpperCase().padStart(2,"0")).join(":")}async espDisconnect(){return!(!this.transport||(await this.transport.disconnect(),await this.transport.waitForUnlock(1500),this.updateEspConnected(this.connectionStates.DISCONNECTED),this.transport=null,this.device=null,this.chip=null))}async runFlow(t){if(t instanceof Event){if(t.preventDefault(),t.stopImmediatePropagation(),!(t.target.id in this.flows))return;t=this.flows[t.target.id]}this.currentFlow=t,this.currentStep=0,await this.currentFlow.steps[this.currentStep].bind(this)()}async nextStep(){this.currentFlow&&this.currentStep<this.currentFlow.steps.length&&(this.currentStep++,await this.currentFlow.steps[this.currentStep].bind(this)())}async prevStep(){this.currentFlow&&0<this.currentStep&&(this.currentStep--,await this.currentFlow.steps[this.currentStep].bind(this)())}async advanceSteps(t){this.currentFlow&&this.currentStep<=this.currentFlow.steps.length+t&&(this.currentStep+=t,await this.currentFlow.steps[this.currentStep].bind(this)())}async showMenu(){this.showDialog(this.dialogs.menu)}async showNotSupported(){this.showDialog(this.dialogs.notSupported)}async showError(t){this.showDialog(this.dialogs.error,{message:t})}async setBaudRateIfChipSupports(t){t!=this.baudRate&&await this.changeBaudRate(t)}async changeBaudRate(t){this.baudRates.includes(t)&&(null==this.transport?this.baudRate=t:this.errorMsg("Cannot change baud rate while connected."))}async espHardReset(){this.esploader&&await this.esploader.hardReset()}async espConnect(e){e.log("Connecting..."),null===this.device&&(this.device=await navigator.serial.requestPort({}),this.transport=new Transport(this.device,!0));var t={transport:this.transport,baudrate:this.baudRate,terminal:{clean(){},writeLine(t){e.log(t)},write(t){e.log(t)}},debugLogging:!1};return this.esploader=new ESPLoader(t),this.chip=await this.esploader.main(),e.log("Connected successfully."),this.esploader}}export{ESP_ROM_BAUD,InstallButton}; |