From 35a8a614f7f32fe574152545f35d6e9779b35f6a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 4 Jul 2026 09:30:42 -0400 Subject: [PATCH] Consolidate duplicated vendored JS into shared /js/ jszip.min.js existed twice (byte-identical); the toolbox and the nrf_web_tools demo page now both load /js/jszip.min.js. The web-serial polyfill existed twice under different names, and only toolbox/serial.js had the unhandled-rejection / byte-reordering / port-close fixes (146f4c0). That fixed copy is now /js/web-serial-polyfill.js and the stale nrf_web_tools/web-serial-polyfill.js is gone, so the demo page gets the fixes too. nrf_web_tools.js existed twice, and the toolbox shipped the older revision without the multi-part DFU reconnect flow, so multi-image NRF52840 packages dead-ended there after part 1. The newer copy is now /js/nrf_web_tools.js and both pages load it. Both copies export the same NrfWebTools API and the zip path resolves against the page URL, so the move is drop-in. Script-injection src values and their querySelector guards updated in both pages to the new absolute paths. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01TEs4tsAWupf2DXFM4faECC --- httpdocs/firmware/toolbox/nrf_web_tools.js | 1224 ----------------- .../{firmware/toolbox => js}/jszip.min.js | 0 .../{nrf_web_tools => js}/nrf_web_tools.js | 0 .../serial.js => js/web-serial-polyfill.js} | 0 httpdocs/nrf_web_tools/jszip.min.js | 13 - httpdocs/nrf_web_tools/web-serial-polyfill.js | 491 ------- 6 files changed, 1728 deletions(-) delete mode 100644 httpdocs/firmware/toolbox/nrf_web_tools.js rename httpdocs/{firmware/toolbox => js}/jszip.min.js (100%) rename httpdocs/{nrf_web_tools => js}/nrf_web_tools.js (100%) rename httpdocs/{firmware/toolbox/serial.js => js/web-serial-polyfill.js} (100%) delete mode 100644 httpdocs/nrf_web_tools/jszip.min.js delete mode 100644 httpdocs/nrf_web_tools/web-serial-polyfill.js diff --git a/httpdocs/firmware/toolbox/nrf_web_tools.js b/httpdocs/firmware/toolbox/nrf_web_tools.js deleted file mode 100644 index bb8c9c8..0000000 --- a/httpdocs/firmware/toolbox/nrf_web_tools.js +++ /dev/null @@ -1,1224 +0,0 @@ -/** - * nRF Web Tool - * Standalone library for installing DFU packages to nRF52 devices via Web Serial - * - * Usage: - * - * - * - * - * Requirements: - * - JSZip library (https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js) - * - Web Serial API support (Chrome/Edge) - * - HTTPS (required for Web Serial) - */ - -class NrfWebTools { - constructor(buttonElement, zipPath) { - this.button = buttonElement; - this.zipPath = zipPath; - this.modal = null; - this.firmwareQueue = []; - this.port = null; - this.reader = null; - this.writer = null; - this.rxBuf = []; - this.hciSequenceNumber = 0; - this.resolveAck = null; - this.ackTimeout = null; - this.currentState = 'WELCOME'; // WELCOME, LOADING, INSTALLING, WAITING_CONTINUE, SUCCESS, ERROR - this.flashState = null; // Store state for continuing after reset - - // Constants - this.SLIP_END = 0xC0; - this.SLIP_ESC = 0xDB; - this.SLIP_ESC_END = 0xDC; - this.SLIP_ESC_ESC = 0xDD; - - this.DATA_INTEGRITY_CHECK_PRESENT = 1; - this.RELIABLE_PACKET = 1; - this.HCI_PACKET_TYPE = 14; - - this.DFU_INIT_PACKET = 1; - this.DFU_START_PACKET = 3; - this.DFU_DATA_PACKET = 4; - this.DFU_STOP_DATA_PACKET = 5; - - this.DFU_UPDATE_MODE_NONE = 0; - this.DFU_UPDATE_MODE_SD = 1; - this.DFU_UPDATE_MODE_BL = 2; - this.DFU_UPDATE_MODE_APP = 4; - this.DFU_UPDATE_MODE_SD_BL = 3; - - this.FLASH_PAGE_SIZE = 4096; - this.FLASH_PAGE_ERASE_TIME = 0.0897; - this.FLASH_WORD_WRITE_TIME = 0.000100; - this.FLASH_PAGE_WRITE_TIME = (this.FLASH_PAGE_SIZE / 4) * this.FLASH_WORD_WRITE_TIME; - this.DFU_PACKET_MAX_SIZE = 512; - this.ACK_PACKET_TIMEOUT = 1000; - - this.init(); - } - - init() { - if (!this.button) { - throw new Error('Button element is required'); - } - - this.button.addEventListener('click', () => this.startInstall()); - // Modal will be created on first use - } - - createModal() { - // Check if modal already exists - if (document.getElementById('nrf52-dfu-modal')) { - this.modal = document.getElementById('nrf52-dfu-modal'); - return; - } - - const modal = document.createElement('div'); - modal.id = 'nrf52-dfu-modal'; - modal.className = 'nrf52-dfu-modal'; - modal.innerHTML = ` -
- -
-
-
-
- `; - - document.body.appendChild(modal); - this.modal = modal; - - // Close button handlers - use event delegation to ensure it always works - modal.addEventListener('click', (e) => { - if (e.target === modal) { - this.closeModal(); - } else if (e.target.closest('.nrf52-dfu-close') || e.target.closest('#nrf52-dfu-close-btn')) { - e.preventDefault(); - e.stopPropagation(); - this.closeModal(); - } - }); - - // Inject styles if not already present - this.injectStyles(); - - // Show welcome page initially - this.renderPage('WELCOME'); - } - - renderPage(state, data = {}) { - this.currentState = state; - const headlineEl = document.getElementById('nrf52-dfu-headline'); - const contentEl = document.getElementById('nrf52-dfu-content'); - const actionsEl = document.getElementById('nrf52-dfu-actions'); - const closeBtn = document.getElementById('nrf52-dfu-close-btn'); - - if (!headlineEl || !contentEl || !actionsEl) return; - - // Show/hide close button based on state - if (closeBtn) { - const shouldHide = (state === 'INSTALLING' || state === 'WAITING_CONTINUE'); - closeBtn.style.display = shouldHide ? 'none' : 'block'; - // Ensure button is clickable when visible - if (!shouldHide) { - closeBtn.style.pointerEvents = 'auto'; - closeBtn.style.zIndex = '1000'; - } - } - - headlineEl.innerHTML = ''; - contentEl.innerHTML = ''; - actionsEl.innerHTML = ''; - - switch (state) { - case 'WELCOME': - this.renderWelcomePage(headlineEl, contentEl, actionsEl); - break; - case 'LOADING': - this.renderLoadingPage(headlineEl, contentEl, actionsEl, data); - break; - case 'INSTALLING': - this.renderInstallingPage(headlineEl, contentEl, actionsEl, data); - break; - case 'WAITING_CONTINUE': - this.renderWaitingContinuePage(headlineEl, contentEl, actionsEl, data); - break; - case 'SUCCESS': - this.renderSuccessPage(headlineEl, contentEl, actionsEl, data); - break; - case 'ERROR': - this.renderErrorPage(headlineEl, contentEl, actionsEl, data); - break; - } - } - - renderWelcomePage(headline, content, actions) { - headline.innerHTML = '

nRF Web Tool

'; - content.innerHTML = ` -
-
๐Ÿ“ฑ
-

This tool will help you update the firmware on your nRF52 device using nRF Web Tool.

-
-

How it works:

-
    -
  1. Click "Start Update" below
  2. -
  3. Select your device twice: -
      -
    • First time: for reset (1200 baud) - device will enter DFU mode
    • -
    • Second time: for flashing (115200 baud) - firmware will be installed
    • -
    -
  4. -
  5. Wait for the update to complete
  6. -
-

Note: The browser will prompt you to select your device twice. This is normal and required for the update process.

-
-
- `; - actions.innerHTML = ` - - `; - const startBtn = document.getElementById('nrf52-dfu-start-btn'); - if (startBtn) { - startBtn.onclick = () => this.startInstall(); - } - } - - renderLoadingPage(headline, content, actions, data) { - headline.innerHTML = '

Loading Package

'; - content.innerHTML = ` -
-
-

${data.message || 'Loading firmware package...'}

-
- `; - actions.innerHTML = ''; - } - - renderInstallingPage(headline, content, actions, data) { - headline.innerHTML = '

Installing

'; - const progress = data.progress || 0; - const label = data.label || 'Installing firmware...'; - const showProgress = progress > 0 && progress < 100; - - content.innerHTML = ` -
- ${showProgress ? ` -
- - - - -
- ` : ` -
- `} -

${label}

- ${data.details ? `

${data.details}

` : ''} - -
- `; - actions.innerHTML = ''; - } - - renderSuccessPage(headline, content, actions, data) { - headline.innerHTML = '

Installation Complete

'; - content.innerHTML = ` -
-
๐ŸŽ‰
-

${data.message || 'Firmware has been successfully installed on your device!'}

-
- `; - actions.innerHTML = ` - - `; - } - - renderWaitingContinuePage(headline, content, actions, data) { - headline.innerHTML = '

Device Reset

'; - content.innerHTML = ` -
-
โœ“
-

${data.message || 'Device has been reset and is now in DFU mode.'}

-

Click "Continue" below to proceed with firmware installation. You will be prompted to select your device again.

-
- `; - actions.innerHTML = ` - - `; - const continueBtn = document.getElementById('nrf52-dfu-continue-btn'); - if (continueBtn) { - continueBtn.onclick = () => this.continueFlash(); - } - } - - renderErrorPage(headline, content, actions, data) { - headline.innerHTML = '

Error

'; - content.innerHTML = ` -
-
โš ๏ธ
-

- ${data.details ? `

` : ''} -
- `; - // Set via textContent so error text (which can include zip-manifest - // filenames from a loaded package) cannot inject markup into the DOM. - content.querySelector('.nrf52-dfu-error-message').textContent = - data.message || 'An error occurred during the update process.'; - if (data.details) { - content.querySelector('.nrf52-dfu-error-details').textContent = data.details; - } - actions.innerHTML = ` - - ${data.retry ? `` : ''} - `; - if (data.retry) { - const retryBtn = document.getElementById('nrf52-dfu-retry-btn'); - if (retryBtn) { - retryBtn.onclick = () => this.startInstall(); - } - } - } - - injectStyles() { - if (document.getElementById('nrf52-dfu-styles')) return; - - const style = document.createElement('style'); - style.id = 'nrf52-dfu-styles'; - style.textContent = ` - .nrf52-dfu-modal { - display: none; - position: fixed; - z-index: 10000; - left: 0; - top: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.5); - align-items: center; - justify-content: center; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; - } - .nrf52-dfu-modal.show { - display: flex; - } - .nrf52-dfu-modal-content { - background: var(--card-background, #1e1e1e); - border: 1px solid var(--border-color, #333); - border-radius: 12px; - padding: 0; - max-width: 390px; - width: 90%; - max-height: 80vh; - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3); - display: flex; - flex-direction: column; - color: var(--foreground, #e0e0e0); - position: relative; - overflow: hidden; - } - .nrf52-dfu-headline { - padding: 24px 24px 0 24px; - position: relative; - } - .nrf52-dfu-headline h3 { - margin: 0; - padding-right: 48px; - color: var(--foreground, #e0e0e0); - font-size: 1.5rem; - font-weight: 400; - } - .nrf52-dfu-close { - position: absolute; - right: 8px; - top: 8px; - z-index: 1000; - color: var(--muted-foreground, #8b949e); - font-size: 24px; - font-weight: bold; - cursor: pointer; - line-height: 1; - width: 40px; - height: 40px; - display: flex; - align-items: center; - justify-content: center; - border: none; - background: transparent; - padding: 0; - pointer-events: auto; - } - .nrf52-dfu-close:hover { - color: var(--foreground, #e0e0e0); - } - .nrf52-dfu-content { - padding: 24px; - flex: 1; - overflow-y: auto; - } - .nrf52-dfu-actions { - padding: 0 24px 24px 24px; - display: flex; - gap: 12px; - } - .nrf52-dfu-welcome { - text-align: center; - } - .nrf52-dfu-welcome-icon { - font-size: 64px; - margin-bottom: 16px; - } - .nrf52-dfu-welcome p { - margin: 16px 0; - line-height: 1.6; - color: var(--foreground, #e0e0e0); - } - .nrf52-dfu-instructions { - text-align: left; - margin-top: 24px; - background: var(--card-background, #2a2a2a); - border: 1px solid var(--border-color, #333); - border-radius: 8px; - padding: 16px; - } - .nrf52-dfu-instructions h4 { - margin: 0 0 12px 0; - font-size: 1rem; - color: var(--foreground, #e0e0e0); - } - .nrf52-dfu-instructions ol { - margin: 0; - padding-left: 20px; - line-height: 1.8; - } - .nrf52-dfu-instructions ul { - margin: 8px 0; - padding-left: 20px; - line-height: 1.6; - } - .nrf52-dfu-instructions li { - margin: 8px 0; - color: var(--muted-foreground, #a0a0a0); - } - .nrf52-dfu-note { - margin-top: 16px; - padding: 12px; - background: rgba(0, 191, 255, 0.1); - border-left: 3px solid var(--accent, #00bfff); - border-radius: 4px; - font-size: 0.9rem; - } - .nrf52-dfu-progress-page { - text-align: center; - display: flex; - flex-direction: column; - align-items: center; - } - .nrf52-dfu-spinner { - width: 48px; - height: 48px; - border: 4px solid var(--border-color, #333); - border-top-color: var(--accent, #00bfff); - border-radius: 50%; - animation: nrf52-dfu-spin 1s linear infinite; - margin-bottom: 16px; - } - @keyframes nrf52-dfu-spin { - to { transform: rotate(360deg); } - } - .nrf52-dfu-progress-page p { - margin: 8px 0; - line-height: 1.6; - } - .nrf52-dfu-details { - font-size: 0.9rem; - color: var(--muted-foreground, #a0a0a0); - margin-top: 8px; - } - .nrf52-dfu-message-page { - text-align: center; - display: flex; - flex-direction: column; - align-items: center; - } - .nrf52-dfu-message-icon { - font-size: 64px; - margin-bottom: 16px; - } - .nrf52-dfu-message-page p { - margin: 8px 0; - line-height: 1.6; - } - .nrf52-dfu-error-details { - margin-top: 16px; - padding: 12px; - background: rgba(248, 81, 73, 0.1); - border-left: 3px solid #f85149; - border-radius: 4px; - font-size: 0.9rem; - text-align: left; - color: #f85149; - } - .nrf52-dfu-circular-progress { - position: relative; - width: 48px; - height: 48px; - margin: 0 auto 16px; - } - .nrf52-dfu-circular-progress-svg { - width: 100%; - height: 100%; - transform: rotate(-90deg); - } - .nrf52-dfu-circular-progress-track { - stroke: var(--border-color, #333); - } - .nrf52-dfu-circular-progress-fill { - stroke: var(--accent, #00bfff); - stroke-linecap: round; - transition: stroke-dashoffset 0.3s ease; - } - .nrf52-dfu-circular-progress .nrf52-dfu-progress-text { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 14px; - color: var(--foreground, #e0e0e0); - font-weight: 500; - margin: 0; - } - .nrf52-dfu-progress-text { - font-size: 16px; - color: var(--foreground, #e0e0e0); - font-weight: 500; - margin-bottom: 16px; - } - .nrf52-dfu-log { - display: none !important; - visibility: hidden; - max-height: 0; - overflow: hidden; - } - .nrf52-dfu-log-entry { - margin-bottom: 2px; - line-height: 1.4; - } - .nrf52-dfu-log-entry.error { - color: #f85149; - } - .nrf52-dfu-log-entry.success { - color: #3fb950; - } - .nrf52-dfu-log-entry.info { - color: #58a6ff; - } - .nrf52-dfu-actions { - display: flex; - gap: 12px; - margin-top: 20px; - } - .nrf52-dfu-btn { - flex: 1; - padding: 12px 24px; - border: none; - border-radius: 8px; - font-size: 14px; - font-weight: 500; - cursor: pointer; - transition: all 0.2s; - } - .nrf52-dfu-btn-primary { - background: var(--accent, #00bfff); - color: var(--accent-button, #fff); - } - .nrf52-dfu-btn-primary:hover { - background: var(--accent-hover, #0099cc); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(0, 191, 255, 0.3); - } - .nrf52-dfu-btn-secondary { - background: var(--card-background, #2a2a2a); - color: var(--foreground, #e0e0e0); - border: 1px solid var(--border-color, #333); - } - .nrf52-dfu-btn-secondary:hover { - background: var(--card-background, #333); - border-color: var(--accent, #00bfff); - } - .nrf52-dfu-btn:disabled { - opacity: 0.5; - cursor: not-allowed; - transform: none; - } - `; - document.head.appendChild(style); - } - - showModal() { - if (this.modal) { - this.modal.classList.add('show'); - } - } - - closeModal() { - if (this.modal) { - this.modal.classList.remove('show'); - } - } - - log(message, type = 'info') { - const logEl = document.getElementById('nrf52-dfu-log'); - if (!logEl) return; - - const entry = document.createElement('div'); - entry.className = `nrf52-dfu-log-entry ${type}`; - entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`; - logEl.appendChild(entry); - logEl.scrollTop = logEl.scrollHeight; - - // Keep only last 50 entries - while (logEl.children.length > 50) { - logEl.removeChild(logEl.firstChild); - } - } - - updateProgress(percent, label = '', details = '') { - if (this.currentState === 'INSTALLING') { - this.renderPage('INSTALLING', { - progress: percent, - label: label || 'Installing firmware...', - details: details - }); - } - } - - sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); - } - - // SLIP encoding - slipEncode(data) { - let out = []; - for (let b of data) { - if (b === this.SLIP_END) out.push(this.SLIP_ESC, this.SLIP_ESC_END); - else if (b === this.SLIP_ESC) out.push(this.SLIP_ESC, this.SLIP_ESC_ESC); - else out.push(b); - } - return new Uint8Array(out); - } - - slipDecodeEscChars(data) { - let result = []; - let i = 0; - while (i < data.length) { - if (data[i] === 0xDB) { - i++; - if (i >= data.length) throw new Error('Invalid SLIP escape sequence'); - if (data[i] === 0xDC) result.push(0xC0); - else if (data[i] === 0xDD) result.push(0xDB); - else throw new Error('Char 0xDB NOT followed by 0xDC or 0xDD'); - } else { - result.push(data[i]); - } - i++; - } - return new Uint8Array(result); - } - - // CRC16 Nordic variant - crc16Nordic(data) { - let crc = 0xFFFF; - for (let b of data) { - crc = (crc >> 8 & 0x00FF) | (crc << 8 & 0xFF00); - crc ^= b; - crc ^= (crc & 0x00FF) >> 4; - crc ^= (crc << 8) << 4; - crc ^= ((crc & 0x00FF) << 4) << 1; - } - return crc & 0xFFFF; - } - - // HCI Packet functions - slipPartsToFourBytes(seq, dip, rp, pktType, pktLen) { - let bytes = new Uint8Array(4); - bytes[0] = seq | (((seq + 1) % 8) << 3) | (dip << 6) | (rp << 7); - bytes[1] = pktType | ((pktLen & 0x000F) << 4); - bytes[2] = (pktLen & 0x0FF0) >> 4; - bytes[3] = (~(bytes[0] + bytes[1] + bytes[2]) + 1) & 0xFF; - return bytes; - } - - int32ToBytes(value) { - return new Uint8Array([ - value & 0xFF, - (value >> 8) & 0xFF, - (value >> 16) & 0xFF, - (value >> 24) & 0xFF - ]); - } - - int16ToBytes(value) { - return new Uint8Array([ - value & 0xFF, - (value >> 8) & 0xFF - ]); - } - - createHciPacket(data) { - this.hciSequenceNumber = (this.hciSequenceNumber + 1) % 8; - - let header = this.slipPartsToFourBytes( - this.hciSequenceNumber, - this.DATA_INTEGRITY_CHECK_PRESENT, - this.RELIABLE_PACKET, - this.HCI_PACKET_TYPE, - data.length - ); - - let tempData = new Uint8Array(header.length + data.length); - tempData.set(header); - tempData.set(data, header.length); - - let crc = this.crc16Nordic(tempData); - let crcBytes = new Uint8Array([crc & 0xFF, (crc >> 8) & 0xFF]); - - let packetData = new Uint8Array(tempData.length + crcBytes.length); - packetData.set(tempData); - packetData.set(crcBytes, tempData.length); - - let encoded = this.slipEncode(packetData); - let finalPacket = new Uint8Array(1 + encoded.length + 1); - finalPacket[0] = this.SLIP_END; - finalPacket.set(encoded, 1); - finalPacket[finalPacket.length - 1] = this.SLIP_END; - - return finalPacket; - } - - concatArrays(a, b) { - let result = new Uint8Array(a.length + b.length); - result.set(a); - result.set(b, a.length); - return result; - } - - // DFU Transport functions - async readLoop() { - while (true) { - try { - const { value, done } = await this.reader.read(); - if (done) break; - - for (let b of value) { - if (b === this.SLIP_END) { - if (this.rxBuf.length >= 2) { - try { - let decoded = this.slipDecodeEscChars(new Uint8Array(this.rxBuf)); - if (decoded.length >= 2) { - let ackNr = (decoded[0] >> 3) & 0x07; - if (this.resolveAck) { - clearTimeout(this.ackTimeout); - this.resolveAck(ackNr); - this.resolveAck = null; - } - } - } catch (err) { - // Invalid packet, ignore - } - } - this.rxBuf = []; - } else { - this.rxBuf.push(b); - } - } - } catch (e) { - this.log("Read error: " + e, 'error'); - break; - } - } - } - - waitForAck(timeout = this.ACK_PACKET_TIMEOUT) { - return new Promise((resolve, reject) => { - this.resolveAck = resolve; - this.ackTimeout = setTimeout(() => { - this.resolveAck = null; - // Do not reset hciSequenceNumber here: sendPacket resends the same pkt - // built with the old seq, and the adafruit-nrfutil reference does not - // reset on timeout. Resetting desyncs the HCI reliable-transport counter - // so one lost ACK could wedge the rest of the transfer. - reject(new Error("ACK timeout")); - }, timeout); - }); - } - - async sendPacket(pkt) { - let attempts = 0; - let lastAck = null; - let packetSent = false; - - while (!packetSent) { - // Arm the ACK listener before writing so an ACK that lands immediately - // after the write is not dropped by readLoop (which needs resolveAck set). - const ackPromise = this.waitForAck(); - await this.writer.write(pkt); - attempts++; - - try { - let ack = await ackPromise; - if (lastAck === null) { - lastAck = ack; - packetSent = true; - } else if (ack === (lastAck + 1) % 8) { - lastAck = ack; - packetSent = true; - } else { - if (attempts > 3) { - throw new Error("Three failed tx attempts encountered"); - } - } - } catch (e) { - if (attempts > 3) { - throw new Error("Failed to get ACK: " + e); - } - } - } - } - - getEraseWaitTime(totalSize) { - return Math.max(500, ((Math.floor(totalSize / this.FLASH_PAGE_SIZE) + 1) * this.FLASH_PAGE_ERASE_TIME * 1000)); - } - - getActivateWaitTime(totalSize, sdSize, singleBank) { - if (singleBank && sdSize === 0) { - return (this.FLASH_PAGE_ERASE_TIME + this.FLASH_PAGE_WRITE_TIME) * 1000; - } else { - let writeWaitTime = ((Math.floor(totalSize / this.FLASH_PAGE_SIZE) + 1) * this.FLASH_PAGE_WRITE_TIME * 1000); - return this.getEraseWaitTime(totalSize) + writeWaitTime; - } - } - - async sendStartDfu(mode, softdeviceSize, bootloaderSize, appSize) { - let frame = this.int32ToBytes(this.DFU_START_PACKET); - frame = this.concatArrays(frame, this.int32ToBytes(mode)); - - let sizes = new Uint8Array(12); - let offset = 0; - if (softdeviceSize !== undefined && softdeviceSize !== null) { - sizes.set(this.int32ToBytes(softdeviceSize), offset); - offset += 4; - } else { - sizes.set(this.int32ToBytes(0), offset); - offset += 4; - } - if (bootloaderSize !== undefined && bootloaderSize !== null) { - sizes.set(this.int32ToBytes(bootloaderSize), offset); - offset += 4; - } else { - sizes.set(this.int32ToBytes(0), offset); - offset += 4; - } - if (appSize !== undefined && appSize !== null) { - sizes.set(this.int32ToBytes(appSize), offset); - } else { - sizes.set(this.int32ToBytes(0), offset); - } - - frame = this.concatArrays(frame, sizes); - let packet = this.createHciPacket(frame); - await this.sendPacket(packet); - - let totalSize = (softdeviceSize || 0) + (bootloaderSize || 0) + (appSize || 0); - await this.sleep(this.getEraseWaitTime(totalSize)); - } - - async sendInitPacket(initPacket) { - let frame = this.int32ToBytes(this.DFU_INIT_PACKET); - frame = this.concatArrays(frame, new Uint8Array(initPacket)); - frame = this.concatArrays(frame, this.int16ToBytes(0x0000)); // Padding - - let packet = this.createHciPacket(frame); - await this.sendPacket(packet); - } - - async sendFirmware(firmware, onProgress) { - let frames = []; - - for (let i = 0; i < firmware.length; i += this.DFU_PACKET_MAX_SIZE) { - let frame = this.int32ToBytes(this.DFU_DATA_PACKET); - let chunk = firmware.slice(i, i + this.DFU_PACKET_MAX_SIZE); - frame = this.concatArrays(frame, chunk); - frames.push(frame); - } - - for (let count = 0; count < frames.length; count++) { - let packet = this.createHciPacket(frames[count]); - await this.sendPacket(packet); - - if (onProgress) { - let percent = Math.floor((count / frames.length) * 100); - onProgress(percent); - } - - if (count % 8 === 0 && count > 0) { - await this.sleep(this.FLASH_PAGE_WRITE_TIME * 1000); - } - } - - await this.sleep(this.FLASH_PAGE_WRITE_TIME * 1000); - - let stopFrame = this.int32ToBytes(this.DFU_STOP_DATA_PACKET); - let stopPacket = this.createHciPacket(stopFrame); - await this.sendPacket(stopPacket); - - if (onProgress) onProgress(100); - } - - async disconnectPort() { - if (!this.port) return; - // ReadableStream/WritableStream have no releaseLock() (only the reader/writer - // do), so the old code threw before ever closing the port, leaving it open - // with a stale this.port and blocking the next reconnect. Close each step - // independently and always null every field in finally. - try { - if (this.reader) { - try { await this.reader.cancel(); } catch (e) {} - try { this.reader.releaseLock(); } catch (e) {} - } - if (this.writer) { - try { await this.writer.close(); } catch (e) {} - try { this.writer.releaseLock(); } catch (e) {} - } - try { await this.port.close(); } catch (e) {} - } finally { - this.reader = null; - this.writer = null; - this.port = null; - } - } - - async loadPackage(zipPath) { - try { - // Check if JSZip is available - if (typeof JSZip === 'undefined') { - throw new Error('JSZip library is required. Please include: '); - } - - this.log(`Loading DFU package from: ${zipPath}...`, 'info'); - - // Load ZIP file (can be URL or File object) - let zip; - if (typeof zipPath === 'string') { - // URL - const response = await fetch(zipPath); - if (!response.ok) { - throw new Error(`Failed to fetch package: ${response.statusText}`); - } - const blob = await response.blob(); - zip = await JSZip.loadAsync(blob); - } else if (zipPath instanceof File) { - // File object - zip = await JSZip.loadAsync(zipPath); - } else { - throw new Error('Invalid zipPath: must be URL string or File object'); - } - - // Check for manifest.json - const manifestFile = zip.file('manifest.json'); - if (!manifestFile) { - throw new Error("manifest.json not found in package"); - } - - // Parse manifest - const manifestText = await manifestFile.async('string'); - const manifestData = JSON.parse(manifestText); - const manifest = manifestData.manifest; - - if (!manifest) { - throw new Error("Invalid manifest structure"); - } - - this.log(`DFU version: ${manifest.dfu_version || 'unknown'}`, 'info'); - - // Load all available firmware types in the correct order - this.firmwareQueue = []; - - const firmwareOrder = [ - { key: 'softdevice_bootloader', type: 'softdevice+bootloader', mode: this.DFU_UPDATE_MODE_SD_BL }, - { key: 'softdevice', type: 'softdevice', mode: this.DFU_UPDATE_MODE_SD }, - { key: 'bootloader', type: 'bootloader', mode: this.DFU_UPDATE_MODE_BL }, - { key: 'application', type: 'application', mode: this.DFU_UPDATE_MODE_APP } - ]; - - for (const fw of firmwareOrder) { - if (manifest[fw.key]) { - const firmwareInfo = manifest[fw.key]; - this.log(`Found ${fw.type} firmware`, 'info'); - this.log(` Binary: ${firmwareInfo.bin_file}`, 'info'); - this.log(` Init packet: ${firmwareInfo.dat_file}`, 'info'); - - const binFile = zip.file(firmwareInfo.bin_file); - if (!binFile) { - throw new Error(`Binary file not found: ${firmwareInfo.bin_file}`); - } - const fwBin = new Uint8Array(await binFile.async('arraybuffer')); - this.log(` Loaded firmware: ${fwBin.length} bytes`, 'info'); - - const datFile = zip.file(firmwareInfo.dat_file); - if (!datFile) { - throw new Error(`Init packet file not found: ${firmwareInfo.dat_file}`); - } - const initBin = new Uint8Array(await datFile.async('arraybuffer')); - this.log(` Loaded init packet: ${initBin.length} bytes`, 'info'); - - this.firmwareQueue.push({ - type: fw.type, - mode: fw.mode, - bin: fwBin, - dat: initBin, - info: firmwareInfo - }); - } - } - - if (this.firmwareQueue.length === 0) { - throw new Error("No firmware found in package"); - } - - this.log(`โœ“ Package loaded successfully - ${this.firmwareQueue.length} firmware type(s) ready`, 'success'); - return true; - } catch (err) { - this.log("โœ— Error loading package: " + err.message, 'error'); - throw err; - } - } - - async flashSingleFirmware(fw, progressBase, progressRange) { - let dfuMode = fw.mode; - let appSize = 0; - let softdeviceSize = 0; - let bootloaderSize = 0; - let firmwareSize = fw.bin.length; - - if (fw.type === "application") { - appSize = firmwareSize; - } else if (fw.type === "bootloader") { - bootloaderSize = firmwareSize; - } else if (fw.type === "softdevice") { - softdeviceSize = firmwareSize; - } else if (fw.type === "softdevice+bootloader") { - softdeviceSize = fw.info.sd_size || 0; - bootloaderSize = fw.info.bl_size || 0; - if (softdeviceSize === 0 || bootloaderSize === 0) { - throw new Error("SD+BL package missing sd_size or bl_size in manifest"); - } - if (softdeviceSize + bootloaderSize !== firmwareSize) { - throw new Error(`SD+BL size mismatch: ${softdeviceSize} + ${bootloaderSize} != ${firmwareSize}`); - } - } - - this.log(`\nFlashing ${fw.type}...`, 'info'); - this.log(` Size: ${firmwareSize} bytes`, 'info'); - - await this.sendStartDfu(dfuMode, softdeviceSize, bootloaderSize, appSize); - this.log(" Start packet sent", 'info'); - - await this.sendInitPacket(fw.dat); - this.log(" Init packet sent", 'info'); - - this.log(" Sending firmware...", 'info'); - await this.sendFirmware(fw.bin, (percent) => { - const overallPercent = progressBase + (percent * progressRange / 100); - this.updateProgress(overallPercent, `Installing ${fw.type}...`, `${Math.round(percent)}% complete`); - if (percent % 10 === 0) { - this.log(` Progress: ${percent}%`, 'info'); - } - }); - - this.log(" Firmware sent, activating...", 'info'); - await this.sleep(this.getActivateWaitTime(firmwareSize, softdeviceSize, false)); - - this.log(` โœ“ ${fw.type} flashed successfully`, 'success'); - } - - async startInstall() { - // Check for Web Serial API support - if (!navigator.serial) { - this.renderPage('ERROR', { - message: 'Web Serial API is not supported', - details: 'Please use Google Chrome or Microsoft Edge to use this tool.' - }); - return; - } - - if (!this.zipPath) { - this.renderPage('ERROR', { - message: 'No package specified', - details: 'Please provide a valid DFU package path.' - }); - return; - } - - // Create modal if it doesn't exist - if (!this.modal) { - this.createModal(); - } - - this.showModal(); - - // Clear log - const logEl = document.getElementById('nrf52-dfu-log'); - if (logEl) logEl.innerHTML = ''; - - try { - // Load package - this.renderPage('LOADING', { message: 'Loading firmware package...' }); - await this.loadPackage(this.zipPath); - - // Start flashing immediately after loading - await this.performFlash(); - - } catch (err) { - this.renderPage('ERROR', { - message: 'Failed to load package', - details: err.message, - retry: true - }); - } - } - - async performFlash() { - try { - // Step 1: Reset device (1200 baud) - this.renderPage('INSTALLING', { - progress: 0, - label: 'Resetting device', - details: 'Please select your device (first time)' - }); - this.log("=== Step 1: Resetting device ===", 'info'); - this.log("Please select your device (first time)...", 'info'); - - await this.disconnectPort(); - - try { - const resetPort = await navigator.serial.requestPort(); - await resetPort.open({ baudRate: 1200 }); - await resetPort.close(); - this.log("โœ“ Reset triggered - device entering DFU mode", 'success'); - await this.sleep(500); - - // Show continue button - this ensures the next requestPort() call is in a user gesture context - this.renderPage('WAITING_CONTINUE', { - message: 'Device reset successful!' - }); - - // Store state so continueFlash can access it - this.flashState = { firmwareQueue: this.firmwareQueue }; - } catch (portError) { - // Check if user cancelled the port selection - if (portError instanceof DOMException && portError.name === 'NotFoundError') { - this.renderPage('ERROR', { - message: 'Port selection cancelled', - details: 'You can also press the reset button on your device twice quickly to force it into bootloader mode, then click "Retry" to continue.', - retry: true - }); - return; - } - // Re-throw other errors to be caught by outer catch - throw portError; - } - - } catch (e) { - this.log("DFU error: " + e, 'error'); - await this.disconnectPort(); - - this.renderPage('ERROR', { - message: 'Installation failed', - details: e.message, - retry: true - }); - } - } - - async continueFlash() { - try { - // Step 2: Flash firmware (115200 baud) - // This is now called from a button click, so we're in a user gesture context - this.renderPage('INSTALLING', { - progress: 0, - label: 'Connecting to device', - details: 'Please select your device again (second time)' - }); - this.log("=== Step 2: Flashing firmware ===", 'info'); - this.log(`Flashing ${this.firmwareQueue.length} firmware type(s)...`, 'info'); - this.log("Please select your device again (second time)...", 'info'); - - this.port = await navigator.serial.requestPort(); - await this.port.open({ baudRate: 115200 }); - this.writer = this.port.writable.getWriter(); - this.reader = this.port.readable.getReader(); - this.rxBuf = []; - this.hciSequenceNumber = 0; - this.readLoop(); - await this.sleep(100); - - this.log("Starting DFU...", 'info'); - this.updateProgress(0, 'Preparing installation'); - - // Flash each firmware type in order - for (let i = 0; i < this.firmwareQueue.length; i++) { - const fw = this.firmwareQueue[i]; - const progressBase = (i / this.firmwareQueue.length) * 100; - const progressRange = 100 / this.firmwareQueue.length; - - this.updateProgress(progressBase, `Installing ${fw.type}...`); - - await this.flashSingleFirmware(fw, progressBase, progressRange); - } - - this.log("\nโœ“ DFU complete! All firmware types flashed successfully.", 'success'); - this.updateProgress(100, 'Installation complete'); - await this.sleep(500); - - await this.disconnectPort(); - - this.renderPage('SUCCESS', { - message: `Firmware has been successfully installed!
${this.firmwareQueue.length} firmware type(s) flashed.` - }); - - } catch (e) { - this.log("DFU error: " + e, 'error'); - await this.disconnectPort(); - - this.renderPage('ERROR', { - message: 'Installation failed', - details: e.message, - retry: true - }); - } - } -} - -// Immediately make available globally (for regular script tags) -(function() { - 'use strict'; - if (typeof window !== 'undefined') { - window.NrfWebTools = NrfWebTools; - // Keep backward compatibility - window.Nrf52DfuInstaller = NrfWebTools; - } - // Export for CommonJS (Node.js) - if (typeof module !== 'undefined' && module.exports) { - module.exports = NrfWebTools; - } -})(); diff --git a/httpdocs/firmware/toolbox/jszip.min.js b/httpdocs/js/jszip.min.js similarity index 100% rename from httpdocs/firmware/toolbox/jszip.min.js rename to httpdocs/js/jszip.min.js diff --git a/httpdocs/nrf_web_tools/nrf_web_tools.js b/httpdocs/js/nrf_web_tools.js similarity index 100% rename from httpdocs/nrf_web_tools/nrf_web_tools.js rename to httpdocs/js/nrf_web_tools.js diff --git a/httpdocs/firmware/toolbox/serial.js b/httpdocs/js/web-serial-polyfill.js similarity index 100% rename from httpdocs/firmware/toolbox/serial.js rename to httpdocs/js/web-serial-polyfill.js diff --git a/httpdocs/nrf_web_tools/jszip.min.js b/httpdocs/nrf_web_tools/jszip.min.js deleted file mode 100644 index ff4cfd5..0000000 --- a/httpdocs/nrf_web_tools/jszip.min.js +++ /dev/null @@ -1,13 +0,0 @@ -/*! - -JSZip v3.10.1 - A JavaScript class for generating and reading zip files - - -(c) 2009-2016 Stuart Knightley -Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown. - -JSZip uses the library pako released under the MIT license : -https://github.com/nodeca/pako/blob/main/LICENSE -*/ - -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).JSZip=e()}}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t="function"==typeof require&&require;if(!e&&t)return t(r,!0);if(l)return l(r,!0);var n=new Error("Cannot find module '"+r+"'");throw n.code="MODULE_NOT_FOUND",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h)}return o[r].exports}for(var l="function"==typeof require&&require,e=0;e>2,s=(3&t)<<4|r>>4,a=1>6:64,o=2>4,r=(15&i)<<4|(s=p.indexOf(e.charAt(o++)))>>2,n=(3&s)<<6|(a=p.indexOf(e.charAt(o++))),l[h++]=t,64!==s&&(l[h++]=r),64!==a&&(l[h++]=n);return l}},{"./support":30,"./utils":32}],2:[function(e,t,r){"use strict";var n=e("./external"),i=e("./stream/DataWorker"),s=e("./stream/Crc32Probe"),a=e("./stream/DataLengthProbe");function o(e,t,r,n,i){this.compressedSize=e,this.uncompressedSize=t,this.crc32=r,this.compression=n,this.compressedContent=i}o.prototype={getContentWorker:function(){var e=new i(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a("data_length")),t=this;return e.on("end",function(){if(this.streamInfo.data_length!==t.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),e},getCompressedWorker:function(){return new i(n.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},o.createWorkerFrom=function(e,t,r){return e.pipe(new s).pipe(new a("uncompressedSize")).pipe(t.compressWorker(r)).pipe(new a("compressedSize")).withStreamInfo("compression",t)},t.exports=o},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(e,t,r){"use strict";var n=e("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(){return new n("STORE compression")},uncompressWorker:function(){return new n("STORE decompression")}},r.DEFLATE=e("./flate")},{"./flate":7,"./stream/GenericWorker":28}],4:[function(e,t,r){"use strict";var n=e("./utils");var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t){return void 0!==e&&e.length?"string"!==n.getTypeOf(e)?function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}(0|t,e,e.length,0):function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t.charCodeAt(a))];return-1^e}(0|t,e,e.length,0):0}},{"./utils":32}],5:[function(e,t,r){"use strict";r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null},{}],6:[function(e,t,r){"use strict";var n=null;n="undefined"!=typeof Promise?Promise:e("lie"),t.exports={Promise:n}},{lie:37}],7:[function(e,t,r){"use strict";var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,i=e("pako"),s=e("./utils"),a=e("./stream/GenericWorker"),o=n?"uint8array":"array";function h(e,t){a.call(this,"FlateWorker/"+e),this._pako=null,this._pakoAction=e,this._pakoOptions=t,this.meta={}}r.magic="\b\0",s.inherits(h,a),h.prototype.processChunk=function(e){this.meta=e.meta,null===this._pako&&this._createPako(),this._pako.push(s.transformTo(o,e.data),!1)},h.prototype.flush=function(){a.prototype.flush.call(this),null===this._pako&&this._createPako(),this._pako.push([],!0)},h.prototype.cleanUp=function(){a.prototype.cleanUp.call(this),this._pako=null},h.prototype._createPako=function(){this._pako=new i[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var t=this;this._pako.onData=function(e){t.push({data:e,meta:t.meta})}},r.compressWorker=function(e){return new h("Deflate",e)},r.uncompressWorker=function(){return new h("Inflate",{})}},{"./stream/GenericWorker":28,"./utils":32,pako:38}],8:[function(e,t,r){"use strict";function A(e,t){var r,n="";for(r=0;r>>=8;return n}function n(e,t,r,n,i,s){var a,o,h=e.file,u=e.compression,l=s!==O.utf8encode,f=I.transformTo("string",s(h.name)),c=I.transformTo("string",O.utf8encode(h.name)),d=h.comment,p=I.transformTo("string",s(d)),m=I.transformTo("string",O.utf8encode(d)),_=c.length!==h.name.length,g=m.length!==d.length,b="",v="",y="",w=h.dir,k=h.date,x={crc32:0,compressedSize:0,uncompressedSize:0};t&&!r||(x.crc32=e.crc32,x.compressedSize=e.compressedSize,x.uncompressedSize=e.uncompressedSize);var S=0;t&&(S|=8),l||!_&&!g||(S|=2048);var z=0,C=0;w&&(z|=16),"UNIX"===i?(C=798,z|=function(e,t){var r=e;return e||(r=t?16893:33204),(65535&r)<<16}(h.unixPermissions,w)):(C=20,z|=function(e){return 63&(e||0)}(h.dosPermissions)),a=k.getUTCHours(),a<<=6,a|=k.getUTCMinutes(),a<<=5,a|=k.getUTCSeconds()/2,o=k.getUTCFullYear()-1980,o<<=4,o|=k.getUTCMonth()+1,o<<=5,o|=k.getUTCDate(),_&&(v=A(1,1)+A(B(f),4)+c,b+="up"+A(v.length,2)+v),g&&(y=A(1,1)+A(B(p),4)+m,b+="uc"+A(y.length,2)+y);var E="";return E+="\n\0",E+=A(S,2),E+=u.magic,E+=A(a,2),E+=A(o,2),E+=A(x.crc32,4),E+=A(x.compressedSize,4),E+=A(x.uncompressedSize,4),E+=A(f.length,2),E+=A(b.length,2),{fileRecord:R.LOCAL_FILE_HEADER+E+f+b,dirRecord:R.CENTRAL_FILE_HEADER+A(C,2)+E+A(p.length,2)+"\0\0\0\0"+A(z,4)+A(n,4)+f+b+p}}var I=e("../utils"),i=e("../stream/GenericWorker"),O=e("../utf8"),B=e("../crc32"),R=e("../signature");function s(e,t,r,n){i.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=t,this.zipPlatform=r,this.encodeFileName=n,this.streamFiles=e,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[]}I.inherits(s,i),s.prototype.push=function(e){var t=e.meta.percent||0,r=this.entriesCount,n=this._sources.length;this.accumulate?this.contentBuffer.push(e):(this.bytesWritten+=e.data.length,i.prototype.push.call(this,{data:e.data,meta:{currentFile:this.currentFile,percent:r?(t+100*(r-n-1))/r:100}}))},s.prototype.openedSource=function(e){this.currentSourceOffset=this.bytesWritten,this.currentFile=e.file.name;var t=this.streamFiles&&!e.file.dir;if(t){var r=n(e,t,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:r.fileRecord,meta:{percent:0}})}else this.accumulate=!0},s.prototype.closedSource=function(e){this.accumulate=!1;var t=this.streamFiles&&!e.file.dir,r=n(e,t,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(r.dirRecord),t)this.push({data:function(e){return R.DATA_DESCRIPTOR+A(e.crc32,4)+A(e.compressedSize,4)+A(e.uncompressedSize,4)}(e),meta:{percent:100}});else for(this.push({data:r.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null},s.prototype.flush=function(){for(var e=this.bytesWritten,t=0;t=this.index;t--)r=(r<<8)+this.byteAt(t);return this.index+=e,r},readString:function(e){return n.transformTo("string",this.readData(e))},readData:function(){},lastIndexOfSignature:function(){},readAndCheckSignature:function(){},readDate:function(){var e=this.readInt(4);return new Date(Date.UTC(1980+(e>>25&127),(e>>21&15)-1,e>>16&31,e>>11&31,e>>5&63,(31&e)<<1))}},t.exports=i},{"../utils":32}],19:[function(e,t,r){"use strict";var n=e("./Uint8ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(e,t,r){"use strict";var n=e("./DataReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.byteAt=function(e){return this.data.charCodeAt(this.zero+e)},i.prototype.lastIndexOfSignature=function(e){return this.data.lastIndexOf(e)-this.zero},i.prototype.readAndCheckSignature=function(e){return e===this.readData(4)},i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./DataReader":18}],21:[function(e,t,r){"use strict";var n=e("./ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return new Uint8Array(0);var t=this.data.subarray(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./ArrayReader":17}],22:[function(e,t,r){"use strict";var n=e("../utils"),i=e("../support"),s=e("./ArrayReader"),a=e("./StringReader"),o=e("./NodeBufferReader"),h=e("./Uint8ArrayReader");t.exports=function(e){var t=n.getTypeOf(e);return n.checkSupport(t),"string"!==t||i.uint8array?"nodebuffer"===t?new o(e):i.uint8array?new h(n.transformTo("uint8array",e)):new s(n.transformTo("array",e)):new a(e)}},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(e,t,r){"use strict";r.LOCAL_FILE_HEADER="PK",r.CENTRAL_FILE_HEADER="PK",r.CENTRAL_DIRECTORY_END="PK",r.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",r.ZIP64_CENTRAL_DIRECTORY_END="PK",r.DATA_DESCRIPTOR="PK\b"},{}],24:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../utils");function s(e){n.call(this,"ConvertWorker to "+e),this.destType=e}i.inherits(s,n),s.prototype.processChunk=function(e){this.push({data:i.transformTo(this.destType,e.data),meta:e.meta})},t.exports=s},{"../utils":32,"./GenericWorker":28}],25:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../crc32");function s(){n.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0)}e("../utils").inherits(s,n),s.prototype.processChunk=function(e){this.streamInfo.crc32=i(e.data,this.streamInfo.crc32||0),this.push(e)},t.exports=s},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataLengthProbe for "+e),this.propName=e,this.withStreamInfo(e,0)}n.inherits(s,i),s.prototype.processChunk=function(e){if(e){var t=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=t+e.data.length}i.prototype.processChunk.call(this,e)},t.exports=s},{"../utils":32,"./GenericWorker":28}],27:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataWorker");var t=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,e.then(function(e){t.dataIsReady=!0,t.data=e,t.max=e&&e.length||0,t.type=n.getTypeOf(e),t.isPaused||t._tickAndRepeat()},function(e){t.error(e)})}n.inherits(s,i),s.prototype.cleanUp=function(){i.prototype.cleanUp.call(this),this.data=null},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,n.delay(this._tickAndRepeat,[],this)),!0)},s.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(n.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0))},s.prototype._tick=function(){if(this.isPaused||this.isFinished)return!1;var e=null,t=Math.min(this.max,this.index+16384);if(this.index>=this.max)return this.end();switch(this.type){case"string":e=this.data.substring(this.index,t);break;case"uint8array":e=this.data.subarray(this.index,t);break;case"array":case"nodebuffer":e=this.data.slice(this.index,t)}return this.index=t,this.push({data:e,meta:{percent:this.max?this.index/this.max*100:0}})},t.exports=s},{"../utils":32,"./GenericWorker":28}],28:[function(e,t,r){"use strict";function n(e){this.name=e||"default",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null}n.prototype={push:function(e){this.emit("data",e)},end:function(){if(this.isFinished)return!1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0}catch(e){this.emit("error",e)}return!0},error:function(e){return!this.isFinished&&(this.isPaused?this.generatedError=e:(this.isFinished=!0,this.emit("error",e),this.previous&&this.previous.error(e),this.cleanUp()),!0)},on:function(e,t){return this._listeners[e].push(t),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[]},emit:function(e,t){if(this._listeners[e])for(var r=0;r "+e:e}},t.exports=n},{}],29:[function(e,t,r){"use strict";var h=e("../utils"),i=e("./ConvertWorker"),s=e("./GenericWorker"),u=e("../base64"),n=e("../support"),a=e("../external"),o=null;if(n.nodestream)try{o=e("../nodejs/NodejsStreamOutputAdapter")}catch(e){}function l(e,o){return new a.Promise(function(t,r){var n=[],i=e._internalType,s=e._outputType,a=e._mimeType;e.on("data",function(e,t){n.push(e),o&&o(t)}).on("error",function(e){n=[],r(e)}).on("end",function(){try{var e=function(e,t,r){switch(e){case"blob":return h.newBlob(h.transformTo("arraybuffer",t),r);case"base64":return u.encode(t);default:return h.transformTo(e,t)}}(s,function(e,t){var r,n=0,i=null,s=0;for(r=0;r>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t}(e)},s.utf8decode=function(e){return h.nodebuffer?o.transformTo("nodebuffer",e).toString("utf-8"):function(e){var t,r,n,i,s=e.length,a=new Array(2*s);for(t=r=0;t>10&1023,a[r++]=56320|1023&n)}return a.length!==r&&(a.subarray?a=a.subarray(0,r):a.length=r),o.applyFromCharCode(a)}(e=o.transformTo(h.uint8array?"uint8array":"array",e))},o.inherits(a,n),a.prototype.processChunk=function(e){var t=o.transformTo(h.uint8array?"uint8array":"array",e.data);if(this.leftOver&&this.leftOver.length){if(h.uint8array){var r=t;(t=new Uint8Array(r.length+this.leftOver.length)).set(this.leftOver,0),t.set(r,this.leftOver.length)}else t=this.leftOver.concat(t);this.leftOver=null}var n=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}(t),i=t;n!==t.length&&(h.uint8array?(i=t.subarray(0,n),this.leftOver=t.subarray(n,t.length)):(i=t.slice(0,n),this.leftOver=t.slice(n,t.length))),this.push({data:s.utf8decode(i),meta:e.meta})},a.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:s.utf8decode(this.leftOver),meta:{}}),this.leftOver=null)},s.Utf8DecodeWorker=a,o.inherits(l,n),l.prototype.processChunk=function(e){this.push({data:s.utf8encode(e.data),meta:e.meta})},s.Utf8EncodeWorker=l},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(e,t,a){"use strict";var o=e("./support"),h=e("./base64"),r=e("./nodejsUtils"),u=e("./external");function n(e){return e}function l(e,t){for(var r=0;r>8;this.dir=!!(16&this.externalFileAttributes),0==e&&(this.dosPermissions=63&this.externalFileAttributes),3==e&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileNameStr.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(){if(this.extraFields[1]){var e=n(this.extraFields[1].value);this.uncompressedSize===s.MAX_VALUE_32BITS&&(this.uncompressedSize=e.readInt(8)),this.compressedSize===s.MAX_VALUE_32BITS&&(this.compressedSize=e.readInt(8)),this.localHeaderOffset===s.MAX_VALUE_32BITS&&(this.localHeaderOffset=e.readInt(8)),this.diskNumberStart===s.MAX_VALUE_32BITS&&(this.diskNumberStart=e.readInt(4))}},readExtraFields:function(e){var t,r,n,i=e.index+this.extraFieldsLength;for(this.extraFields||(this.extraFields={});e.index+4>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t},r.buf2binstring=function(e){return l(e,e.length)},r.binstring2buf=function(e){for(var t=new h.Buf8(e.length),r=0,n=t.length;r>10&1023,o[n++]=56320|1023&i)}return l(o,n)},r.utf8border=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}},{"./common":41}],43:[function(e,t,r){"use strict";t.exports=function(e,t,r,n){for(var i=65535&e|0,s=e>>>16&65535|0,a=0;0!==r;){for(r-=a=2e3>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a>>8^i[255&(e^t[a])];return-1^e}},{}],46:[function(e,t,r){"use strict";var h,c=e("../utils/common"),u=e("./trees"),d=e("./adler32"),p=e("./crc32"),n=e("./messages"),l=0,f=4,m=0,_=-2,g=-1,b=4,i=2,v=8,y=9,s=286,a=30,o=19,w=2*s+1,k=15,x=3,S=258,z=S+x+1,C=42,E=113,A=1,I=2,O=3,B=4;function R(e,t){return e.msg=n[t],t}function T(e){return(e<<1)-(4e.avail_out&&(r=e.avail_out),0!==r&&(c.arraySet(e.output,t.pending_buf,t.pending_out,r,e.next_out),e.next_out+=r,t.pending_out+=r,e.total_out+=r,e.avail_out-=r,t.pending-=r,0===t.pending&&(t.pending_out=0))}function N(e,t){u._tr_flush_block(e,0<=e.block_start?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,F(e.strm)}function U(e,t){e.pending_buf[e.pending++]=t}function P(e,t){e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t}function L(e,t){var r,n,i=e.max_chain_length,s=e.strstart,a=e.prev_length,o=e.nice_match,h=e.strstart>e.w_size-z?e.strstart-(e.w_size-z):0,u=e.window,l=e.w_mask,f=e.prev,c=e.strstart+S,d=u[s+a-1],p=u[s+a];e.prev_length>=e.good_match&&(i>>=2),o>e.lookahead&&(o=e.lookahead);do{if(u[(r=t)+a]===p&&u[r+a-1]===d&&u[r]===u[s]&&u[++r]===u[s+1]){s+=2,r++;do{}while(u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&sh&&0!=--i);return a<=e.lookahead?a:e.lookahead}function j(e){var t,r,n,i,s,a,o,h,u,l,f=e.w_size;do{if(i=e.window_size-e.lookahead-e.strstart,e.strstart>=f+(f-z)){for(c.arraySet(e.window,e.window,f,f,0),e.match_start-=f,e.strstart-=f,e.block_start-=f,t=r=e.hash_size;n=e.head[--t],e.head[t]=f<=n?n-f:0,--r;);for(t=r=f;n=e.prev[--t],e.prev[t]=f<=n?n-f:0,--r;);i+=f}if(0===e.strm.avail_in)break;if(a=e.strm,o=e.window,h=e.strstart+e.lookahead,u=i,l=void 0,l=a.avail_in,u=x)for(s=e.strstart-e.insert,e.ins_h=e.window[s],e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x)if(n=u._tr_tally(e,e.strstart-e.match_start,e.match_length-x),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=x){for(e.match_length--;e.strstart++,e.ins_h=(e.ins_h<=x&&(e.ins_h=(e.ins_h<=x&&e.match_length<=e.prev_length){for(i=e.strstart+e.lookahead-x,n=u._tr_tally(e,e.strstart-1-e.prev_match,e.prev_length-x),e.lookahead-=e.prev_length-1,e.prev_length-=2;++e.strstart<=i&&(e.ins_h=(e.ins_h<e.pending_buf_size-5&&(r=e.pending_buf_size-5);;){if(e.lookahead<=1){if(j(e),0===e.lookahead&&t===l)return A;if(0===e.lookahead)break}e.strstart+=e.lookahead,e.lookahead=0;var n=e.block_start+r;if((0===e.strstart||e.strstart>=n)&&(e.lookahead=e.strstart-n,e.strstart=n,N(e,!1),0===e.strm.avail_out))return A;if(e.strstart-e.block_start>=e.w_size-z&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):(e.strstart>e.block_start&&(N(e,!1),e.strm.avail_out),A)}),new M(4,4,8,4,Z),new M(4,5,16,8,Z),new M(4,6,32,32,Z),new M(4,4,16,16,W),new M(8,16,32,32,W),new M(8,16,128,128,W),new M(8,32,128,256,W),new M(32,128,258,1024,W),new M(32,258,258,4096,W)],r.deflateInit=function(e,t){return Y(e,t,v,15,8,0)},r.deflateInit2=Y,r.deflateReset=K,r.deflateResetKeep=G,r.deflateSetHeader=function(e,t){return e&&e.state?2!==e.state.wrap?_:(e.state.gzhead=t,m):_},r.deflate=function(e,t){var r,n,i,s;if(!e||!e.state||5>8&255),U(n,n.gzhead.time>>16&255),U(n,n.gzhead.time>>24&255),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,255&n.gzhead.os),n.gzhead.extra&&n.gzhead.extra.length&&(U(n,255&n.gzhead.extra.length),U(n,n.gzhead.extra.length>>8&255)),n.gzhead.hcrc&&(e.adler=p(e.adler,n.pending_buf,n.pending,0)),n.gzindex=0,n.status=69):(U(n,0),U(n,0),U(n,0),U(n,0),U(n,0),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,3),n.status=E);else{var a=v+(n.w_bits-8<<4)<<8;a|=(2<=n.strategy||n.level<2?0:n.level<6?1:6===n.level?2:3)<<6,0!==n.strstart&&(a|=32),a+=31-a%31,n.status=E,P(n,a),0!==n.strstart&&(P(n,e.adler>>>16),P(n,65535&e.adler)),e.adler=1}if(69===n.status)if(n.gzhead.extra){for(i=n.pending;n.gzindex<(65535&n.gzhead.extra.length)&&(n.pending!==n.pending_buf_size||(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending!==n.pending_buf_size));)U(n,255&n.gzhead.extra[n.gzindex]),n.gzindex++;n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),n.gzindex===n.gzhead.extra.length&&(n.gzindex=0,n.status=73)}else n.status=73;if(73===n.status)if(n.gzhead.name){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.gzindex=0,n.status=91)}else n.status=91;if(91===n.status)if(n.gzhead.comment){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindexi&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.status=103)}else n.status=103;if(103===n.status&&(n.gzhead.hcrc?(n.pending+2>n.pending_buf_size&&F(e),n.pending+2<=n.pending_buf_size&&(U(n,255&e.adler),U(n,e.adler>>8&255),e.adler=0,n.status=E)):n.status=E),0!==n.pending){if(F(e),0===e.avail_out)return n.last_flush=-1,m}else if(0===e.avail_in&&T(t)<=T(r)&&t!==f)return R(e,-5);if(666===n.status&&0!==e.avail_in)return R(e,-5);if(0!==e.avail_in||0!==n.lookahead||t!==l&&666!==n.status){var o=2===n.strategy?function(e,t){for(var r;;){if(0===e.lookahead&&(j(e),0===e.lookahead)){if(t===l)return A;break}if(e.match_length=0,r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):3===n.strategy?function(e,t){for(var r,n,i,s,a=e.window;;){if(e.lookahead<=S){if(j(e),e.lookahead<=S&&t===l)return A;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=x&&0e.lookahead&&(e.match_length=e.lookahead)}if(e.match_length>=x?(r=u._tr_tally(e,1,e.match_length-x),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):h[n.level].func(n,t);if(o!==O&&o!==B||(n.status=666),o===A||o===O)return 0===e.avail_out&&(n.last_flush=-1),m;if(o===I&&(1===t?u._tr_align(n):5!==t&&(u._tr_stored_block(n,0,0,!1),3===t&&(D(n.head),0===n.lookahead&&(n.strstart=0,n.block_start=0,n.insert=0))),F(e),0===e.avail_out))return n.last_flush=-1,m}return t!==f?m:n.wrap<=0?1:(2===n.wrap?(U(n,255&e.adler),U(n,e.adler>>8&255),U(n,e.adler>>16&255),U(n,e.adler>>24&255),U(n,255&e.total_in),U(n,e.total_in>>8&255),U(n,e.total_in>>16&255),U(n,e.total_in>>24&255)):(P(n,e.adler>>>16),P(n,65535&e.adler)),F(e),0=r.w_size&&(0===s&&(D(r.head),r.strstart=0,r.block_start=0,r.insert=0),u=new c.Buf8(r.w_size),c.arraySet(u,t,l-r.w_size,r.w_size,0),t=u,l=r.w_size),a=e.avail_in,o=e.next_in,h=e.input,e.avail_in=l,e.next_in=0,e.input=t,j(r);r.lookahead>=x;){for(n=r.strstart,i=r.lookahead-(x-1);r.ins_h=(r.ins_h<>>=y=v>>>24,p-=y,0===(y=v>>>16&255))C[s++]=65535&v;else{if(!(16&y)){if(0==(64&y)){v=m[(65535&v)+(d&(1<>>=y,p-=y),p<15&&(d+=z[n++]<>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(d&(1<>>=y,p-=y,(y=s-a)>3,d&=(1<<(p-=w<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function s(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new I.Buf16(320),this.work=new I.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function a(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=P,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new I.Buf32(n),t.distcode=t.distdyn=new I.Buf32(i),t.sane=1,t.back=-1,N):U}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,a(e)):U}function h(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15=s.wsize?(I.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(n<(i=s.wsize-s.wnext)&&(i=n),I.arraySet(s.window,t,r-n,i,s.wnext),(n-=i)?(I.arraySet(s.window,t,r-n,n,0),s.wnext=n,s.whave=s.wsize):(s.wnext+=i,s.wnext===s.wsize&&(s.wnext=0),s.whave>>8&255,r.check=B(r.check,E,2,0),l=u=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&u)<<8)+(u>>8))%31){e.msg="incorrect header check",r.mode=30;break}if(8!=(15&u)){e.msg="unknown compression method",r.mode=30;break}if(l-=4,k=8+(15&(u>>>=4)),0===r.wbits)r.wbits=k;else if(k>r.wbits){e.msg="invalid window size",r.mode=30;break}r.dmax=1<>8&1),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=3;case 3:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>8&255,E[2]=u>>>16&255,E[3]=u>>>24&255,r.check=B(r.check,E,4,0)),l=u=0,r.mode=4;case 4:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>8),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=5;case 5:if(1024&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<>>8&255,r.check=B(r.check,E,2,0)),l=u=0}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&(o<(d=r.length)&&(d=o),d&&(r.head&&(k=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),I.arraySet(r.head.extra,n,s,d,k)),512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,r.length-=d),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&d>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break e;o--,u+=n[s++]<>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==t)break;u>>>=2,l-=2;break e;case 2:r.mode=17;break;case 3:e.msg="invalid block type",r.mode=30}u>>>=2,l-=2;break;case 14:for(u>>>=7&l,l-=7&l;l<32;){if(0===o)break e;o--,u+=n[s++]<>>16^65535)){e.msg="invalid stored block lengths",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(d=r.length){if(o>>=5,l-=5,r.ndist=1+(31&u),u>>>=5,l-=5,r.ncode=4+(15&u),u>>>=4,l-=4,286>>=3,l-=3}for(;r.have<19;)r.lens[A[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,S={bits:r.lenbits},x=T(0,r.lens,0,19,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid code lengths set",r.mode=30;break}r.have=0,r.mode=19;case 19:for(;r.have>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=_,l-=_,r.lens[r.have++]=b;else{if(16===b){for(z=_+2;l>>=_,l-=_,0===r.have){e.msg="invalid bit length repeat",r.mode=30;break}k=r.lens[r.have-1],d=3+(3&u),u>>>=2,l-=2}else if(17===b){for(z=_+3;l>>=_)),u>>>=3,l-=3}else{for(z=_+7;l>>=_)),u>>>=7,l-=7}if(r.have+d>r.nlen+r.ndist){e.msg="invalid bit length repeat",r.mode=30;break}for(;d--;)r.lens[r.have++]=k}}if(30===r.mode)break;if(0===r.lens[256]){e.msg="invalid code -- missing end-of-block",r.mode=30;break}if(r.lenbits=9,S={bits:r.lenbits},x=T(D,r.lens,0,r.nlen,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid literal/lengths set",r.mode=30;break}if(r.distbits=6,r.distcode=r.distdyn,S={bits:r.distbits},x=T(F,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,S),r.distbits=S.bits,x){e.msg="invalid distances set",r.mode=30;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(6<=o&&258<=h){e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,R(e,c),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,12===r.mode&&(r.back=-1);break}for(r.back=0;g=(C=r.lencode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,r.length=b,0===g){r.mode=26;break}if(32&g){r.back=-1,r.mode=12;break}if(64&g){e.msg="invalid literal/length code",r.mode=30;break}r.extra=15&g,r.mode=22;case 22:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}r.was=r.length,r.mode=23;case 23:for(;g=(C=r.distcode[u&(1<>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,64&g){e.msg="invalid distance code",r.mode=30;break}r.offset=b,r.extra=15&g,r.mode=24;case 24:if(r.extra){for(z=r.extra;l>>=r.extra,l-=r.extra,r.back+=r.extra}if(r.offset>r.dmax){e.msg="invalid distance too far back",r.mode=30;break}r.mode=25;case 25:if(0===h)break e;if(d=c-h,r.offset>d){if((d=r.offset-d)>r.whave&&r.sane){e.msg="invalid distance too far back",r.mode=30;break}p=d>r.wnext?(d-=r.wnext,r.wsize-d):r.wnext-d,d>r.length&&(d=r.length),m=r.window}else m=i,p=a-r.offset,d=r.length;for(hd?(m=R[T+a[v]],A[I+a[v]]):(m=96,0),h=1<>S)+(u-=h)]=p<<24|m<<16|_|0,0!==u;);for(h=1<>=1;if(0!==h?(E&=h-1,E+=h):E=0,v++,0==--O[b]){if(b===w)break;b=t[r+a[v]]}if(k>>7)]}function U(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255}function P(e,t,r){e.bi_valid>d-r?(e.bi_buf|=t<>d-e.bi_valid,e.bi_valid+=r-d):(e.bi_buf|=t<>>=1,r<<=1,0<--t;);return r>>>1}function Z(e,t,r){var n,i,s=new Array(g+1),a=0;for(n=1;n<=g;n++)s[n]=a=a+r[n-1]<<1;for(i=0;i<=t;i++){var o=e[2*i+1];0!==o&&(e[2*i]=j(s[o]++,o))}}function W(e){var t;for(t=0;t>1;1<=r;r--)G(e,s,r);for(i=h;r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],G(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,s[2*i]=s[2*r]+s[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,s[2*r+1]=s[2*n+1]=i,e.heap[1]=i++,G(e,s,1),2<=e.heap_len;);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,s,a,o,h=t.dyn_tree,u=t.max_code,l=t.stat_desc.static_tree,f=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,m=0;for(s=0;s<=g;s++)e.bl_count[s]=0;for(h[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<_;r++)p<(s=h[2*h[2*(n=e.heap[r])+1]+1]+1)&&(s=p,m++),h[2*n+1]=s,u>=7;n>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return o;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return h;for(t=32;t>>3,(s=e.static_len+3+7>>>3)<=i&&(i=s)):i=s=r+5,r+4<=i&&-1!==t?J(e,t,r,n):4===e.strategy||s===i?(P(e,2+(n?1:0),3),K(e,z,C)):(P(e,4+(n?1:0),3),function(e,t,r,n){var i;for(P(e,t-257,5),P(e,r-1,5),P(e,n-4,4),i=0;i>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(A[r]+u+1)]++,e.dyn_dtree[2*N(t)]++),e.last_lit===e.lit_bufsize-1},r._tr_align=function(e){P(e,2,3),L(e,m,z),function(e){16===e.bi_valid?(U(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}(e)}},{"../utils/common":41}],53:[function(e,t,r){"use strict";t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],54:[function(e,t,r){(function(e){!function(r,n){"use strict";if(!r.setImmediate){var i,s,t,a,o=1,h={},u=!1,l=r.document,e=Object.getPrototypeOf&&Object.getPrototypeOf(r);e=e&&e.setTimeout?e:r,i="[object process]"==={}.toString.call(r.process)?function(e){process.nextTick(function(){c(e)})}:function(){if(r.postMessage&&!r.importScripts){var e=!0,t=r.onmessage;return r.onmessage=function(){e=!1},r.postMessage("","*"),r.onmessage=t,e}}()?(a="setImmediate$"+Math.random()+"$",r.addEventListener?r.addEventListener("message",d,!1):r.attachEvent("onmessage",d),function(e){r.postMessage(a+e,"*")}):r.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){c(e.data)},function(e){t.port2.postMessage(e)}):l&&"onreadystatechange"in l.createElement("script")?(s=l.documentElement,function(e){var t=l.createElement("script");t.onreadystatechange=function(){c(e),t.onreadystatechange=null,s.removeChild(t),t=null},s.appendChild(t)}):function(e){setTimeout(c,0,e)},e.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r { - var _a; - let chunkSize; - if (controller.desiredSize) { - const d = controller.desiredSize / this.endpoint_.packetSize; - chunkSize = Math.ceil(d) * this.endpoint_.packetSize; - } - else { - chunkSize = this.endpoint_.packetSize; - } - try { - const result = await this.device_.transferIn(this.endpoint_.endpointNumber, chunkSize); - if (result.status != 'ok') { - controller.error(`USB error: ${result.status}`); - this.onError_(); - } - if ((_a = result.data) === null || _a === void 0 ? void 0 : _a.buffer) { - const chunk = new Uint8Array(result.data.buffer, result.data.byteOffset, result.data.byteLength); - controller.enqueue(chunk); - } - } - catch (error) { - controller.error(error.toString()); - this.onError_(); - } - })(); - } -} -/** - * Implementation of the underlying sink API[2] which writes data to a USB - * endpoint. This can be used to construct a WritableStream. - * - * [2]: https://streams.spec.whatwg.org/#underlying-sink-api - */ -class UsbEndpointUnderlyingSink { - /** - * Constructs a new UnderlyingSink that will write data to the specified - * endpoint on the given USB device. - * - * @param {USBDevice} device - * @param {USBEndpoint} endpoint - * @param {function} onError function to be called on error - */ - constructor(device, endpoint, onError) { - this.device_ = device; - this.endpoint_ = endpoint; - this.onError_ = onError; - } - /** - * Writes a chunk to the device. - * - * @param {Uint8Array} chunk - * @param {WritableStreamDefaultController} controller - */ - async write(chunk, controller) { - try { - const result = await this.device_.transferOut(this.endpoint_.endpointNumber, chunk); - if (result.status != 'ok') { - controller.error(result.status); - this.onError_(); - } - } - catch (error) { - controller.error(error.toString()); - this.onError_(); - } - } -} -/** a class used to control serial devices over WebUSB */ -export class SerialPort { - /** - * constructor taking a WebUSB device that creates a SerialPort instance. - * @param {USBDevice} device A device acquired from the WebUSB API - * @param {SerialPolyfillOptions} polyfillOptions Optional options to - * configure the polyfill. - */ - constructor(device, polyfillOptions) { - this.polyfillOptions_ = Object.assign(Object.assign({}, kDefaultPolyfillOptions), polyfillOptions); - this.outputSignals_ = { - dataTerminalReady: false, - requestToSend: false, - break: false, - }; - this.device_ = device; - this.controlInterface_ = findInterface(this.device_, this.polyfillOptions_.usbControlInterfaceClass); - this.transferInterface_ = findInterface(this.device_, this.polyfillOptions_.usbTransferInterfaceClass); - this.inEndpoint_ = findEndpoint(this.transferInterface_, 'in'); - this.outEndpoint_ = findEndpoint(this.transferInterface_, 'out'); - } - /** - * Getter for the readable attribute. Constructs a new ReadableStream as - * necessary. - * @return {ReadableStream} the current readable stream - */ - get readable() { - var _a; - if (!this.readable_ && this.device_.opened) { - this.readable_ = new ReadableStream(new UsbEndpointUnderlyingSource(this.device_, this.inEndpoint_, () => { - this.readable_ = null; - }), { - highWaterMark: (_a = this.serialOptions_.bufferSize) !== null && _a !== void 0 ? _a : kDefaultBufferSize, - }); - } - return this.readable_; - } - /** - * Getter for the writable attribute. Constructs a new WritableStream as - * necessary. - * @return {WritableStream} the current writable stream - */ - get writable() { - var _a; - if (!this.writable_ && this.device_.opened) { - this.writable_ = new WritableStream(new UsbEndpointUnderlyingSink(this.device_, this.outEndpoint_, () => { - this.writable_ = null; - }), new ByteLengthQueuingStrategy({ - highWaterMark: (_a = this.serialOptions_.bufferSize) !== null && _a !== void 0 ? _a : kDefaultBufferSize, - })); - } - return this.writable_; - } - /** - * a function that opens the device and claims all interfaces needed to - * control and communicate to and from the serial device - * @param {SerialOptions} options Object containing serial options - * @return {Promise} A promise that will resolve when device is ready - * for communication - */ - async open(options) { - this.serialOptions_ = options; - this.validateOptions(); - try { - await this.device_.open(); - if (this.device_.configuration === null) { - await this.device_.selectConfiguration(1); - } - await this.device_.claimInterface(this.controlInterface_.interfaceNumber); - if (this.controlInterface_ !== this.transferInterface_) { - await this.device_.claimInterface(this.transferInterface_.interfaceNumber); - } - await this.setLineCoding(); - await this.setSignals({ dataTerminalReady: true }); - } - catch (error) { - if (this.device_.opened) { - await this.device_.close(); - } - throw new Error('Error setting up device: ' + error.toString()); - } - } - /** - * Closes the port. - * - * @return {Promise} A promise that will resolve when the port is - * closed. - */ - async close() { - const promises = []; - if (this.readable_) { - promises.push(this.readable_.cancel()); - } - if (this.writable_) { - promises.push(this.writable_.abort()); - } - await Promise.all(promises); - this.readable_ = null; - this.writable_ = null; - if (this.device_.opened) { - await this.setSignals({ dataTerminalReady: false, requestToSend: false }); - await this.device_.close(); - } - } - /** - * Forgets the port. - * - * @return {Promise} A promise that will resolve when the port is - * forgotten. - */ - async forget() { - return this.device_.forget(); - } - /** - * A function that returns properties of the device. - * @return {SerialPortInfo} Device properties. - */ - getInfo() { - return { - usbVendorId: this.device_.vendorId, - usbProductId: this.device_.productId, - }; - } - /** - * A function used to change the serial settings of the device - * @param {object} options the object which carries serial settings data - * @return {Promise} A promise that will resolve when the options are - * set - */ - reconfigure(options) { - this.serialOptions_ = Object.assign(Object.assign({}, this.serialOptions_), options); - this.validateOptions(); - return this.setLineCoding(); - } - /** - * Sets control signal state for the port. - * @param {SerialOutputSignals} signals The signals to enable or disable. - * @return {Promise} a promise that is resolved when the signal state - * has been changed. - */ - async setSignals(signals) { - this.outputSignals_ = Object.assign(Object.assign({}, this.outputSignals_), signals); - if (signals.dataTerminalReady !== undefined || - signals.requestToSend !== undefined) { - // The Set_Control_Line_State command expects a bitmap containing the - // values of all output signals that should be enabled or disabled. - // - // Ref: USB CDC specification version 1.1 ยง6.2.14. - const value = (this.outputSignals_.dataTerminalReady ? 1 << 0 : 0) | - (this.outputSignals_.requestToSend ? 1 << 1 : 0); - await this.device_.controlTransferOut({ - 'requestType': 'class', - 'recipient': 'interface', - 'request': kSetControlLineState, - 'value': value, - 'index': this.controlInterface_.interfaceNumber, - }); - } - if (signals.break !== undefined) { - // The SendBreak command expects to be given a duration for how long the - // break signal should be asserted. Passing 0xFFFF enables the signal - // until 0x0000 is send. - // - // Ref: USB CDC specification version 1.1 ยง6.2.15. - const value = this.outputSignals_.break ? 0xFFFF : 0x0000; - await this.device_.controlTransferOut({ - 'requestType': 'class', - 'recipient': 'interface', - 'request': kSendBreak, - 'value': value, - 'index': this.controlInterface_.interfaceNumber, - }); - } - } - /** - * Checks the serial options for validity and throws an error if it is - * not valid - */ - validateOptions() { - if (!this.isValidBaudRate(this.serialOptions_.baudRate)) { - throw new RangeError('invalid Baud Rate ' + this.serialOptions_.baudRate); - } - if (!this.isValidDataBits(this.serialOptions_.dataBits)) { - throw new RangeError('invalid dataBits ' + this.serialOptions_.dataBits); - } - if (!this.isValidStopBits(this.serialOptions_.stopBits)) { - throw new RangeError('invalid stopBits ' + this.serialOptions_.stopBits); - } - if (!this.isValidParity(this.serialOptions_.parity)) { - throw new RangeError('invalid parity ' + this.serialOptions_.parity); - } - } - /** - * Checks the baud rate for validity - * @param {number} baudRate the baud rate to check - * @return {boolean} A boolean that reflects whether the baud rate is valid - */ - isValidBaudRate(baudRate) { - return baudRate % 1 === 0; - } - /** - * Checks the data bits for validity - * @param {number} dataBits the data bits to check - * @return {boolean} A boolean that reflects whether the data bits setting is - * valid - */ - isValidDataBits(dataBits) { - if (typeof dataBits === 'undefined') { - return true; - } - return kAcceptableDataBits.includes(dataBits); - } - /** - * Checks the stop bits for validity - * @param {number} stopBits the stop bits to check - * @return {boolean} A boolean that reflects whether the stop bits setting is - * valid - */ - isValidStopBits(stopBits) { - if (typeof stopBits === 'undefined') { - return true; - } - return kAcceptableStopBits.includes(stopBits); - } - /** - * Checks the parity for validity - * @param {string} parity the parity to check - * @return {boolean} A boolean that reflects whether the parity is valid - */ - isValidParity(parity) { - if (typeof parity === 'undefined') { - return true; - } - return kAcceptableParity.includes(parity); - } - /** - * sends the options alog the control interface to set them on the device - * @return {Promise} a promise that will resolve when the options are set - */ - async setLineCoding() { - var _a, _b, _c; - // Ref: USB CDC specification version 1.1 ยง6.2.12. - const buffer = new ArrayBuffer(7); - const view = new DataView(buffer); - view.setUint32(0, this.serialOptions_.baudRate, true); - view.setUint8(4, kStopBitsIndexMapping.indexOf((_a = this.serialOptions_.stopBits) !== null && _a !== void 0 ? _a : kDefaultStopBits)); - view.setUint8(5, kParityIndexMapping.indexOf((_b = this.serialOptions_.parity) !== null && _b !== void 0 ? _b : kDefaultParity)); - view.setUint8(6, (_c = this.serialOptions_.dataBits) !== null && _c !== void 0 ? _c : kDefaultDataBits); - const result = await this.device_.controlTransferOut({ - 'requestType': 'class', - 'recipient': 'interface', - 'request': kSetLineCoding, - 'value': 0x00, - 'index': this.controlInterface_.interfaceNumber, - }, buffer); - if (result.status != 'ok') { - throw new DOMException('NetworkError', 'Failed to set line coding.'); - } - } -} -/** implementation of the global navigator.serial object */ -class Serial { - /** - * Requests permission to access a new port. - * - * @param {SerialPortRequestOptions} options - * @param {SerialPolyfillOptions} polyfillOptions - * @return {Promise} - */ - async requestPort(options, polyfillOptions) { - polyfillOptions = Object.assign(Object.assign({}, kDefaultPolyfillOptions), polyfillOptions); - const usbFilters = []; - if (options && options.filters) { - for (const filter of options.filters) { - const usbFilter = { - classCode: polyfillOptions.usbControlInterfaceClass, - }; - if (filter.usbVendorId !== undefined) { - usbFilter.vendorId = filter.usbVendorId; - } - if (filter.usbProductId !== undefined) { - usbFilter.productId = filter.usbProductId; - } - usbFilters.push(usbFilter); - } - } - if (usbFilters.length === 0) { - usbFilters.push({ - classCode: polyfillOptions.usbControlInterfaceClass, - }); - } - const device = await navigator.usb.requestDevice({ 'filters': usbFilters }); - const port = new SerialPort(device, polyfillOptions); - return port; - } - /** - * Get the set of currently available ports. - * - * @param {SerialPolyfillOptions} polyfillOptions Polyfill configuration that - * should be applied to these ports. - * @return {Promise} a promise that is resolved with a list of - * ports. - */ - async getPorts(polyfillOptions) { - polyfillOptions = Object.assign(Object.assign({}, kDefaultPolyfillOptions), polyfillOptions); - const devices = await navigator.usb.getDevices(); - const ports = []; - devices.forEach((device) => { - try { - const port = new SerialPort(device, polyfillOptions); - ports.push(port); - } - catch (e) { - // Skip unrecognized port. - } - }); - return ports; - } -} -/* an object to be used for starting the serial workflow */ -export const serial = new Serial(); -//# sourceMappingURL=serial.js.map \ No newline at end of file