Repository: Kitware/itk-vtk-viewer Branch: master Commit: 1c373bfcbb50 Files: 400 Total size: 2.1 MB Directory structure: gitextract_6jojsqhp/ ├── .babelrc ├── .editorconfig ├── .eslintrc.js ├── .github/ │ └── workflows/ │ ├── build-test.yml │ └── publish.yml ├── .gitignore ├── .gitmodules ├── .npmignore ├── .prettierrc ├── CONTRIBUTING.md ├── Copyright.txt ├── LICENSE ├── README.md ├── bin/ │ ├── itk-vtk-viewer-cli.js │ ├── network.js │ └── server.js ├── buildUI.js ├── doc/ │ ├── .gitignore │ ├── config.js │ ├── content/ │ │ ├── api/ │ │ │ └── index.md │ │ ├── config/ │ │ │ └── index.md │ │ ├── docs/ │ │ │ ├── cli.md │ │ │ ├── customizeUI.md │ │ │ ├── embeddedViewer.md │ │ │ ├── imjoy.md │ │ │ ├── index.md │ │ │ ├── shortcuts.md │ │ │ └── viewer.md │ │ ├── index.html │ │ └── index.jade │ ├── data/ │ │ └── menu.yml │ └── tpl/ │ ├── __en__ │ └── __sidebar__ ├── examples/ │ ├── compare-image.html │ ├── cyan-magenta-compare-image.html │ ├── test-conglomerate.html │ ├── test-pointsets.html │ └── test.html ├── karma.conf.js ├── package.json ├── postcss.config.js ├── src/ │ ├── Compression/ │ │ ├── blosc-zarr/ │ │ │ ├── BloscZarr.cxx │ │ │ ├── CMakeLists.txt │ │ │ └── web-build/ │ │ │ ├── BloscZarr.js │ │ │ ├── BloscZarr.umd.js │ │ │ └── BloscZarr.wasm │ │ └── bloscZarrDecompress.js │ ├── Context/ │ │ ├── ImageActorContext.js │ │ ├── ImagesMachineContext.js │ │ ├── LayerActorContext.js │ │ ├── LayersMachineContext.js │ │ ├── MainMachineContext.js │ │ ├── ViewerMachineContext.js │ │ └── WidgetsMachineContext.js │ ├── IO/ │ │ ├── Analyze/ │ │ │ ├── ComputeRanges.worker.js │ │ │ ├── UpdateHistogram.worker.js │ │ │ ├── computeHistograms.js │ │ │ ├── computeRanges.js │ │ │ ├── createRangeHelper.js │ │ │ └── webWorkerPromiseWorkerPool.js │ │ ├── Compare/ │ │ │ ├── .gitignore │ │ │ ├── CMakeLists.txt │ │ │ ├── Compare.cxx │ │ │ ├── createCompareImage.js │ │ │ ├── emscripten-build/ │ │ │ │ ├── Compare.js │ │ │ │ ├── Compare.umd.js │ │ │ │ ├── Compare.umd.wasm │ │ │ │ └── Compare.wasm │ │ │ ├── index.mjs │ │ │ └── package.json │ │ ├── ConglomerateMultiscaleSpatialImage.js │ │ ├── Downsample/ │ │ │ ├── .gitignore │ │ │ ├── CMakeLists.txt │ │ │ ├── Downsample.cxx │ │ │ ├── DownsampleLabelImage.cxx │ │ │ ├── cypress/ │ │ │ │ ├── integration/ │ │ │ │ │ └── load_data_spec.js │ │ │ │ └── plugins/ │ │ │ │ └── index.cjs │ │ │ ├── cypress.json │ │ │ ├── downsample.js │ │ │ ├── emscripten-build/ │ │ │ │ ├── Downsample.js │ │ │ │ ├── Downsample.umd.js │ │ │ │ ├── Downsample.umd.wasm │ │ │ │ ├── Downsample.wasm │ │ │ │ ├── DownsampleLabelImage.js │ │ │ │ ├── DownsampleLabelImage.umd.js │ │ │ │ ├── DownsampleLabelImage.umd.wasm │ │ │ │ └── DownsampleLabelImage.wasm │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ └── styles.css │ │ ├── HttpStore.js │ │ ├── ImageDataFromChunks.worker.js │ │ ├── InMemoryMultiscaleSpatialImage.js │ │ ├── MultiscaleSpatialImage.js │ │ ├── ResampleLabelImage/ │ │ │ ├── .gitignore │ │ │ ├── CMakeLists.txt │ │ │ ├── ResampleLabelImage.cxx │ │ │ ├── emscripten-build/ │ │ │ │ ├── ResampleLabelImage.js │ │ │ │ ├── ResampleLabelImage.umd.js │ │ │ │ ├── ResampleLabelImage.umd.wasm │ │ │ │ └── ResampleLabelImage.wasm │ │ │ ├── index.mjs │ │ │ ├── package.json │ │ │ └── resampleLabelImage.js │ │ ├── ZarrMultiscaleSpatialImage.js │ │ ├── ZarrStoreParser.js │ │ ├── componentTypeToTypedArray.js │ │ ├── composeComponents.js │ │ ├── dimensionUtils.js │ │ ├── dtypeUtils.js │ │ ├── fetchBinaryContent.js │ │ ├── fetchJsonContent.js │ │ ├── itkWasmUtils.js │ │ ├── ndarrayToItkImage.js │ │ ├── ndarrayToPointSet.js │ │ ├── processFiles.js │ │ ├── toMultiscaleSpatialImage.js │ │ └── uploadFileHandler.js │ ├── ImJoyPluginAPI.js │ ├── Rendering/ │ │ ├── Images/ │ │ │ ├── createImageRenderingActor.js │ │ │ ├── createImagesRenderingMachine.js │ │ │ └── makeTransitions.js │ │ ├── Layers/ │ │ │ └── createLayersRenderingMachine.js │ │ ├── Main/ │ │ │ ├── backgroundIsDark.js │ │ │ ├── backgroundIsLight.js │ │ │ └── createMainRenderingMachine.js │ │ ├── VTKJS/ │ │ │ ├── Images/ │ │ │ │ ├── ComposeImage.worker.js │ │ │ │ ├── applyBlendMode.js │ │ │ │ ├── applyCinematicChanged.js │ │ │ │ ├── applyColorMap.js │ │ │ │ ├── applyColorRange.js │ │ │ │ ├── applyColorRangeBounds.js │ │ │ │ ├── applyComponentVisibility.js │ │ │ │ ├── applyComponentWeights.js │ │ │ │ ├── applyGradientOpacity.js │ │ │ │ ├── applyIndependentComponents.js │ │ │ │ ├── applyLabelImageBlend.js │ │ │ │ ├── applyLabelImageWeights.js │ │ │ │ ├── applyLabelNames.js │ │ │ │ ├── applyLookupTable.js │ │ │ │ ├── applyPiecewiseFunction.js │ │ │ │ ├── applyRenderedImage.js │ │ │ │ ├── applySelectedLabel.js │ │ │ │ ├── applyShadow.js │ │ │ │ ├── applyVolumeSampleDistance.js │ │ │ │ ├── assignRenderedImage.js │ │ │ │ ├── assignVisualizedComponents.js │ │ │ │ ├── createImageRenderer.js │ │ │ │ ├── fuseImages.js │ │ │ │ ├── imagesRenderingMachineOptions.js │ │ │ │ ├── mapToColorFunctionRange.js │ │ │ │ ├── mapToPiecewiseFunctionNodes.js │ │ │ │ ├── selectImageLayer.js │ │ │ │ ├── toggleInterpolation.js │ │ │ │ ├── toggleLayerBBox.js │ │ │ │ ├── toggleLayerVisibility.js │ │ │ │ ├── transformLabelImageWeight.js │ │ │ │ ├── updateHistogram.js │ │ │ │ ├── updateLabelImagePiecewiseFunction.js │ │ │ │ └── updateRenderedImage.js │ │ │ ├── Layers/ │ │ │ │ └── layersRenderingMachineOptions.js │ │ │ ├── Main/ │ │ │ │ ├── applyCroppingPlanes.js │ │ │ │ ├── applySlicingPlanes.js │ │ │ │ ├── applyXSlice.js │ │ │ │ ├── applyYSlice.js │ │ │ │ ├── applyZSlice.js │ │ │ │ ├── computeRenderedBounds.js │ │ │ │ ├── createMainRenderer.js │ │ │ │ ├── croppingPlanes.js │ │ │ │ ├── mainRenderingMachineOptions.js │ │ │ │ ├── resetCamera.js │ │ │ │ ├── resetCroppingPlanes.js │ │ │ │ ├── setBackgroundColor.js │ │ │ │ ├── setUnits.js │ │ │ │ ├── takeScreenshot.js │ │ │ │ ├── toggleAnnotations.js │ │ │ │ ├── toggleAxes.js │ │ │ │ ├── toggleCroppingPlanes.js │ │ │ │ ├── toggleRotate.js │ │ │ │ ├── updateFps.js │ │ │ │ ├── updateSlicingPlanes.js │ │ │ │ ├── viewModeVolume.js │ │ │ │ ├── viewModeXPlane.js │ │ │ │ ├── viewModeYPlane.js │ │ │ │ └── viewModeZPlane.js │ │ │ ├── Widgets/ │ │ │ │ ├── DistanceWidget/ │ │ │ │ │ ├── DistanceWidget.js │ │ │ │ │ └── state.js │ │ │ │ ├── HandlesInPixelsImageCroppingWidget.js │ │ │ │ ├── createWidgets.js │ │ │ │ ├── toggleDistanceWidget.js │ │ │ │ └── widgetsRenderingMachineOptions.js │ │ │ ├── cancelAnimation.js │ │ │ ├── createRenderer.js │ │ │ ├── numericalSort.js │ │ │ ├── proxyManagerConfiguration.js │ │ │ ├── render.js │ │ │ ├── requestAnimation.js │ │ │ ├── vtk/ │ │ │ │ ├── AxesLabelsWidget/ │ │ │ │ │ ├── behavior.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── state.js │ │ │ │ ├── ItkVtkViewProxy.js │ │ │ │ ├── OpenGLImageMapperFractional.js │ │ │ │ ├── PointSetRepresentationProxy/ │ │ │ │ │ └── index.js │ │ │ │ ├── SVGMarkerTextRepresentation/ │ │ │ │ │ └── index.js │ │ │ │ ├── SVGRepresentation/ │ │ │ │ │ └── index.js │ │ │ │ ├── SliceOutlineFilter/ │ │ │ │ │ └── index.js │ │ │ │ └── WidgetManagerPickWhileAnimating.js │ │ │ └── vtkJSRenderingMachineOptions.js │ │ ├── Widgets/ │ │ │ └── createWidgetsRenderingMachine.js │ │ ├── createRenderingMachine.js │ │ └── updateLabelMapComponentWeight.js │ ├── UI/ │ │ ├── Images/ │ │ │ ├── createImagesUIMachine.js │ │ │ └── transferFunctionManipulators.js │ │ ├── Layers/ │ │ │ ├── createLayerUIActor.js │ │ │ └── createLayersUIMachine.js │ │ ├── Main/ │ │ │ └── createMainUIMachine.js │ │ ├── Widgets/ │ │ │ └── createWidgetsUIMachine.js │ │ ├── addKeyboardShortcuts.js │ │ ├── createRenderingViewContainers.js │ │ ├── createUIMachine.js │ │ ├── reference-ui/ │ │ │ ├── .babelrc │ │ │ ├── package.json │ │ │ ├── rollup.config.js │ │ │ ├── src/ │ │ │ │ ├── Images/ │ │ │ │ │ ├── applyBlendMode.js │ │ │ │ │ ├── applyColorMap.js │ │ │ │ │ ├── applyColorRange.js │ │ │ │ │ ├── applyColorRangeBounds.js │ │ │ │ │ ├── applyComponentVisibility.js │ │ │ │ │ ├── applyGradientOpacity.js │ │ │ │ │ ├── applyGradientOpacityScale.js │ │ │ │ │ ├── applyHistogram.js │ │ │ │ │ ├── applyImagesContrastSensitiveStyle.js │ │ │ │ │ ├── applyLabelImageBlend.js │ │ │ │ │ ├── applyLabelImageWeights.js │ │ │ │ │ ├── applyLabelNames.js │ │ │ │ │ ├── applyLookupTable.js │ │ │ │ │ ├── applyPiecewiseFunctionGaussians.js │ │ │ │ │ ├── applySelectedLabel.js │ │ │ │ │ ├── applyVolumeSampleDistance.js │ │ │ │ │ ├── applyWindowingReset.js │ │ │ │ │ ├── cinematic.js │ │ │ │ │ ├── createBlendModeSelector.js │ │ │ │ │ ├── createColorRangeInput.js │ │ │ │ │ ├── createComponentSelector.js │ │ │ │ │ ├── createGradientOpacitySlider.js │ │ │ │ │ ├── createImagesInterface.js │ │ │ │ │ ├── createInterpolationButton.js │ │ │ │ │ ├── createLabelImageColorWidget.js │ │ │ │ │ ├── createLabelImageWeightWidget.js │ │ │ │ │ ├── createSampleDistanceSlider.js │ │ │ │ │ ├── createShadowToggle.js │ │ │ │ │ ├── createTransferFunctionEditor.js │ │ │ │ │ ├── createTransferFunctionWidget.js │ │ │ │ │ ├── createVolumeRenderingInputs.js │ │ │ │ │ ├── createWindowLevelReset.js │ │ │ │ │ ├── createWindowLevelToggle.js │ │ │ │ │ ├── imagesUIMachineOptions.js │ │ │ │ │ ├── scaleSelector.js │ │ │ │ │ ├── selectImageComponent.js │ │ │ │ │ ├── throttle.js │ │ │ │ │ ├── toggleInterpolation.js │ │ │ │ │ ├── toggleShadow.js │ │ │ │ │ ├── toggleUseShadow.js │ │ │ │ │ ├── toggleWindowLevel.js │ │ │ │ │ ├── updateAvailableComponents.js │ │ │ │ │ ├── updateImageInterface.js │ │ │ │ │ ├── updateLabelImageInterface.js │ │ │ │ │ └── updateRenderedImageInterface.js │ │ │ │ ├── ItkVtkViewer.module.css │ │ │ │ ├── Layers/ │ │ │ │ │ ├── addLayerUIRow.js │ │ │ │ │ ├── applyLayersContrastSensitiveStyle.js │ │ │ │ │ ├── compareUI.js │ │ │ │ │ ├── createLayerInterface.js │ │ │ │ │ ├── createLayersInterface.js │ │ │ │ │ ├── dataUpdateIndicator.js │ │ │ │ │ ├── extensionToImageIo.js │ │ │ │ │ ├── layerIcon.ts │ │ │ │ │ ├── layerSettings.ts │ │ │ │ │ ├── layersUIMachineOptions.js │ │ │ │ │ ├── selectLayer.js │ │ │ │ │ └── toggleLayerVisibility.js │ │ │ │ ├── Main/ │ │ │ │ │ ├── applyMainContrastSensitiveStyle.js │ │ │ │ │ ├── applySlicingPlanes.js │ │ │ │ │ ├── applyXSlice.js │ │ │ │ │ ├── applyYSlice.js │ │ │ │ │ ├── applyZSlice.js │ │ │ │ │ ├── createAnnotationsButton.js │ │ │ │ │ ├── createAxesButton.js │ │ │ │ │ ├── createBackgroundColorButton.js │ │ │ │ │ ├── createCroppingButtons.js │ │ │ │ │ ├── createFullscreenButton.js │ │ │ │ │ ├── createMainInterface.js │ │ │ │ │ ├── createPlaneSliders.js │ │ │ │ │ ├── createResetCameraButton.js │ │ │ │ │ ├── createRotateButton.js │ │ │ │ │ ├── createScreenshotButton.js │ │ │ │ │ ├── createViewModeButtons.js │ │ │ │ │ ├── createViewPlanesToggle.js │ │ │ │ │ ├── fullscreenMethods.js │ │ │ │ │ ├── mainUIMachineOptions.js │ │ │ │ │ ├── resetCrop.js │ │ │ │ │ ├── toggleAnnotations.js │ │ │ │ │ ├── toggleAxes.js │ │ │ │ │ ├── toggleBackgroundColor.js │ │ │ │ │ ├── toggleCrop.js │ │ │ │ │ ├── toggleCroppingPlanes.js │ │ │ │ │ ├── toggleFullscreen.js │ │ │ │ │ ├── toggleRotate.js │ │ │ │ │ ├── viewModeVolume.js │ │ │ │ │ ├── viewModeXPlane.js │ │ │ │ │ ├── viewModeYPlane.js │ │ │ │ │ └── viewModeZPlane.js │ │ │ │ ├── Widgets/ │ │ │ │ │ ├── applyDistanceWidgetValue.js │ │ │ │ │ ├── applyWidgetsContrastSensitiveStyle.js │ │ │ │ │ ├── createDistanceWidget.js │ │ │ │ │ ├── createWidgetsInterface.js │ │ │ │ │ ├── toggleDistanceWidget.js │ │ │ │ │ ├── viewModeVolume.js │ │ │ │ │ ├── viewModeXPlane.js │ │ │ │ │ ├── viewModeYPlane.js │ │ │ │ │ ├── viewModeZPlane.js │ │ │ │ │ └── widgetsUIMachineOptions.js │ │ │ │ ├── applyCategoricalColorToColorTransferFunction.js │ │ │ │ ├── applyContrastSensitiveStyleToElement.js │ │ │ │ ├── applyGroupVisibility.js │ │ │ │ ├── collapse-ui.ts │ │ │ │ ├── context.ts │ │ │ │ ├── createCategoricalColorIconSelector.js │ │ │ │ ├── createColorMapIconSelector.js │ │ │ │ ├── createInterface.js │ │ │ │ ├── referenceUIMachineOptions.js │ │ │ │ ├── serviceContext.ts │ │ │ │ ├── shims.d.ts │ │ │ │ ├── toggleDarkMode.js │ │ │ │ ├── toggleUICollapsed.js │ │ │ │ └── utils.js │ │ │ └── tsconfig.json │ │ └── styleRenderingViewContainers.js │ ├── UserInterface/ │ │ ├── CategoricalPresetNames.js │ │ ├── Geometries/ │ │ │ ├── createGeometryColorBySelector.js │ │ │ ├── createGeometryColorChooser.js │ │ │ ├── createGeometryColorRangeInput.js │ │ │ ├── createGeometryColorWidget.js │ │ │ ├── createGeometryOpacitySlider.js │ │ │ └── createGeometryRepresentationSelector.js │ │ ├── ItkVtkViewer.module.css │ │ ├── PointSets/ │ │ │ ├── createPointSetColorBySelector.js │ │ │ ├── createPointSetColorChooser.js │ │ │ ├── createPointSetColorRangeInput.js │ │ │ ├── createPointSetColorWidget.js │ │ │ ├── createPointSetOpacitySlider.js │ │ │ ├── createPointSetRepresentationSelector.js │ │ │ └── createPointSetSizeSlider.js │ │ ├── addLogo.js │ │ ├── applyContrastSensitiveStyle.js │ │ ├── checkForWebGL.js │ │ ├── createCategoricalColorIconSelector.js │ │ ├── createColorMapIconSelector.js │ │ ├── createFileDragAndDrop.js │ │ ├── createGeometriesUI.js │ │ ├── createLoadingProgress.js │ │ ├── createPointSetsUI.js │ │ ├── customColorMapIcon.js │ │ ├── emptyContainer.js │ │ ├── getContrastSensitiveStyle.js │ │ ├── getRootContainer.js │ │ ├── hex2rgb.js │ │ ├── index.js │ │ ├── preventDefaults.js │ │ └── rgb2hex.js │ ├── ViewerStore.js │ ├── createViewer.js │ ├── createViewerMachine.js │ ├── imJoyCodecs.js │ ├── index.d.ts │ ├── index.js │ ├── internalUtils.js │ ├── itkConfig.js │ ├── itkConfigCDN.js │ ├── transformBounds.js │ ├── utils.js │ └── viewerMachineOptions.js ├── test/ │ ├── conglomerateTest.js │ ├── convertItkImageToVtkImageTest.js │ ├── createViewerTest.js │ ├── customElementsDefineOverride.js │ ├── downloadData.mjs │ ├── imjoyTest.js │ ├── itkConfigBrowserTest.js │ ├── multiscaleSpatialImageTest.js │ ├── pipelineTest.js │ ├── processFilesTest.js │ ├── run.sh │ ├── test-ui-rollup.config.js │ ├── testUINoPlaneSliders.js │ ├── tests.js │ ├── zarrImageBaselines.js │ └── zarrTest.js ├── tsconfig.json └── webpack.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": [ [ "@babel/preset-env", { "targets": { "browsers": ["last 2 versions"] }, "useBuiltIns": false } ], "mobx" ], "plugins": [ "@babel/plugin-transform-runtime", ["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/plugin-proposal-class-properties", { "loose": false }] ] } ================================================ FILE: .editorconfig ================================================ # editorconfig.org root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.md] trim_trailing_whitespace = false ================================================ FILE: .eslintrc.js ================================================ module.exports = { env: { browser: true, es2021: true, }, extends: ['eslint:recommended', 'prettier'], parserOptions: { ecmaVersion: 'latest', sourceType: 'module', }, rules: {}, } ================================================ FILE: .github/workflows/build-test.yml ================================================ name: Build and Test on: [push, pull_request] jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-22.04, macos-13, windows-2022] node: [20, 22] name: ${{ matrix.os }} and node ${{ matrix.node }} steps: - uses: actions/checkout@v4 - name: Setup node uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} - name: Install dependencies run: npm ci - name: Download testing data shell: bash run: | curl -OL https://github.com/Kitware/itk-vtk-viewer/releases/download/v14.35.1/itk-vtk-viewer-testing-data.tar.gz tar xvzf ./itk-vtk-viewer-testing-data.tar.gz -C test/ - name: Build run: npm run build - name: Test if: ${{ runner.os == 'Linux' }} run: | npm run test:downloadData # Allow writing test/output.html sudo chmod -R 777 test # To debug, run `./test/run.sh -d` npm run test:headless ================================================ FILE: .github/workflows/publish.yml ================================================ name: Release on: push: branches: - master jobs: publish: name: Publish runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup node uses: actions/setup-node@v4 with: node-version: 22 - name: Install dependencies run: | npm ci sudo apt-get install xvfb - name: Download testing data shell: bash run: | curl -OL https://github.com/Kitware/itk-vtk-viewer/releases/download/v14.35.1/itk-vtk-viewer-testing-data.tar.gz tar xvzf ./itk-vtk-viewer-testing-data.tar.gz -C test/ rm ./itk-vtk-viewer-testing-data.tar.gz - name: Build run: npm run build - name: Chrome and Firefox tests run: | # Failing based on vtk.js piecewisegaussian? # xvfb-run --auto-servernum npm run test -- --browsers Chrome,Firefox # Allow writing test/output.html sudo chmod -R 777 test # To debug, run `./test/run.sh -d` npm run test:headless - name: Release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: | git config --global user.name "Github Actions" git config --global user.email "sebastien.jourdain@kitware.com" npm run semantic-release - name: Publish docs if: github.ref == 'refs/heads/master' env: GIT_PUBLISH_URL: https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/Kitware/itk-vtk-viewer.git run: npm run doc:publish ================================================ FILE: .gitignore ================================================ .DS_Store *.swp .npm-packages .npmrc yarn.lock npm-debug.log node_modules Data dist/ coverage/ test/output.html test/TESTS-*.xml test/testUINoPlaneSlidersBundle.js src/Compression/blosc-zarr/web-build/* !src/Compression/blosc-zarr/web-build/BloscZarr.js !src/Compression/blosc-zarr/web-build/BloscZarr.umd.js !src/Compression/blosc-zarr/web-build/BloscZarr.wasm test/data/ ================================================ FILE: .gitmodules ================================================ [submodule "src/blosc/c-blosc"] path = src/Compression/blosc-zarr/c-blosc url = https://github.com/Blosc/c-blosc ================================================ FILE: .npmignore ================================================ .DS_Store .gitmodules .npmrc .sass-cache .travis.yml doc npm-debug.log src/UI/reference-ui/node_modules src/Compression/blosc-zarr test src/Compression/blosc-zarr/web-build src/IO/Downsample/web-build .github dist/itk/image-io/node_modules dist/itk/mesh-io/node_modules dist/*.map ================================================ FILE: .prettierrc ================================================ { "endOfLine": "lf", "semi": false, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5" } ================================================ FILE: CONTRIBUTING.md ================================================ Contributing to ITK/VTK ImageViewer ==================================== This page documents at a very high level how to contribute to itk-vtk-viewer. 1. The itk-vtk-viewer source is maintained on Github at [github.com/kitware/itk-vtk-viewer](https://github.com/kitware/itk-vtk-viewer) 2. [Fork the repository] into your user's namespace on Github. 3. Create a local clone of the main repository: ```sh $ git clone https://github.com/kitware/itk-vtk-viewer.git $ cd itk-vtk-viewer ``` The main repository will be configured as your `origin` remote. 4. Run the setup script to prepare itk-vtk-viewer: ```sh $ npm install $ npm install -g commitizen ``` 5. Edit files and create commits (repeat as needed): ```sh $ edit file1 file2 file3 $ git add file1 file2 file3 $ npm run commit ``` 6. Push commits in your topic branch to your fork in Github: ```sh $ git push ``` 7. Visit your fork in Github, browse to the "**Pull Requests**" link on the left, and use the "**New Pull Request**" button in the upper right to create a Pull Request. For more information see: [Create a Pull Request] This project uses GitHub for code review and Travis-CI to test proposed patches before they are merged. Our [DevSite] is used to document features, flesh out designs and host other documentation as well as the API. [Fork the repository]: https://help.github.com/articles/fork-a-repo/ [Create a Pull Request]: https://help.github.com/articles/creating-a-pull-request/ [DevSite]: http://kitware.github.io/itk-vtk-viewer ================================================ FILE: Copyright.txt ================================================ /*========================================================================= Program: ITK/VTK ImageViewer Module: Copyright.txt Copyright (c) 2016 Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither name of Ken Martin, Will Schroeder, or Bill Lorensen nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =========================================================================*/ ================================================ FILE: LICENSE ================================================ Copyright (c) 2016, Kitware Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: README.md ================================================ ## [ITK/VTK Viewer - Web based Image, Mesh, and Point Set Viewer](http://kitware.github.io/itk-vtk-viewer/) ![Build and Test](https://github.com/Kitware/itk-vtk-viewer/workflows/Build%20and%20Test/badge.svg) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) ![npm-download](https://img.shields.io/npm/dm/itk-vtk-viewer.svg) [![](https://data.jsdelivr.com/v1/package/npm/itk-vtk-viewer/badge?style=rounded)](https://www.jsdelivr.com/package/npm/itk-vtk-viewer) ![npm-version-requirement](https://img.shields.io/badge/npm->=8.0.0-brightgreen.svg) ![node-version-requirement](https://img.shields.io/badge/node->=12.0.0-brightgreen.svg) [![launch ImJoy](https://imjoy.io/static/badge/launch-imjoy-badge.svg)](http://imjoy.io/#/app?plugin=https://kitware.github.io/itk-vtk-viewer/app/) [![DOI](https://zenodo.org/badge/92198432.svg)](https://zenodo.org/badge/latestdoi/92198432) # Introduction ITK/VTK Viewer is an open-source software system for medical and scientific image, mesh, and point set visualization. # Reporting Issues If you would like to discuss a bug or possible improvement: 1. If you have a patch, please read the [CONTRIBUTING.md][] document. 2. Open an entry in the [Issue Tracker][]. [contributing.md]: CONTRIBUTING.md [issue tracker]: https://github.com/Kitware/itk-vtk-viewer/issues # Requirements In general ITK/VTK Viewer tries to be as portable as possible; the specific configurations below are tested and known to work. ITK/VTK Viewer supports the following development environments: - Node 16+ - NPM 8+ and the following browsers: - Firefox - Chrome - Safari # Documentation See the [documentation](https://kitware.github.io/itk-vtk-viewer) for a getting started guide, advanced documentation, and API descriptions. # Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) for instructions to contribute. # License ITK/VTK Viewer is distributed under the OSI-approved BSD 3-clause License. See [Copyright.txt][] for details. [copyright.txt]: Copyright.txt # Build Blosc with Debug Checkout `c-blosc` git submodule, then `itk-wasm --build-dir web-build build -- -DCMAKE_BUILD_TYPE=Debug` ================================================ FILE: bin/itk-vtk-viewer-cli.js ================================================ #!/usr/bin/env node let program = require('commander'); let ipList = require('./network'); let server = require('./server'); let pkg = require('../package.json'); let openFn = require('open'); let path = require('path'); let app = null; let version = /semantically-release/.test(pkg.version) ? 'development version' : pkg.version; let dataPath = process.cwd(); function handlePort(value) { if (!isNaN(parseInt(value, 10))) { return parseInt(value, 10); } throw Error('port option requires a number'); } program .version(version) .option('-p, --port [3000]', 'Start web server with given port', handlePort, 3000) .option('-s, --server-only', 'Do not open the web browser\n') .arguments('[inputFile]') .parse(process.argv); const inputFilePath = program.args[0]; let urlOptions = ''; if (inputFilePath) { const fullPath = path.normalize(inputFilePath); dataPath = path.dirname(fullPath); const inputFileName = path.basename(fullPath); urlOptions = `?fileToLoad=/data/${inputFileName.replace(/ /g, '%20')}` } // Start server and listening app = server(dataPath); app.listen(program.port); // Print server information if (ipList.length === 1) { console.log(['\nitk-vtk-viewer\n => Serving ', dataPath, '\n\n http://', ipList[0].ip, ':', program.port, `/${urlOptions}\n`].join('')); } else { function printIP(l) { console.log(' ', l.name, ['=> http://', l.ip, ':', program.port, `/${urlOptions}`].join('')); } console.log(['\nitk-vtk-viewer\n => Serving ', dataPath, ' on port ', program.port, '\n'].join('')); ipList.forEach(printIP); console.log(); } // Open browser if asked if (!program.serverOnly) { openFn(['http://localhost:', program.port, `/${urlOptions}`].join('')); } ================================================ FILE: bin/network.js ================================================ var os = require('os'); var ifaces = os.networkInterfaces(); var networkInterfaces = []; var alias = 0; var currentName = ''; function netInterface(iface) { if (iface.family !== 'IPv4' || iface.internal !== false) { // skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses return; } if (alias >= 1) { // this single interface has multiple ipv4 addresses networkInterfaces.push({ name: [currentName, alias].join(':'), ip: iface.address, }); } else { // this interface has only one ipv4 adress networkInterfaces.push({ name: currentName, ip: iface.address, }); } ++alias; } function device(ifname) { alias = 0; currentName = ifname; ifaces[ifname].forEach(netInterface); } Object.keys(ifaces).forEach(device); module.exports = networkInterfaces; ================================================ FILE: bin/server.js ================================================ var path = require('path'); var express = require('express'); function webServer(dataPath) { var app = express(); var fullDataPath = dataPath; // Handle relative path if (fullDataPath[0] === '.') { fullDataPath = path.normalize(path.join(process.cwd(), dataPath)); } app.use(express.static(path.join(__dirname, '/../dist'))); app.use('/data', express.static(fullDataPath)); return app; } module.exports = webServer; ================================================ FILE: buildUI.js ================================================ #!/usr/bin/env node const path = require('path') const spawnSync = require('child_process').spawnSync const cwd = path.join(__dirname, 'src', 'UI', 'reference-ui') spawnSync('npm', ['ci'], { cwd, env: process.env, stdio: 'inherit' }) spawnSync('npm', ['run', 'build'], { cwd, env: process.env, stdio: 'inherit' }) ================================================ FILE: doc/.gitignore ================================================ build-tmp ================================================ FILE: doc/config.js ================================================ module.exports = { baseUrl: '/itk-vtk-viewer', work: './build-tmp', examples: [], config: { title: 'itk-vtk-viewer', description: '"ITK/VTK Image Viewer for the Web"', subtitle: '"Enable medical imaging to the Web."', author: 'Kitware Inc.', timezone: 'UTC', url: 'https://kitware.github.io/itk-vtk-viewer', root: '/itk-vtk-viewer/', github: 'kitware/itk-vtk-viewer', google_analytics: 'UA-90338862-7', }, copy: [ { src: '../dist/*', dest: './build-tmp/public/app' }, ], }; ================================================ FILE: doc/content/api/index.md ================================================ # API This documentation provides more detailed information about the viewer application programming interface (API). ## Viewer API ### getConfig() Get the viewer configuration. This can be used to restore a viewer's configuration when created. ### setRenderingViewContainerStyle(containerStyle) ### getRenderingViewContainerStyle() Set/get the CSS style for the rendering view `div`'s. ### setBackgroundColor(bgColor) ### getBackgroundColor() Set/get the rendering background color. An array of RGB values from 0.0 to 1.0, e.g. [1.0, 0.5, 0.5]. ### setUnits(units) ### getUnits() Set/get the string identifying the spatial length units in the scale bar. ### setUICollapsed(collapsed) ### getUICollapsed() Set/get whether the user interface is collapsed. ## Viewer Main API ### setRotateEnabled(enabled) ### getRotateEnabled() Set/get whether the 3D scene is continuously rotated. ### setAnnotationsEnabled(enabled) ### getAnnotationsEnabled() Set/get whether annotations such as the current pixel value, scale bar, or orientation widget are displayed. ### setAxesEnabled(enabled) ### getAxesEnabled() Set/get whether spatial axes are visualized in the scene. ### setXSlice(position) ### getXSlice() Set/get the position in world space of the X slicing plane. ### setYSlice(position) ### getYSlice() Set/get the position in world space of the Y slicing plane. ### setZSlice(position) ### getZSlice() Set/get the position in world space of the Z slicing plane. ### setViewMode(mode) ### getViewMode() Set/get the viewer mode for the current primary view. Valid values: 'XPlane', 'YPlane', 'ZPlane', or 'Volume'. ### getLayerNames() Get the names of all data layers. ### setLayerVisibility(visible, name) ### getLayerVisibility(name) Set/get whether the named layer is visible. ### selectLayer(name) Select the layer identified by `name` in the user interface. ### setCroppingPlanes(croppingPlanes) The croppingPlanes parameter is an array of plane defining objects. Example: `[ { normal: [1, 0, 0], origin: [1, 2, 3] }, ...]`. Maximum number of planes is 6. ### getCroppingPlanes(): [ { normal: [number, number, number], origin: [number, number, number] }, ...] Returns array of plane objects. ### resetCroppingPlanes() Sets cropping box to encompass all images and geometries ### setCroppingPlanesEnabled(enabled) Control visibility of croppingPlanes ### getCroppingPlanesEnabled(): Boolean ## Viewer Image API ### setImage(image) Set the image to be visualized. Can be an [itk.js Image](https://insightsoftwareconsortium.github.io/itk-js/api/Image.html) or a [scijs ndarray](http://scijs.net/packages/#scijs/ndarray) for JavaScript; for Python, it can be a [numpy](https://numpy.org) array. ### setPointSets(pointSets) Set a set of points to be visualized. It can be an array of or a single imjoy-rpc encoded ndarray. The ndarray should be an array with the shape [x, 2] or [x, 3]. ### captureImage() Take a screenshot for the current view and return a base64 encoded image string. ### setImageInterpolationEnabled(enabled) ### getImageInterpolationEnabled() Set/get whether bilinear interpolation is used in the image slicing planes. ### setImageComponentVisibility(visibility, component, name) ### getImageComponentVisibility(component, name) Set/get the given image intensity component index's visibility. ### setImageColorRange(range, component, name) ### getImageColorRange(component, name) Set/get the [min, max] range of intensity values mapped to colors for the given image component identified by name. ### setImageColorRangeBounds(bounds, component, name) ### getImageColorRangeBounds(component, name) Set/get the [min, max] range of intensity values for color maps that provide a bounds for user inputs. ### setImageColorMap(colorMap, component, name) ### getImageColorMap(component, name) Set/get the color map for the given component/channel. ### setImagePiecewiseFunctionPoints(points, component, name = current) ### getImagePiecewiseFunctionPoints(component, name) Set/get the points defining the piecewise volume opacity transfer and multi-component slice blending function. Parameter points is of type `[intensity: number, opacity:number][]` with intensity and opacity values going from 0 to 1. The 0 to 1 intensity values are scaled between the component's range of intensity values. Example: If the intensity values for component 0 go from 100 to 200, `viewer.setImagePiecewiseFunctionPoints([[0, 0], [.5, 1]], 0)` puts a point at intensity 100 and another at intensity 150. For volume rendering, intensities above or below the range of points have 0 opacity. For 2D or slice rendering, intensities above or below the range of points take the last color of the color map. With 2 or more components in 2D or slice rendering, the `y` value of points controls the contribution of matching intensities. Intensities before the start point take the `y`/mix-factor of the start point, intensities after the end point take the end point `y` value. ### setImageShadowEnabled(enabled, name) ### getImageShadowEnabled(name) Set/get whether to used gradient-based shadows in the volume rendering. ### setImageGradientOpacity(opacity, name) ### getImageGradientOpacity(name) Set/get the gradient opacity in the volume rendering. Values range from 0.0 to 1.0. ### setImageGradientOpacityScale(scale, name) ### getImageGradientOpacityScale(name) Set/get the gradient scale for gradient-based opacity in the volume rendering. Values range from 0.0 to 1.0. ### setImageVolumeSampleDistance(distance, name) ### getImageVolumeSampleDistance(name) Set/get the depth sampling distance in the volume rendering. Values range from 0.0 to 1.0. ### setImageBlendMode(mode, name) ### getImageBlendMode(name) Set/get the volume rendering blend mode. Supported modes: 'Composite', 'Maximum', 'Minimum', 'Average'. ### setLabelImage(labelImageToLoad, layerImageName) Loads a label image and fuses with selected image or layerImageName. setLabelImage disables 2D image interpolation. ### setLabelImageLookupTable(lookupTable, name) ### getLabelImageLookupTable(name) Set/get the label image lookup table. ### setLabelImageBlend(blend, name) ### getLabelImageBlend(name) Set/get the blend ratio between the label image and the intensity image. ### setLabelImageLabelNames(labeNames, name) ### getLabelImageLabelNames(name) Set/get the string names for the integer label values. A Map of label integer to string values. ### setLabelImageWeights(weights, name) ### getLabelImageWeights(name) Set/get the rendering weights assigned to labels. A Map of label integer to float, 0.0 to 1.0, weight. string values. ### setImageScale(resolutionScale) resolutionScale is a integer with 0 being the most detailed scale. ### setCompareImages(fixedImageName, movingImageName, options) Post processing to detect difference in images. The moving and fixed images are separately compressed to one component with a vector magnitude or luminance filter. Then, the moving image is re-sampled to the fixed image space. The final rendered image has the fixed image on the first component, the moving image is on the second component. The moving image must have been added last. `options` is an Object with: ``` { method: 'checkerboard' | 'cyan-magenta' | 'blend' | 'disabled', imageMix?: number, checkerboard?: boolean, pattern?: number[], swapImageOrder?: boolean } ``` `method` can be `blend`, `green-magenta`, `cyan-red`, `cyan-magenta`, `checkerboard`, or `disabled`. `blend` simply initiates the comparison composition of images. `cyan-magenta` changes the color map for fixed image to cyan, moving image to magenta. If images match perfectly, the color will be purple. `green-magenta` and `cyan-red` changes the color map for the fixed and moving image. If images match, the color will be gray/white. `checkerboard` is equivalent to `blend` with `imageMix` set to `0`. `imageMix` changes the percent contribution the fixed vs moving image makes to the render by modifying the opacity transfer function. Value of 1 means max opacity for moving image, 0 for fixed image. `checkerboard` picks pixels from the fixed and moving image to create a checkerboard pattern. The 2 components in the output image differ by the image order for each checkerboard box. `pattern` is an array with the number of checkerboard boxes for each dimension. If pattern === undefined, it defaults to 4 boxes across each dimension `swapImageOrder` toggles the checkerboard pattern by switching the imageMix parameter between 0 and 1. ================================================ FILE: doc/content/config/index.md ================================================ title: Config --- This documentation provides more detailed information about the viewer configuration. The viewer configuration is a JSON-serializable JavaScript object in the browser and Python dictionary in a Python environment. To obtain a viewer's configuration, call the [viewer API `getConfig()` method](../api/). Pass a viewer's configuration during creation with the `config` option. The followings sections describe the configuration fields. ## Viewer Config ### viewerConfigVersion This is a "major.minor" version of the configuration. The major version changes with incompatible configuration -- a viewer will only use a configuration that has the same major version. The minor version indicates supported configuration entries. ### uiMachineOptions How to render the user interface. Either `'reference'` or `{ href: 'https://url.to/uiMachineOptionsESM.js, export: 'default' }`, or a JavaScript object with the UI machine options. If a JavaScript object, it will not be serializable. ### xyLowerLeft When viewing the Z slice, the X-Y plane, whether the origin is in the lower left or upper left. ### renderingViewContainerStyle CSS style of the container (`div`) for the rendering views. ### uiCollapsed Whether the user interface is collapsed. ## Main Config ### backgroundColor Background color of the renderer. ### units Spatial length units displayed in the scale bar. ================================================ FILE: doc/content/docs/cli.md ================================================ title: Command Line Interface --- ITK/VTK Viewer can be used as a command line tool for opening and visualizing your local data file. ## Installation First, [install Node.js](https://nodejs.org/en/download/), if not already installed. After Node.js is installed, the `node` and `npm` executables should be in your `PATH`. ```sh $ npm install itk-vtk-viewer -g ``` This command will install the application globally, which will provide a new command line executable, `itk-vtk-viewer`: ```sh $ itk-vtk-viewer Usage: itk-vtk-viewer [options] ] [inputFile] Options: -V, --version output the version number -p, --port [3000] Start web server with given port (default: 3000) -s, --server-only Do not open the web browser -h, --help output usage information ``` ### Quick start To visualize an image, pass the path to the file to visualize. By default, a new tab will open in your browser with your visualization. ```sh $ itk-vtk-viewer ./MRHead.nrrd itk-vtk-viewer => Serving . http://10.10.10.10:3000/?fileToLoad=/data/MRHead.nrrd ``` ### Drag and drop viewer Instead of specifying files via the command line, 1. drag and drop, 2. click on the viewer page, or 3. press the *Enter* key after starting the executable without positional arguments: ```sh $ itk-vtk-viewer ``` ![ItkVtkViewer](./viewer.jpg) ================================================ FILE: doc/content/docs/customizeUI.md ================================================ title: User Interface (UI) Customization --- The viewer's user interface (UI) is completely customizable. An existing viewer user interface can be tweaked, or a new user interface can be built from scratch. Use vanilla HTML/CSS/JavaScript or your favorite UI framework, such as React.js or Vue.js. The user interface is specified on viewer creation with a JavaScript object or an object that specifies an [ES Module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). The UI object should contain functions that implement **actions** called as UI [state machines](https://xstate.js.org/docs/) respond to events. Nested objects provide actions for child state machines. A few example actions are `toggleDarkMode` or `toggleUICollapsed`. Each [action function](https://xstate.js.org/docs/about/glossary.html#action) is expected to accept two arguments: `context` and `event`. The `context` is the current state machine context. A `context` object is comprised of the context generated by the [viewer configuration](../config/) and additional objects generated by the UI or renderer. For example, the `createInterface` action may add a `div` property to the `context` for the UI that other UI actions may reference later. The `event` argument is the current event object that triggered the action. An `event` object contains the event name, an upper case string by convention. Optionally, the `event` may contain a payload in the `data` property. In addition to responding to events, a UI implementation will trigger events from user input and pass those events to the state machine by calling `context.service.send()`. For example, ``` collapseUIButton.addEventListener('click', () => { context.service.send('TOGGLE_UI_COLLAPSED')) ``` ## UI options architecture **Warning**: the viewer machine's architecture is experimental and it is subject to change without modification to the major version of the package. We mean it! Available actions can be observed in the [Reference UI Machine Options](https://github.com/Kitware/itk-vtk-viewer/blob/master/src/UI/reference-ui/src/referenceUIMachineOptions.js). If an action is not implemented, it is a no-op. Nested options objects correspond to the nested state machines. They are: * [`main`](https://github.com/Kitware/itk-vtk-viewer/blob/master/src/UI/Reference/Main/mainUIMachineOptions.js): UI components impacting the entire viewer's state. For example, the UI to toggle fullscreen mode or change the viewer's background color. * [`layers`](https://github.com/Kitware/itk-vtk-viewer/blob/master/src/UI/Reference/Layers/layersUIMachineOptions.js): UI components related to dataset layers. For example, the UI to select a layer or toggle its visibility. * [`widgets`](https://github.com/Kitware/itk-vtk-viewer/blob/master/src/UI/Reference/Widgets/widgetsUIMachineOptions.js): UI components for interactive widget parameters. For example, display the current distance measured by a distance widget. * [`images`](https://github.com/Kitware/itk-vtk-viewer/blob/master/src/UI/Reference/Images/imagesUIMachineOptions.js): UI components related to the currently selected image layer. For example, the current color map used. * `geometries` (todo): UI components related to the currently selected geometry layer. For example, the current geometry opacity. * `pointSets` (todo): UI components related to the currently selected point set layer. For example, the current point set opacity. ## Example: Main UI with only a screenshot button This example demonstrates how to customize the `reference` UI so the `main` interface only presents a screenshot button. ### Create repository Let's create a repository for our interface. Either use the *Use this template* button on the [itk-viewer-reference-ui-template GitHub repository](https://github.com/InsightSoftwareConsortium/itk-viewer-reference-ui-template): ![itk-viewer-reference-ui-template button](./referenceUITemplateGitHub.png) or use the template repository on the command line: ``` npx degit InsightSoftwareConsortium/itk-viewer-reference-ui-template my-viewer-ui ``` ### Edit interface To create our interface, first install the Node.js packages: ``` cd my-viewer-ui npm ci ``` Then start the development server: ``` npm run dev ``` And visit *http://localhost:3000* in your web browser. The template has customized the viewer's [Reference UI](https://www.npmjs.com/package/itk-viewer-reference-ui) to only present a screenshot button in the viewer's main interface. The main interface is customized with the `createMainInterface` action. ``` import referenceUIMachineOptions from 'itk-viewer-reference-ui/src/referenceUIMachineOptions.js' import style from 'itk-viewer-reference-ui/src/ItkVtkViewer.module.css' import createScreenshotButton from 'itk-viewer-reference-ui/src/Main/createScreenshotButton.js' function modifiedCreateMainInterface(context) { const mainUIGroup = document.createElement('div') mainUIGroup.setAttribute('class', style.uiGroup) context.uiGroups.set('main', mainUIGroup) const mainUIRow1 = document.createElement('div') mainUIRow1.setAttribute('class', style.mainUIRow) mainUIGroup.appendChild(mainUIRow1) createScreenshotButton(context, mainUIRow1) context.uiContainer.appendChild(mainUIGroup) } ``` ![screenshot button](./screenshotButton.png) We re-use the rest of the user interface machine actions: ``` const uiMachineOptions = { ...referenceUIMachineOptions } const uiMainActions = { ...uiMachineOptions.main.actions } uiMainActions.createMainInterface = modifiedCreateMainInterface const uiMain = { ...uiMachineOptions.main } uiMain.actions = uiMainActions uiMachineOptions.main = uiMain ``` And use the `uiMachineOptions` as the default module export: ``` export default uiMachineOptions ``` Let's make the background of the main user interface green by editing *main.js*: ``` [...] createScreenshotButton(context, mainUIRow1) // Add this line mainUIGroup.style.backgroundColor = "green" context.uiContainer.appendChild(mainUIGroup) [...] ``` After saving *main.js*, the page will reload with our change applied: ![screenshot button green](./screenshotButtonGreen.png) ### Publish the UI module Exit the development server with *Ctrl+C* if still open, then build a production version of the user interface module: ``` npm run build ``` This builds the module `dist/referenceUIMachineOptions.js.es.js`. We can publish our interface to [npmjs.com](https://www.npmjs.com/). First change the package `name` in the *package.json* file to something unique: ``` { "version": "0.1.0", "name": "my-viewer-ui", "scripts": { ``` Then publish the package: ``` npm login npm publish ``` After published and propagated on the network, your package is available for download. The package files are also served by services like [jsdelivr](https://jsdelivr.com) (better for production use) or [unpkg](https://unpkg.com) (better for testing). Use your user interface by specifying it in the [viewer config](../config.html): ``` const uiMachineOptions = { href: "https://cdn.jsdelivr.net/npm/itk-viewer-reference-ui-template@0.1.2/dist/referenceUIMachineOptions.js.es.js" } // or // const uiMachineOptions = { href: "https://unpkg.com/itk-viewer-reference-ui-template@0.1.2/dist/referenceUIMachineOptions.js.es.js" } itkVtkViewer.createViewer(container, { image: ipfsImage, rotate: false, config: { uiMachineOptions }, }) ``` ================================================ FILE: doc/content/docs/embeddedViewer.md ================================================ title: Embedded Viewer --- ITK/VTK Viewer can be used within an existing web site as a library to embed interactive 3D visualizations for remote or local datasets. To do so, create a container element for the viewer as follows. ```html
``` Moreover, the JavaScript library should also be added to the web page. Only one of the following is required ```html ``` or ```html ``` or, fixed to a specific version: ```html ``` ### Viewer configuration The container `
` can be extended with the following set of [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes): - (Mandatory) __data-url__="/data/005_36months_T2_RegT1_Reg2Atlas_ManualBrainMask_Stripped.nrrd" - (Optional) __data-viewport__="300x200" | default is 500x500 - (Optional) __data-background-color__="00aa00" | default is black - (Optional) __data-use2D="false" ![ItkVtkViewer-embedded](./embeddedViewer.png) ```html [...]
[...]
[...] ``` ================================================ FILE: doc/content/docs/imjoy.md ================================================ title: ImJoy Plugin --- [![launch ImJoy](https://imjoy.io/static/badge/launch-imjoy-badge.svg)](http://imjoy.io/#/app?plugin=https://kitware.github.io/itk-vtk-viewer/app/) An *itk-vtk-viewer* plugin is available for [ImJoy](https://imjoy.io), a plugin powered hybrid computing platform for deploying deep learning applications such as advanced image analysis tools. ![ImJoy itk-vtk-viewer plugin](./imjoy.png) ## Installation Install the plugin into the workspace with the following [ImJoy Web App](http://imjoy.io/#/app?plugin=https://kitware.github.io/itk-vtk-viewer/app/) or [ImJoy Lite App](http://imjoy.io/lite?plugin=https://kitware.github.io/itk-vtk-viewer/app/) links with the plugin URI: ``` https://kitware.github.io/itk-vtk-viewer/app/ ``` Note that the link can also be used directly. To install a specific version associated with a specific commit, click the *Details* link associated with the *fleek/build* check found in checkmark link popup in the [GitHub commit interface](https://github.com/Kitware/itk-vtk-viewer/commits/master). ![CID link](./fleek-build-link.png) This results in a URL that contains a root [Content Identifier (CID)](https://proto.school/anatomy-of-a-cid/01). For example, ``` https://bafybeihh34vpeoczdl3bu5wff3cvx35g2u3h3cbs6cmc3werg7drobr3ty.on.fleek.co/ ``` ## Inputs Supported context `data` inputs: **image**: Image to be visualized. Can be: - An [itk-wasm Image](https://wasm.itk.org/api/Image.html) - A [scijs ndarray](http://scijs.net/packages/#scijs/ndarray) for JavaScript; for Python, it can be a [numpy](https://numpy.org) array. - A [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) pointing to an [image file supported by itk-wasm](https://wasm.itk.org/docs/image_formats.html). For [scijs ndarray](http://scijs.net/packages/#scijs/ndarray), you can use the following function to encoded it into an imjoy-rpc encoded ndarray. ``` function encodeScijsArray(array){ return { _rtype: 'ndarray', _rdtype: array.dtype, _rshape: array.shape, _rvalue: array.data.buffer, } } ``` The `image` key is optional; one can also call `setImage()` later. **pointSets**: An array of pointSet or a single pointSet to be visualized. Can be **an array** of imjoy-rpc encoded ndarray (as described in **image**): The `pointSets` key is optional; one can also call `setPointSets()` later. Context `config`: An optional [viewer configuration](../config/) can be passed with the context `config`. To retrieve the configuration from an existing viewer, call `viewer.getConfig()`. Usage in javascript: ```javascript const imageArray = ... // itk-wasm Image or imjoy-rpc encoded ndarray const viewer = await api.createWindow({ src: "https://kitware.github.io/itk-vtk-viewer/app/", data: { image: imageArray }, config: config, }) ``` Usage in Python ```python from imjoy import api import numpy as np # a 2D or 3D numpy array image_array = np.random.randint(0, 255, [500, 500], dtype='uint8') async def setup(): viewer = await api.createWindow(src="https://kitware.github.io/itk-vtk-viewer/app/", data={"image": imageArray}, config=config) api.export({"setup": setup}) ``` Displaying a point cloud in Python: ```python import numpy as np from imjoy import api # make a point set array gaussian_1_mean = [0.0, 0.0, 0.0] gaussian_1_cov = [[1.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 0.5]] number_of_points = 1000000 point_set_array = np.random.multivariate_normal(gaussian_1_mean, gaussian_1_cov, number_of_points) point_set_array = point_set_array.astype('float32') async def setup(): viewer = await api.createWindow( src="https://kitware.github.io/itk-vtk-viewer/app/" ) await viewer.setPointSets([point_set_array]) api.export({"setup": setup}) ``` ## API functions In addition to the standard `setup` and `run` methods, the *itk-vtk-viewer* plugin exposes the full [viewer API](../api/). ================================================ FILE: doc/content/docs/index.md ================================================ title: Overview --- ![Build and Test](https://github.com/Kitware/itk-vtk-viewer/workflows/Build%20and%20Test/badge.svg) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) ![npm-download](https://img.shields.io/npm/dm/itk-vtk-viewer.svg) [![DOI](https://zenodo.org/badge/92198432.svg)](https://zenodo.org/badge/latestdoi/92198432) [![launch ImJoy](https://imjoy.io/static/badge/launch-imjoy-badge.svg)](http://imjoy.io/#/app?plugin=https://kitware.github.io/itk-vtk-viewer/app/) ITK/VTK Viewer is an open-source web application for medical and scientific image, mesh, and point set visualization. ![How it works](./howToUse.jpg) ================================================ FILE: doc/content/docs/shortcuts.md ================================================ title: Keyboard Shortcuts and Controls --- The viewer supports mouse-based controls, touchscreen interaction, and keyboard shortcuts. To use the keyboard shortcuts when the viewer is embedded, the mouse must be over the viewer. **Key** | **Action** --- | --- | **1** | X-plane mode **2** | Y-plane mode **3** | Z-plane mode **4** | Volume rendering mode **q** | Toggle user interface **w** | Toggle region of interest (ROI) selection widget **e** | Reset ROI **r** | Reset camera **p** | Toggle spinning **s** | Toggle slicing planes in volume rendering mode **f** | Toggle fullscreen **Mouse** | **Action** --- | --- | **Left click + drag** | Rotate **Right click + drag** or **shift + left click + drag** | Pan **Mouse wheel** or **control + left click + drag** or **pinch** | Zoom **Alt + left click + drag left-right** | Change color transfer function level **Alt + left click + drag top-bottom** | Change color transfer function window **Alt + right click + drag top-bottom** | Change primary volume opacity transfer function magnitude ================================================ FILE: doc/content/docs/viewer.md ================================================ title: Progressive Web App --- The [default ITK/VTK Viewer page](https://kitware.github.io/itk-vtk-viewer/app/) lets you drag and drop or select a data file from your local filesystem to visualize. Once you have loaded data with this [progressive web app](https://en.wikipedia.org/wiki/Progressive_Web_Apps), the app will also work offline. Additionally, visualization links for data files available on the web can be created by providing extra arguments to the app URL. The resulting link can be quickly shared to distribute interative visualizations for your data. For example,

The extra argument, `?fileToLoad=[...]`, uses a full `http://` url to the data file. ![ItkVtkViewer](./dataViewer.jpg) An additional parameter can be added to force the slice viewing mode. Just add `?use2D` or `?fileToLoad=[..]&use2D` inside the URL. ![ItkVtkViewer2D](./2dViewer.jpg) ### Compare Images URL Parameters To compare 2 images use these parameters: `image`: full `http://` url to a data file `fixedImage`: full `http://` url to a data file `compare`: sets the compare "method". Could be `blend`, `green-magenta`, `cyan-red`, `cyan-magenta` or `checkerboard` `imageMix`: 0 to 1 percent contribution the fixed vs moving image `checkerboard`: `true` or `false` to force checkerboard for other methods like `blend` or `cyan-magenta` `pattern`: Checkerboard pattern as array with length matching the number of dimension in the images. Example: `pattern=[1,3,4]` `swapImageOrder`: `true` or `false` which toggles the checkerboard pattern. Example: `http://localhost:8082/?rotate=false&image=http://localhost:8082/test-data/HeadMRVolume.nrrd&fixedImage=http://localhost:8082/test-data/HeadMRVolume2Components.nrrd&compare=checkerboard&pattern=[1,3,4]&swapImageOrder=true` More information under setCompareImages on the API docs. ================================================ FILE: doc/content/index.html ================================================

Redirecting to the documentation. For the application, go to ./app/.

================================================ FILE: doc/content/index.jade ================================================ layout: index description: ITK/VTK Viewer subtitle: An open-source software system for medical and scientific image, mesh, and point set visualization. cmd: itk-vtk-viewer --help comments: false --- ul#intro-feature-list li.intro-feature-wrap .intro-feature .intro-feature-icon i.fa.fa-cloud-download h3.intro-feature-title a(href="https://www.npmjs.com/package/itk-vtk-viewer").link Releases img(style="padding-left: 25px",src="https://badge.fury.io/js/itk-vtk-viewer.svg") p.intro-feature-desc ITK/VTK Viewer is a web-based viewer for loading 2D/3D images and more... li.intro-feature-wrap .intro-feature .intro-feature-icon i.fa.fa-life-ring h3.intro-feature-title a(href="http://www.vtk.org/services/").link Support and Services p.intro-feature-desc Kitware offers advanced software R&D solutions and services. Find out how we can help with your next project. ul#intro-cmd-wrap(style='margin-top: 25px;border-radius: 10px;') li.intro-cmd-item npm install itk-vtk-viewer -g li.intro-cmd-item itk-vtk-viewer p(style='text-align: center; margin: 20px auto; max-width: 700px;') img(src='./docs/howToUse.jpg', width='100%') ================================================ FILE: doc/data/menu.yml ================================================ docs: /docs/ api: /api/ config: /config/ app: /app/ ================================================ FILE: doc/tpl/__en__ ================================================ menu: docs: Documentation api: API config: Config examples: Examples news: News search: Search app: Application index: get_started: Get started page: contents: Contents back_to_top: Back to Top improve: Improve this doc prev: Prev next: Next last_updated: "Last updated: %s" sidebar: docs: getting_started: Getting Started overview: Overview develop: Development requirement: Requirements troubleshooting: Troubleshooting debugging: Debugging contributing: Contributing testing: Testing tests: Tests coverage: Coverage cli: Command Line App viewer: Progressive Web App embeddedViewer: Inline Viewer imjoy: ImJoy Plugin shortcuts: Keyboard Shortcuts customizeUI: UI Customization ================================================ FILE: doc/tpl/__sidebar__ ================================================ docs: getting_started: overview: index.html viewer: viewer.html cli: cli.html embeddedViewer: embeddedViewer.html imjoy: imjoy.html shortcuts: shortcuts.html customizeUI: customizeUI.html ================================================ FILE: examples/compare-image.html ================================================
================================================ FILE: examples/cyan-magenta-compare-image.html ================================================
================================================ FILE: examples/test-conglomerate.html ================================================
================================================ FILE: examples/test-pointsets.html ================================================
================================================ FILE: examples/test.html ================================================
================================================ FILE: karma.conf.js ================================================ /* eslint-disable global-require */ const path = require('path') const vtkRules = require('vtk.js/Utilities/config/dependency.js').webpack.core .rules const cssRules = require('vtk.js/Utilities/config/dependency.js').webpack.css .rules var webpack = require('webpack') var sourcePath = path.join(__dirname, './src') if (!process.env.NODE_ENV) process.env.NODE_ENV = 'test' process.env.CHROME_BIN = require('puppeteer').executablePath() const fallback = { path: false, url: false, module: false, fs: false, stream: require.resolve('stream-browserify'), crypto: false, } const itkConfigTest = path.resolve(__dirname, 'test', 'itkConfigBrowserTest.js') // should be same as in webpack.config.js const moduleConfigRules = [ { test: /\.js$/, loader: 'babel-loader', dependency: { not: ['url'] } }, { test: /\.worker.js$/, use: [{ loader: 'worker-loader', options: { inline: 'no-fallback' } }], }, { test: /\.(png|jpg)$/, type: 'asset', parser: { dataUrlCondition: { maxSize: 128 * 1024 } }, }, // 128kb { test: /\.svg$/, type: 'asset/source' }, ].concat(vtkRules, cssRules) const entry = path.join(__dirname, './src/index.js') module.exports = function init(config) { config.set({ plugins: [ require('karma-webpack'), require('karma-tap'), require('karma-chrome-launcher'), require('karma-firefox-launcher'), require('karma-tap-pretty-reporter'), require('karma-junit-reporter'), ], basePath: '', frameworks: ['tap', 'webpack'], files: [ './test/tests.js', { pattern: './dist/itk/image-io/**', watched: true, served: true, included: false, }, { pattern: './dist/itk/mesh-io/**', watched: true, served: true, included: false, }, { pattern: './dist/itk/web-workers/**', watched: true, served: true, included: false, }, { pattern: './dist/itk/pipeline/**', watched: true, served: true, included: false, }, { pattern: './test/data/**', watched: false, served: true, included: false, }, { pattern: './test/data/**/.*', watched: false, served: true, included: false, }, { pattern: './dist/index.html', watched: true, served: true, included: false, }, { pattern: './dist/itkVtkViewer.js', watched: true, served: true, included: false, }, { pattern: './src/UI/reference-ui/dist/referenceUIMachineOptions.js', watched: true, served: true, included: false, }, { pattern: './src/UI/reference-ui/**/**', watched: true, served: true, included: false, }, { pattern: './test/testUINoPlaneSlidersBundle.js', watched: true, served: true, included: false, }, ], preprocessors: { './test/tests.js': ['webpack'], }, webpack: { mode: 'development', devtool: 'eval-source-map', module: { rules: moduleConfigRules.concat([ { test: entry, loader: 'expose-loader', options: { exposes: 'itkVtkViewer' }, }, ]), }, resolve: { modules: [path.resolve(__dirname, 'node_modules'), sourcePath], alias: { '../itkConfig.js': itkConfigTest, '../../itkConfig.js': itkConfigTest, stream: 'stream-browserify', buffer: 'buffer', }, fallback, }, plugins: [ new webpack.DefinePlugin({ __BASE_PATH__: "'/base'", }), new webpack.ProvidePlugin({ process: ['process/browser'] }), ], }, webpackMiddleware: { noInfo: true, }, reporters: ['tap-pretty', 'junit'], tapReporter: { outputFile: 'test/output.html', separator: '\n=========================================================\n=========================================================\n', }, junitReporter: { outputDir: 'test', }, client: { useIframe: true, args: config.dockered ? ['--dockered'] : [], }, browserDisconnectTimeout: 60000, browserNoActivityTimeout: 60000, port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: ['Chrome_without_sandbox'], singleRun: true, customLaunchers: { Chrome_without_sandbox: { base: 'Chrome', flags: ['--no-sandbox'], }, }, }) } ================================================ FILE: package.json ================================================ { "name": "itk-vtk-viewer", "version": "0.0.0-semantically-release", "description": "Web-based image, mesh, and point set viewer", "keywords": [ "3d", "visualization", "webgl", "medical", "scientific", "itk", "vtk", "image", "geometry", "point cloud", "mesh", "gl-vis", "volume", "graphics" ], "repository": { "type": "git", "url": "https://github.com/kitware/itk-vtk-viewer.git" }, "license": "BSD-3-Clause", "bugs": { "url": "https://github.com/kitware/itk-vtk-viewer/issues" }, "homepage": "https://kitware.github.io/itk-vtk-viewer/", "main": "./dist/itkVtkViewer.js", "types": "./src/index.d.ts", "dependencies": { "@kitware/vtk.js": "^29.4.6", "@material/web": "^1.0.1", "@thewtex/iconselect.js": "^2.1.2", "@xstate/inspect": "^0.4.1", "axios": "^1.6.0", "commander": "^2.20.3", "core-js": "^3.36.0", "css-element-queries": "^1.2.3", "curry": "^1.2.0", "eventemitter3": "^4.0.7", "express": "^4.17.1", "gl-matrix": "^3.4.3", "itk-image-io": "^1.0.0-b.84", "itk-mesh-io": "^1.0.0-b.84", "itk-viewer-color-maps": "^1.2.0", "itk-viewer-transfer-function-editor": "^1.6.0", "itk-wasm": "^1.0.0-b.83", "mobx": "^5.15.7", "mousetrap": "^1.6.5", "open": "^6.4.0", "p-queue": "^7.3.0", "promise-file-reader": "^1.0.3", "promise.any": "^2.0.2", "regenerator-runtime": "^0.13.7", "vtk.js": "^29.4.6", "webworker-promise": "^0.4.2", "xstate": "^4.37.0" }, "devDependencies": { "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-transform-runtime": "^7.13.6", "@babel/preset-env": "^7.13.5", "@babel/runtime": "^7.13.6", "@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-commonjs": "^21.0.2", "@rollup/plugin-node-resolve": "^13.0.0", "@rollup/plugin-typescript": "^9.0.2", "@web3-storage/w3": "^2.6.0", "autoprefixer": "^10.2.6", "babel-loader": "^8.2.2", "babel-plugin-istanbul": "^6.0.0", "babel-preset-mobx": "^2.0.0", "buffer": "^6.0.3", "copy-webpack-plugin": "^9.0.1", "css-loader": "^5.0.2", "es-abstract": "1.18.0-next.1", "eslint": "^8.13.0", "eslint-config-prettier": "^8.5.0", "expose-loader": "^1.0.3", "husky": "^4.3.7", "imjoy-core": "^0.14.5", "is-buffer": "^2.0.5", "karma": "^6.3.16", "karma-chrome-launcher": "^3.1.0", "karma-firefox-launcher": "^2.1.0", "karma-junit-reporter": "^2.0.1", "karma-tap": "^4.2.0", "karma-tap-pretty-reporter": "^4.2.0", "karma-webpack": "5.0.0", "kw-doc": "^3.0.6", "lint-staged": "^10.5.4", "ndarray": "^1.0.19", "npm-run-all": "^4.1.5", "pixelmatch": "^5.2.1", "postcss": "^8.3.5", "postcss-loader": "^4.1.0", "prettier": "^1.19.1", "process": "^0.11.10", "puppeteer": "^5.0.0", "raw-loader": "^4.0.2", "readable-stream": "^3.6.0", "request": "^2.88.2", "resemblejs": "^4.1.0", "rollup": "^2.52.7", "rollup-plugin-ignore": "^1.0.10", "rollup-plugin-postcss": "^4.0.0", "rollup-plugin-svgo": "^2.0.0", "semantic-release": "^19.0.3", "sockjs-client": "^1.5.0", "stream-browserify": "^3.0.0", "style-loader": "^2.0.0", "tap-spec": "^5.0.0", "tape": "^5.2.0", "tape-catch": "^1.0.6", "typescript": "^5.3.3", "webpack": "^5.65.0", "webpack-cli": "^4.8.0", "webpack-dev-server": "^4.7.4", "webpackbar": "^5.0.0-3", "workbox-build": "^6.5.1", "workbox-webpack-plugin": "^6.5.1", "worker-loader": "^3.0.8" }, "scripts": { "doc": "kw-doc -c ./doc/config.js", "doc:www": "kw-doc -c ./doc/config.js -s", "doc:publish": "kw-doc -c ./doc/config.js -mp", "build": "npm-run-all build:release build:ui", "build:debug": "webpack --progress --color --mode development", "build:release": "webpack --progress --color --mode production", "build:ui": "node ./buildUI.js", "build:test-ui": "rollup -c ./test/test-ui-rollup.config.js", "prepack": "npm run build", "bundle": "StandaloneHTML ./dist/index.html ./dist/ItkVtkViewer.html", "commit": "git cz", "format": "prettier --write src/UserInterface/**/*.js src/*.js", "lint:types": "tsc --noEmit", "start": "webpack serve --mode development --static ./dist/ --open --port 8082", "dev": "webpack serve --mode development --static ./dist/ --port 8082", "semantic-release": "semantic-release", "test": "npm run test:downloadData && npm run lint:types && npm run build:test-ui && karma start ./karma.conf.js --browsers Chrome_without_sandbox,Firefox", "test:downloadData": "node test/downloadData.mjs", "test:headless": "./test/run.sh", "test:headless-debug": "./test/run.sh -d", "test:debug": "npm run build:test-ui && karma start ./karma.conf.js --no-single-run" }, "config": { "commitizen": { "path": "cz-conventional-changelog" } }, "bin": { "itk-vtk-viewer": "./bin/itk-vtk-viewer-cli.js" }, "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "*.js": "prettier --write" } } ================================================ FILE: postcss.config.js ================================================ const autoprefix = require('autoprefixer'); module.exports = { plugins: [autoprefix], }; ================================================ FILE: src/Compression/blosc-zarr/BloscZarr.cxx ================================================ #include #include #include "itkPipeline.h" #include "itkInputBinaryStream.h" #include "itkOutputBinaryStream.h" #include int main(int argc, char * argv[]){ itk::wasm::Pipeline pipeline ("Compress or decompress binaries with Blosc", argc, argv); itk::wasm::InputBinaryStream input_binary_stream; pipeline.add_option("input-binary-stream", input_binary_stream, "The input binary stream")->required(); itk::wasm::OutputBinaryStream output_binary_stream; pipeline.add_option("output-binary-stream", output_binary_stream, "The output binary stream")->required(); std::string compressor; pipeline.add_option("compressor", compressor, "Blosc compressor")->required(); size_t input_size; pipeline.add_option("input-size", input_size, "Input binary size in bytes")->required(); bool decompress = false; const auto decompress_option = pipeline.add_flag("-d,--decompress", decompress, "Decompress instead of compress"); size_t output_size = 0; pipeline.add_option("--output-size", output_size, "Output binary size in bytes")->needs(decompress_option); int compression_level = 3; pipeline.add_option("-c,--compression-level", compression_level, "Compression level in compression, 0 to 9")->excludes(decompress_option); size_t typesize = 1; pipeline.add_option("--typesize", typesize, "Assumed type size in compression")->excludes(decompress_option); bool no_shuffle = false; pipeline.add_flag("--no-shuffle", no_shuffle, "Do not add bitshuffle support in compression")->excludes(decompress_option); bool verbose = false; pipeline.add_flag("-v,--verbose", verbose, "Output status information"); ITK_WASM_PARSE(pipeline); /* Register the filter with the library */ if (verbose) { printf("Blosc version info: %s (%s)\n", BLOSC_VERSION_STRING, BLOSC_VERSION_DATE); } /* Initialize the Blosc compressor */ blosc_init(); const int nthreads = 1; const int pnthreads = blosc_set_nthreads(nthreads); if (verbose) { printf("Using %d threads (previously using %d)\n", nthreads, pnthreads); } int rcode = blosc_set_compressor(compressor.c_str()); if (rcode < 0) { printf("Error setting %s compressor. Does it really exist?\n", compressor.c_str()); blosc_destroy(); return rcode; } if (verbose) { printf("Using %s compressor\n", compressor.c_str()); } void * input_array = malloc(input_size); if(input_array == NULL) { printf("Input memory allocation failed\n"); blosc_destroy(); return 1; } input_binary_stream.Get().read(static_cast(input_array), input_size); const auto read_size = input_binary_stream.Get().gcount(); if(read_size != input_size) { printf("Could only read %zu bytes from input file.\n", read_size); blosc_destroy(); free(input_array); return 1; } if (!decompress) { output_size = input_size; } else if(output_size == 0) { blosc_destroy(); free(input_array); CLI::Error err("Runtime error", "--output-size must be specified for decompression", 1); pipeline.exit(err); return 1; } void * output_array = malloc(output_size + BLOSC_MAX_OVERHEAD); if(output_array == NULL) { printf("Output memory allocation failed\n"); blosc_destroy(); free(input_array); return 1; } if (!decompress) { if (verbose) { printf("Compression level %d\n", compression_level); } /* Compress */ const size_t compressed_size = blosc_compress(compression_level, !no_shuffle, typesize, input_size, input_array, output_array, output_size + BLOSC_MAX_OVERHEAD); free(input_array); /* After using it, destroy the Blosc environment */ blosc_destroy(); if (compressed_size < 0) { printf("Compression error. Error code: %lu\n", compressed_size); free(output_array); return compressed_size; } if (verbose) { printf("Compression: %zu -> %lu (%.1fx)\n", input_size, compressed_size, (1.*input_size) / compressed_size); } output_binary_stream.Get().write(static_cast(output_array), compressed_size); free(output_array); } else { /* Decompress */ const int decompressed_size = blosc_decompress(input_array, output_array, output_size); free(input_array); /* After using it, destroy the Blosc environment */ blosc_destroy(); if (decompressed_size < 0) { printf("Decompression error. Error code: %d\n", decompressed_size); free(output_array); return decompressed_size; } output_binary_stream.Get().write(static_cast(output_array), output_size); free(output_array); } return 0; } ================================================ FILE: src/Compression/blosc-zarr/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16.0) project(blosc-zarr) set(BUILD_STATIC ON CACHE BOOL "Build a static version of the blosc library.") set(BUILD_SHARED OFF CACHE BOOL "Build a shared library version of the blosc library.") set(BUILD_TESTS OFF CACHE BOOL "Build test programs form the blosc compression library") set(BUILD_BENCHMARKS OFF CACHE BOOL "Build benchmark programs form the blosc compression library") if(EMSCRIPTEN OR WASI) set(HAVE_THREADS OFF CACHE BOOL "Whether we use threading") endif() if(EMSCRIPTEN) set(CMAKE_C_FLAGS "-s STRICT=1") set(CMAKE_EXE_LINKER_FLAGS "-s STRICT=1") endif() add_subdirectory(c-blosc) find_package(ITK REQUIRED COMPONENTS WebAssemblyInterface ) include(${ITK_USE_FILE}) add_executable(BloscZarr BloscZarr.cxx) target_link_libraries(BloscZarr PUBLIC blosc_static ${ITK_LIBRARIES}) ================================================ FILE: src/Compression/blosc-zarr/web-build/BloscZarr.js ================================================ var BloscZarr = (() => { var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename; return ( function(BloscZarr) { BloscZarr = BloscZarr || {}; null;var Module=typeof BloscZarr!="undefined"?BloscZarr:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var mStdout=null;var mStderr=null;Module["resetModuleStdout"]=function(){mStdout=""};Module["resetModuleStderr"]=function(){mStderr=""};Module["print"]=function(text){console.log(text);mStdout+=text+"\n"};Module["printErr"]=function(text){console.error(text);mStderr+=text+"\n"};Module["getModuleStdout"]=function(){return mStdout};Module["getModuleStderr"]=function(){return mStderr};var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;function logExceptionOnExit(e){if(e instanceof ExitStatus)return;let toLog=e;err("exiting due to exception: "+toLog)}var fs;var nodePath;var requireNodeFS;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}requireNodeFS=(()=>{if(!nodePath){fs=require("fs");nodePath=require("path")}});read_=function shell_read(filename,binary){requireNodeFS();filename=nodePath["normalize"](filename);return fs.readFileSync(filename,binary?undefined:"utf8")};readBinary=(filename=>{var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret});readAsync=((filename,onload,onerror)=>{requireNodeFS();filename=nodePath["normalize"](filename);fs.readFile(filename,function(err,data){if(err)onerror(err);else onload(data.buffer)})});if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",function(reason){throw reason});quit_=((status,toThrow)=>{if(keepRuntimeAlive()){process["exitCode"]=status;throw toThrow}logExceptionOnExit(toThrow);process["exit"](status)});Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=(url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText});if(ENVIRONMENT_IS_WORKER){readBinary=(url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)})}readAsync=((url,onload,onerror)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=(()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()});xhr.onerror=onerror;xhr.send(null)})}setWindowTitle=(title=>document.title=title)}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort(text)}}function getCFunc(ident){var func=Module["_"+ident];return func}function ccall(ident,returnType,argTypes,args,opts){var toC={"string":function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){var len=(str.length<<2)+1;ret=stackAlloc(len);stringToUTF8(str,ret,len)}return ret},"array":function(arr){var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string")return UTF8ToString(ret);if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}function AsciiToString(ptr){var str="";while(1){var ch=HEAPU8[ptr++>>0];if(!ch)return str;str+=String.fromCharCode(ch)}}function allocateUTF8OnStack(str){var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8Array(str,HEAP8,ret,size);return ret}function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;var runtimeKeepaliveCounter=0;function keepRuntimeAlive(){return noExitRuntime||runtimeKeepaliveCounter>0}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();FS.ignorePermissions=false;TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){{if(Module["onAbort"]){Module["onAbort"](what)}}what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return filename.startsWith(dataURIPrefix)}function isFileURI(filename){return filename.startsWith("file://")}var wasmBinaryFile;wasmBinaryFile="BloscZarr.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch=="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["u"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["y"];addOnInit(Module["asm"]["v"]);removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(function(instance){return instance}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch=="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiationResult,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiationResult)})})}else{return instantiateArrayBuffer(receiveInstantiationResult)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}var tempDouble;var tempI64;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func=="number"){if(callback.arg===undefined){getWasmTableEntry(func)()}else{getWasmTableEntry(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var wasmTableMirror=[];function getWasmTableEntry(funcPtr){var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func}function handleException(e){if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)}function ___cxa_allocate_exception(size){return _malloc(size+16)+16}function ExceptionInfo(excPtr){this.excPtr=excPtr;this.ptr=excPtr-16;this.set_type=function(type){HEAP32[this.ptr+4>>2]=type};this.get_type=function(){return HEAP32[this.ptr+4>>2]};this.set_destructor=function(destructor){HEAP32[this.ptr+8>>2]=destructor};this.get_destructor=function(){return HEAP32[this.ptr+8>>2]};this.set_refcount=function(refcount){HEAP32[this.ptr>>2]=refcount};this.set_caught=function(caught){caught=caught?1:0;HEAP8[this.ptr+12>>0]=caught};this.get_caught=function(){return HEAP8[this.ptr+12>>0]!=0};this.set_rethrown=function(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13>>0]=rethrown};this.get_rethrown=function(){return HEAP8[this.ptr+13>>0]!=0};this.init=function(type,destructor){this.set_type(type);this.set_destructor(destructor);this.set_refcount(0);this.set_caught(false);this.set_rethrown(false)};this.add_ref=function(){var value=HEAP32[this.ptr>>2];HEAP32[this.ptr>>2]=value+1};this.release_ref=function(){var prev=HEAP32[this.ptr>>2];HEAP32[this.ptr>>2]=prev-1;return prev===1}}var exceptionLast=0;var uncaughtExceptionCount=0;function ___cxa_throw(ptr,type,destructor){var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw ptr}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};function getRandomDevice(){if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){var randomBuffer=new Uint8Array(1);return function(){crypto.getRandomValues(randomBuffer);return randomBuffer[0]}}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");return function(){return crypto_module["randomBytes"](1)[0]}}catch(e){}}return function(){abort("randomDevice")}}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:function(from,to){from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function mmapAlloc(size){abort()}var MEMFS={ops_table:null,mount:function(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode:function(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}if(!MEMFS.ops_table){MEMFS.ops_table={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}}}var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp}return node},getFileDataAsTypedArray:function(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage:function(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr:function(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr:function(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup:function(parent,name){throw FS.genericErrors[44]},mknod:function(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename:function(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp;old_node.parent=new_dir},unlink:function(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir:function(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir:function(node){var entries=[".",".."];for(var key in node.contents){if(!node.contents.hasOwnProperty(key)){continue}entries.push(key)}return entries},symlink:function(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink:function(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read:function(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{NODEFS.isWindows=!!process.platform.match(/^win/);var flags=process["binding"]("constants");if(flags["fs"]){flags=flags["fs"]}NODEFS.flagsForNodeMap={1024:flags["O_APPEND"],64:flags["O_CREAT"],128:flags["O_EXCL"],256:flags["O_NOCTTY"],0:flags["O_RDONLY"],2:flags["O_RDWR"],4096:flags["O_SYNC"],512:flags["O_TRUNC"],1:flags["O_WRONLY"],131072:flags["O_NOFOLLOW"]}},convertNodeCode:e=>{var code=e.code;return ERRNO_CODES[code]},mount:mount=>{return NODEFS.createNode(null,"/",NODEFS.getMode(mount.opts.root),0)},createNode:(parent,name,mode,dev)=>{if(!FS.isDir(mode)&&!FS.isFile(mode)&&!FS.isLink(mode)){throw new FS.ErrnoError(28)}var node=FS.createNode(parent,name,mode);node.node_ops=NODEFS.node_ops;node.stream_ops=NODEFS.stream_ops;return node},getMode:path=>{var stat;try{stat=fs.lstatSync(path);if(NODEFS.isWindows){stat.mode=stat.mode|(stat.mode&292)>>2}}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}return stat.mode},realPath:node=>{var parts=[];while(node.parent!==node){parts.push(node.name);node=node.parent}parts.push(node.mount.opts.root);parts.reverse();return PATH.join.apply(null,parts)},flagsForNode:flags=>{flags&=~2097152;flags&=~2048;flags&=~32768;flags&=~524288;flags&=~65536;var newFlags=0;for(var k in NODEFS.flagsForNodeMap){if(flags&k){newFlags|=NODEFS.flagsForNodeMap[k];flags^=k}}if(!flags){return newFlags}else{throw new FS.ErrnoError(28)}},node_ops:{getattr:node=>{var path=NODEFS.realPath(node);var stat;try{stat=fs.lstatSync(path)}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}if(NODEFS.isWindows&&!stat.blksize){stat.blksize=4096}if(NODEFS.isWindows&&!stat.blocks){stat.blocks=(stat.size+stat.blksize-1)/stat.blksize|0}return{dev:stat.dev,ino:stat.ino,mode:stat.mode,nlink:stat.nlink,uid:stat.uid,gid:stat.gid,rdev:stat.rdev,size:stat.size,atime:stat.atime,mtime:stat.mtime,ctime:stat.ctime,blksize:stat.blksize,blocks:stat.blocks}},setattr:(node,attr)=>{var path=NODEFS.realPath(node);try{if(attr.mode!==undefined){fs.chmodSync(path,attr.mode);node.mode=attr.mode}if(attr.timestamp!==undefined){var date=new Date(attr.timestamp);fs.utimesSync(path,date,date)}if(attr.size!==undefined){fs.truncateSync(path,attr.size)}}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},lookup:(parent,name)=>{var path=PATH.join2(NODEFS.realPath(parent),name);var mode=NODEFS.getMode(path);return NODEFS.createNode(parent,name,mode)},mknod:(parent,name,mode,dev)=>{var node=NODEFS.createNode(parent,name,mode,dev);var path=NODEFS.realPath(node);try{if(FS.isDir(node.mode)){fs.mkdirSync(path,node.mode)}else{fs.writeFileSync(path,"",{mode:node.mode})}}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}return node},rename:(oldNode,newDir,newName)=>{var oldPath=NODEFS.realPath(oldNode);var newPath=PATH.join2(NODEFS.realPath(newDir),newName);try{fs.renameSync(oldPath,newPath)}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}oldNode.name=newName},unlink:(parent,name)=>{var path=PATH.join2(NODEFS.realPath(parent),name);try{fs.unlinkSync(path)}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},rmdir:(parent,name)=>{var path=PATH.join2(NODEFS.realPath(parent),name);try{fs.rmdirSync(path)}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},readdir:node=>{var path=NODEFS.realPath(node);try{return fs.readdirSync(path)}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},symlink:(parent,newName,oldPath)=>{var newPath=PATH.join2(NODEFS.realPath(parent),newName);try{fs.symlinkSync(oldPath,newPath)}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},readlink:node=>{var path=NODEFS.realPath(node);try{path=fs.readlinkSync(path);path=nodePath.relative(nodePath.resolve(node.mount.opts.root),path);return path}catch(e){if(!e.code)throw e;if(e.code==="UNKNOWN")throw new FS.ErrnoError(28);throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}}},stream_ops:{open:stream=>{var path=NODEFS.realPath(stream.node);try{if(FS.isFile(stream.node.mode)){stream.nfd=fs.openSync(path,NODEFS.flagsForNode(stream.flags))}}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},close:stream=>{try{if(FS.isFile(stream.node.mode)&&stream.nfd){fs.closeSync(stream.nfd)}}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},read:(stream,buffer,offset,length,position)=>{if(length===0)return 0;try{return fs.readSync(stream.nfd,Buffer.from(buffer.buffer),offset,length,position)}catch(e){throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},write:(stream,buffer,offset,length,position)=>{try{return fs.writeSync(stream.nfd,Buffer.from(buffer.buffer),offset,length,position)}catch(e){throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},llseek:(stream,offset,whence)=>{var position=offset;if(whence===1){position+=stream.position}else if(whence===2){if(FS.isFile(stream.node.mode)){try{var stat=fs.fstatSync(stream.nfd);position+=stat.size}catch(e){throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}}}if(position<0){throw new FS.ErrnoError(28)}return position},mmap:(stream,address,length,position,prot,flags)=>{if(address!==0){throw new FS.ErrnoError(28)}if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}var ptr=mmapAlloc(length);NODEFS.stream_ops.read(stream,HEAP8,ptr,length,position);return{ptr:ptr,allocated:true}},msync:(stream,buffer,offset,length,mmapFlags)=>{if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(mmapFlags&2){return 0}var bytesWritten=NODEFS.stream_ops.write(stream,buffer,0,length,offset,false);return 0}}};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,ErrnoError:null,genericErrors:{},filesystems:null,syncFSRequests:0,lookupPath:(path,opts={})=>{path=PATH_FS.resolve(FS.cwd(),path);if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};for(var key in defaults){if(opts[key]===undefined){opts[key]=defaults[key]}}if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(p=>!!p),false);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:node=>{var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:(parentid,name)=>{var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:node=>{var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:node=>{var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:(parent,name)=>{var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:(parent,name,mode,rdev)=>{var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:node=>{FS.hashRemoveNode(node)},isRoot:node=>{return node===node.parent},isMountpoint:node=>{return!!node.mounted},isFile:mode=>{return(mode&61440)===32768},isDir:mode=>{return(mode&61440)===16384},isLink:mode=>{return(mode&61440)===40960},isChrdev:mode=>{return(mode&61440)===8192},isBlkdev:mode=>{return(mode&61440)===24576},isFIFO:mode=>{return(mode&61440)===4096},isSocket:mode=>{return(mode&49152)===49152},flagModes:{"r":0,"r+":2,"w":577,"w+":578,"a":1089,"a+":1090},modeStringToFlags:str=>{var flags=FS.flagModes[str];if(typeof flags=="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:flag=>{var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:(node,perms)=>{if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup:dir=>{var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:(dir,name)=>{try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:(dir,name,isdir)=>{var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:(node,flags)=>{if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:(fd_start=0,fd_end=FS.MAX_OPEN_FDS)=>{for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:fd=>FS.streams[fd],createStream:(stream,fd_start,fd_end)=>{if(!FS.FSStream){FS.FSStream=function(){};FS.FSStream.prototype={object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}}}}stream=Object.assign(new FS.FSStream,stream);var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:fd=>{FS.streams[fd]=null},chrdev_stream_ops:{open:stream=>{var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:()=>{throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice:(dev,ops)=>{FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts:mount=>{var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:(populate,callback)=>{if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:(type,opts,mountpoint)=>{var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:mountpoint=>{var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:(parent,name)=>{return parent.node_ops.lookup(parent,name)},mknod:(path,mode,dev)=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:(path,mode)=>{mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:(path,mode)=>{mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:(path,mode)=>{var dirs=path.split("/");var d="";for(var i=0;i{if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink:(oldpath,newpath)=>{if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename:(old_path,new_path)=>{var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name)}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir:path=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir:path=>{var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node.node_ops.readdir){throw new FS.ErrnoError(54)}return node.node_ops.readdir(node)},unlink:path=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink:path=>{var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return PATH_FS.resolve(FS.getPath(link.parent),link.node_ops.readlink(link))},stat:(path,dontFollow)=>{var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;if(!node){throw new FS.ErrnoError(44)}if(!node.node_ops.getattr){throw new FS.ErrnoError(63)}return node.node_ops.getattr(node)},lstat:path=>{return FS.stat(path,true)},chmod:(path,mode,dontFollow)=>{var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{mode:mode&4095|node.mode&~4095,timestamp:Date.now()})},lchmod:(path,mode)=>{FS.chmod(path,mode,true)},fchmod:(fd,mode)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chmod(stream.node,mode)},chown:(path,uid,gid,dontFollow)=>{var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{timestamp:Date.now()})},lchown:(path,uid,gid)=>{FS.chown(path,uid,gid,true)},fchown:(fd,uid,gid)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chown(stream.node,uid,gid)},truncate:(path,len)=>{if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}node.node_ops.setattr(node,{size:len,timestamp:Date.now()})},ftruncate:(fd,len)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.truncate(stream.node,len)},utime:(path,atime,mtime)=>{var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;node.node_ops.setattr(node,{timestamp:Math.max(atime,mtime)})},open:(path,flags,mode,fd_start,fd_end)=>{if(path===""){throw new FS.ErrnoError(44)}flags=typeof flags=="string"?FS.modeStringToFlags(flags):flags;mode=typeof mode=="undefined"?438:mode;if(flags&64){mode=mode&4095|32768}else{mode=0}var node;if(typeof path=="object"){node=path}else{path=PATH.normalize(path);try{var lookup=FS.lookupPath(path,{follow:!(flags&131072)});node=lookup.node}catch(e){}}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else{node=FS.mknod(path,mode,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node:node,path:FS.getPath(node),flags:flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false},fd_start,fd_end);if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(Module["logReadFiles"]&&!(flags&1)){if(!FS.readFiles)FS.readFiles={};if(!(path in FS.readFiles)){FS.readFiles[path]=1}}return stream},close:stream=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed:stream=>{return stream.fd===null},llseek:(stream,offset,whence)=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read:(stream,buffer,offset,length,position)=>{if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write:(stream,buffer,offset,length,position,canOwn)=>{if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},allocate:(stream,offset,length)=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(offset<0||length<=0){throw new FS.ErrnoError(28)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(!FS.isFile(stream.node.mode)&&!FS.isDir(stream.node.mode)){throw new FS.ErrnoError(43)}if(!stream.stream_ops.allocate){throw new FS.ErrnoError(138)}stream.stream_ops.allocate(stream,offset,length)},mmap:(stream,address,length,position,prot,flags)=>{if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}return stream.stream_ops.mmap(stream,address,length,position,prot,flags)},msync:(stream,buffer,offset,length,mmapFlags)=>{if(!stream||!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},munmap:stream=>0,ioctl:(stream,cmd,arg)=>{if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile:(path,opts={})=>{opts.flags=opts.flags||0;opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){throw new Error('Invalid encoding type "'+opts.encoding+'"')}var ret;var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){ret=UTF8ArrayToString(buf,0)}else if(opts.encoding==="binary"){ret=buf}FS.close(stream);return ret},writeFile:(path,data,opts={})=>{opts.flags=opts.flags||577;var stream=FS.open(path,opts.flags,opts.mode);if(typeof data=="string"){var buf=new Uint8Array(lengthBytesUTF8(data)+1);var actualNumBytes=stringToUTF8Array(data,buf,0,buf.length);FS.write(stream,buf,0,actualNumBytes,undefined,opts.canOwn)}else if(ArrayBuffer.isView(data)){FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn)}else{throw new Error("Unsupported data type")}FS.close(stream)},cwd:()=>FS.currentPath,chdir:path=>{var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories:()=>{FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices:()=>{FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var random_device=getRandomDevice();FS.createDevice("/dev","random",random_device);FS.createDevice("/dev","urandom",random_device);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories:()=>{FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount:()=>{var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup:(parent,name)=>{var fd=+name;var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams:()=>{if(Module["stdin"]){FS.createDevice("/dev","stdin",Module["stdin"])}else{FS.symlink("/dev/tty","/dev/stdin")}if(Module["stdout"]){FS.createDevice("/dev","stdout",null,Module["stdout"])}else{FS.symlink("/dev/tty","/dev/stdout")}if(Module["stderr"]){FS.createDevice("/dev","stderr",null,Module["stderr"])}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},ensureErrnoError:()=>{if(FS.ErrnoError)return;FS.ErrnoError=function ErrnoError(errno,node){this.node=node;this.setErrno=function(errno){this.errno=errno};this.setErrno(errno);this.message="FS error"};FS.ErrnoError.prototype=new Error;FS.ErrnoError.prototype.constructor=FS.ErrnoError;[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack=""})},staticInit:()=>{FS.ensureErrnoError();FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={"MEMFS":MEMFS,"NODEFS":NODEFS}},init:(input,output,error)=>{FS.init.initialized=true;FS.ensureErrnoError();Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit:()=>{FS.init.initialized=false;for(var i=0;i{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode},findObject:(path,dontResolveLastLink)=>{var ret=FS.analyzePath(path,dontResolveLastLink);if(ret.exists){return ret.object}else{return null}},analyzePath:(path,dontResolveLastLink)=>{try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath:(parent,path,canRead,canWrite)=>{parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){}parent=current}return current},createFile:(parent,name,properties,canRead,canWrite)=>{var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS.getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile:(parent,name,data,canRead,canWrite,canOwn)=>{var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS.getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){if(typeof data=="string"){var arr=new Array(data.length);for(var i=0,len=data.length;i{var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS.getMode(!!input,!!output);if(!FS.createDevice.major)FS.createDevice.major=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open:stream=>{stream.seekable=false},close:stream=>{if(output&&output.buffer&&output.buffer.length){output(10)}},read:(stream,buffer,offset,length,pos)=>{var bytesRead=0;for(var i=0;i{for(var i=0;i{if(obj.isDevice||obj.isFolder||obj.link||obj.contents)return true;if(typeof XMLHttpRequest!="undefined"){throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.")}else if(read_){try{obj.contents=intArrayFromString(read_(obj.url),true);obj.usedBytes=obj.contents.length}catch(e){throw new FS.ErrnoError(29)}}else{throw new Error("Cannot load without read() or XMLHttpRequest.")}},createLazyFile:(parent,name,url,canRead,canWrite)=>{function LazyUint8Array(){this.lengthKnown=false;this.chunks=[]}LazyUint8Array.prototype.get=function LazyUint8Array_get(idx){if(idx>this.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});stream_ops.read=((stream,buffer,offset,length,position)=>{FS.forceLoadFile(node);var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency("cp "+fullname);function processData(byteArray){function finish(byteArray){if(preFinish)preFinish();if(!dontCreateFile){FS.createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}if(onload)onload();removeRunDependency(dep)}if(Browser.handledByPreloadPlugin(byteArray,fullname,finish,()=>{if(onerror)onerror();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url,byteArray=>processData(byteArray),onerror)}else{processData(url)}},indexedDB:()=>{return window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB},DB_NAME:()=>{return"EM_FS_"+window.location.pathname},DB_VERSION:20,DB_STORE_NAME:"FILE_DATA",saveFilesToDB:(paths,onload,onerror)=>{onload=onload||(()=>{});onerror=onerror||(()=>{});var indexedDB=FS.indexedDB();try{var openRequest=indexedDB.open(FS.DB_NAME(),FS.DB_VERSION)}catch(e){return onerror(e)}openRequest.onupgradeneeded=(()=>{out("creating db");var db=openRequest.result;db.createObjectStore(FS.DB_STORE_NAME)});openRequest.onsuccess=(()=>{var db=openRequest.result;var transaction=db.transaction([FS.DB_STORE_NAME],"readwrite");var files=transaction.objectStore(FS.DB_STORE_NAME);var ok=0,fail=0,total=paths.length;function finish(){if(fail==0)onload();else onerror()}paths.forEach(path=>{var putRequest=files.put(FS.analyzePath(path).object.contents,path);putRequest.onsuccess=(()=>{ok++;if(ok+fail==total)finish()});putRequest.onerror=(()=>{fail++;if(ok+fail==total)finish()})});transaction.onerror=onerror});openRequest.onerror=onerror},loadFilesFromDB:(paths,onload,onerror)=>{onload=onload||(()=>{});onerror=onerror||(()=>{});var indexedDB=FS.indexedDB();try{var openRequest=indexedDB.open(FS.DB_NAME(),FS.DB_VERSION)}catch(e){return onerror(e)}openRequest.onupgradeneeded=onerror;openRequest.onsuccess=(()=>{var db=openRequest.result;try{var transaction=db.transaction([FS.DB_STORE_NAME],"readonly")}catch(e){onerror(e);return}var files=transaction.objectStore(FS.DB_STORE_NAME);var ok=0,fail=0,total=paths.length;function finish(){if(fail==0)onload();else onerror()}paths.forEach(path=>{var getRequest=files.get(path);getRequest.onsuccess=(()=>{if(FS.analyzePath(path).exists){FS.unlink(path)}FS.createDataFile(PATH.dirname(path),PATH.basename(path),getRequest.result,true,true,true);ok++;if(ok+fail==total)finish()});getRequest.onerror=(()=>{fail++;if(ok+fail==total)finish()})});transaction.onerror=onerror});openRequest.onerror=onerror}};var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt:function(dirfd,path,allowEmpty){if(path[0]==="/"){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=FS.getStream(dirfd);if(!dirstream)throw new FS.ErrnoError(8);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat:function(func,path,buf){try{var stat=func(path)}catch(e){if(e&&e.node&&PATH.normalize(path)!==PATH.normalize(FS.getPath(e.node))){return-54}throw e}HEAP32[buf>>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},doMkdir:function(path,mode){path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0},doMknod:function(path,mode,dev){switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0},doReadlink:function(path,buf,bufsize){if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len},doAccess:function(path,amode){if(amode&~7){return-28}var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0},doDup:function(path,flags,suggestFD){var suggest=FS.getStream(suggestFD);if(suggest)FS.close(suggest);return FS.open(path,flags,0,suggestFD,suggestFD).fd},doReadv:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},get64:function(low,high){return low}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.open(stream.path,stream.flags,0,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 5:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 6:case 7:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_getcwd(buf,size){try{if(size===0)return-28;var cwd=FS.cwd();var cwdLengthInBytes=lengthBytesUTF8(cwd);if(size>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_open(path,flags,varargs){SYSCALLS.varargs=varargs;try{var pathname=SYSCALLS.getStr(path);var mode=varargs?SYSCALLS.get():0;var stream=FS.open(pathname,flags,mode);return stream.fd}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_readlink(path,buf,bufsize){try{path=SYSCALLS.getStr(path);return SYSCALLS.doReadlink(path,buf,bufsize)}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.stat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function _abort(){abort("")}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_max(){return 2147483648}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){var oldSize=HEAPU8.length;requestedSize=requestedSize>>>0;var maxHeapSize=_emscripten_get_heap_max();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(x+"="+env[x])}getEnvStrings.strings=strings}return getEnvStrings.strings}function _environ_get(__environ,environ_buf){var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAP32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}function _environ_sizes_get(penviron_count,penviron_buf_size){var strings=getEnvStrings();HEAP32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAP32[penviron_buf_size>>2]=bufSize;return 0}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function _fd_fdstat_get(fd,pbuf){try{var stream=SYSCALLS.getStreamFromFD(fd);var type=stream.tty?2:FS.isDir(stream.mode)?3:FS.isLink(stream.mode)?7:4;HEAP8[pbuf>>0]=type;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doReadv(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var stream=SYSCALLS.getStreamFromFD(fd);var HIGH_OFFSET=4294967296;var offset=offset_high*HIGH_OFFSET+(offset_low>>>0);var DOUBLE_LIMIT=9007199254740992;if(offset<=-DOUBLE_LIMIT||offset>=DOUBLE_LIMIT){return-61}FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doWritev(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function _getpwnam(){err("missing function: getpwnam");abort(-1)}function __isLeapYear(year){return year%4===0&&(year%100!==0||year%400===0)}function __arraySum(array,index){var sum=0;for(var i=0;i<=index;sum+=array[i++]){}return sum}var __MONTH_DAYS_LEAP=[31,29,31,30,31,30,31,31,30,31,30,31];var __MONTH_DAYS_REGULAR=[31,28,31,30,31,30,31,31,30,31,30,31];function __addDays(date,days){var newDate=new Date(date.getTime());while(days>0){var leap=__isLeapYear(newDate.getFullYear());var currentMonth=newDate.getMonth();var daysInCurrentMonth=(leap?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR)[currentMonth];if(days>daysInCurrentMonth-newDate.getDate()){days-=daysInCurrentMonth-newDate.getDate()+1;newDate.setDate(1);if(currentMonth<11){newDate.setMonth(currentMonth+1)}else{newDate.setMonth(0);newDate.setFullYear(newDate.getFullYear()+1)}}else{newDate.setDate(newDate.getDate()+days);return newDate}}return newDate}function _strftime(s,maxsize,format,tm){var tm_zone=HEAP32[tm+40>>2];var date={tm_sec:HEAP32[tm>>2],tm_min:HEAP32[tm+4>>2],tm_hour:HEAP32[tm+8>>2],tm_mday:HEAP32[tm+12>>2],tm_mon:HEAP32[tm+16>>2],tm_year:HEAP32[tm+20>>2],tm_wday:HEAP32[tm+24>>2],tm_yday:HEAP32[tm+28>>2],tm_isdst:HEAP32[tm+32>>2],tm_gmtoff:HEAP32[tm+36>>2],tm_zone:tm_zone?UTF8ToString(tm_zone):""};var pattern=UTF8ToString(format);var EXPANSION_RULES_1={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var rule in EXPANSION_RULES_1){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_1[rule])}var WEEKDAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var MONTHS=["January","February","March","April","May","June","July","August","September","October","November","December"];function leadingSomething(value,digits,character){var str=typeof value=="number"?value.toString():value||"";while(str.length0?1:0}var compare;if((compare=sgn(date1.getFullYear()-date2.getFullYear()))===0){if((compare=sgn(date1.getMonth()-date2.getMonth()))===0){compare=sgn(date1.getDate()-date2.getDate())}}return compare}function getFirstWeekStartDate(janFourth){switch(janFourth.getDay()){case 0:return new Date(janFourth.getFullYear()-1,11,29);case 1:return janFourth;case 2:return new Date(janFourth.getFullYear(),0,3);case 3:return new Date(janFourth.getFullYear(),0,2);case 4:return new Date(janFourth.getFullYear(),0,1);case 5:return new Date(janFourth.getFullYear()-1,11,31);case 6:return new Date(janFourth.getFullYear()-1,11,30)}}function getWeekBasedYear(date){var thisDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);var janFourthThisYear=new Date(thisDate.getFullYear(),0,4);var janFourthNextYear=new Date(thisDate.getFullYear()+1,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);if(compareByDay(firstWeekStartThisYear,thisDate)<=0){if(compareByDay(firstWeekStartNextYear,thisDate)<=0){return thisDate.getFullYear()+1}else{return thisDate.getFullYear()}}else{return thisDate.getFullYear()-1}}var EXPANSION_RULES_2={"%a":function(date){return WEEKDAYS[date.tm_wday].substring(0,3)},"%A":function(date){return WEEKDAYS[date.tm_wday]},"%b":function(date){return MONTHS[date.tm_mon].substring(0,3)},"%B":function(date){return MONTHS[date.tm_mon]},"%C":function(date){var year=date.tm_year+1900;return leadingNulls(year/100|0,2)},"%d":function(date){return leadingNulls(date.tm_mday,2)},"%e":function(date){return leadingSomething(date.tm_mday,2," ")},"%g":function(date){return getWeekBasedYear(date).toString().substring(2)},"%G":function(date){return getWeekBasedYear(date)},"%H":function(date){return leadingNulls(date.tm_hour,2)},"%I":function(date){var twelveHour=date.tm_hour;if(twelveHour==0)twelveHour=12;else if(twelveHour>12)twelveHour-=12;return leadingNulls(twelveHour,2)},"%j":function(date){return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900)?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,date.tm_mon-1),3)},"%m":function(date){return leadingNulls(date.tm_mon+1,2)},"%M":function(date){return leadingNulls(date.tm_min,2)},"%n":function(){return"\n"},"%p":function(date){if(date.tm_hour>=0&&date.tm_hour<12){return"AM"}else{return"PM"}},"%S":function(date){return leadingNulls(date.tm_sec,2)},"%t":function(){return"\t"},"%u":function(date){return date.tm_wday||7},"%U":function(date){var janFirst=new Date(date.tm_year+1900,0,1);var firstSunday=janFirst.getDay()===0?janFirst:__addDays(janFirst,7-janFirst.getDay());var endDate=new Date(date.tm_year+1900,date.tm_mon,date.tm_mday);if(compareByDay(firstSunday,endDate)<0){var februaryFirstUntilEndMonth=__arraySum(__isLeapYear(endDate.getFullYear())?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,endDate.getMonth()-1)-31;var firstSundayUntilEndJanuary=31-firstSunday.getDate();var days=firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate();return leadingNulls(Math.ceil(days/7),2)}return compareByDay(firstSunday,janFirst)===0?"01":"00"},"%V":function(date){var janFourthThisYear=new Date(date.tm_year+1900,0,4);var janFourthNextYear=new Date(date.tm_year+1901,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);var endDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);if(compareByDay(endDate,firstWeekStartThisYear)<0){return"53"}if(compareByDay(firstWeekStartNextYear,endDate)<=0){return"01"}var daysDifference;if(firstWeekStartThisYear.getFullYear()=0;off=Math.abs(off)/60;off=off/60*100+off%60;return(ahead?"+":"-")+String("0000"+off).slice(-4)},"%Z":function(date){return date.tm_zone},"%%":function(){return"%"}};pattern=pattern.replace(/%%/g,"\0\0");for(var rule in EXPANSION_RULES_2){if(pattern.includes(rule)){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_2[rule](date))}}pattern=pattern.replace(/\0\0/g,"%");var bytes=intArrayFromString(pattern,false);if(bytes.length>maxsize){return 0}writeArrayToMemory(bytes,s);return bytes.length-1}function _strftime_l(s,maxsize,format,tm){return _strftime(s,maxsize,format,tm)}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createDevice"]=FS.createDevice;Module["FS_unlink"]=FS.unlink;if(ENVIRONMENT_IS_NODE){requireNodeFS();NODEFS.staticInit()}ERRNO_CODES={"EPERM":63,"ENOENT":44,"ESRCH":71,"EINTR":27,"EIO":29,"ENXIO":60,"E2BIG":1,"ENOEXEC":45,"EBADF":8,"ECHILD":12,"EAGAIN":6,"EWOULDBLOCK":6,"ENOMEM":48,"EACCES":2,"EFAULT":21,"ENOTBLK":105,"EBUSY":10,"EEXIST":20,"EXDEV":75,"ENODEV":43,"ENOTDIR":54,"EISDIR":31,"EINVAL":28,"ENFILE":41,"EMFILE":33,"ENOTTY":59,"ETXTBSY":74,"EFBIG":22,"ENOSPC":51,"ESPIPE":70,"EROFS":69,"EMLINK":34,"EPIPE":64,"EDOM":18,"ERANGE":68,"ENOMSG":49,"EIDRM":24,"ECHRNG":106,"EL2NSYNC":156,"EL3HLT":107,"EL3RST":108,"ELNRNG":109,"EUNATCH":110,"ENOCSI":111,"EL2HLT":112,"EDEADLK":16,"ENOLCK":46,"EBADE":113,"EBADR":114,"EXFULL":115,"ENOANO":104,"EBADRQC":103,"EBADSLT":102,"EDEADLOCK":16,"EBFONT":101,"ENOSTR":100,"ENODATA":116,"ETIME":117,"ENOSR":118,"ENONET":119,"ENOPKG":120,"EREMOTE":121,"ENOLINK":47,"EADV":122,"ESRMNT":123,"ECOMM":124,"EPROTO":65,"EMULTIHOP":36,"EDOTDOT":125,"EBADMSG":9,"ENOTUNIQ":126,"EBADFD":127,"EREMCHG":128,"ELIBACC":129,"ELIBBAD":130,"ELIBSCN":131,"ELIBMAX":132,"ELIBEXEC":133,"ENOSYS":52,"ENOTEMPTY":55,"ENAMETOOLONG":37,"ELOOP":32,"EOPNOTSUPP":138,"EPFNOSUPPORT":139,"ECONNRESET":15,"ENOBUFS":42,"EAFNOSUPPORT":5,"EPROTOTYPE":67,"ENOTSOCK":57,"ENOPROTOOPT":50,"ESHUTDOWN":140,"ECONNREFUSED":14,"EADDRINUSE":3,"ECONNABORTED":13,"ENETUNREACH":40,"ENETDOWN":38,"ETIMEDOUT":73,"EHOSTDOWN":142,"EHOSTUNREACH":23,"EINPROGRESS":26,"EALREADY":7,"EDESTADDRREQ":17,"EMSGSIZE":35,"EPROTONOSUPPORT":66,"ESOCKTNOSUPPORT":137,"EADDRNOTAVAIL":4,"ENETRESET":39,"EISCONN":30,"ENOTCONN":53,"ETOOMANYREFS":141,"EUSERS":136,"EDQUOT":19,"ESTALE":72,"ENOTSUP":138,"ENOMEDIUM":148,"EILSEQ":25,"EOVERFLOW":61,"ECANCELED":11,"ENOTRECOVERABLE":56,"EOWNERDEAD":62,"ESTRPIPE":135};function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var asmLibraryArg={"a":___cxa_allocate_exception,"b":___cxa_throw,"e":___syscall_fcntl64,"t":___syscall_getcwd,"j":___syscall_ioctl,"k":___syscall_open,"o":___syscall_readlink,"p":___syscall_stat64,"c":_abort,"l":_emscripten_memcpy_big,"d":_emscripten_resize_heap,"r":_environ_get,"s":_environ_sizes_get,"f":_fd_close,"q":_fd_fdstat_get,"i":_fd_read,"m":_fd_seek,"h":_fd_write,"g":_getpwnam,"n":_strftime_l};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["v"]).apply(null,arguments)};var _main=Module["_main"]=function(){return(_main=Module["_main"]=Module["asm"]["w"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["x"]).apply(null,arguments)};var _itk_wasm_input_array_alloc=Module["_itk_wasm_input_array_alloc"]=function(){return(_itk_wasm_input_array_alloc=Module["_itk_wasm_input_array_alloc"]=Module["asm"]["z"]).apply(null,arguments)};var _itk_wasm_input_json_alloc=Module["_itk_wasm_input_json_alloc"]=function(){return(_itk_wasm_input_json_alloc=Module["_itk_wasm_input_json_alloc"]=Module["asm"]["A"]).apply(null,arguments)};var _itk_wasm_output_json_address=Module["_itk_wasm_output_json_address"]=function(){return(_itk_wasm_output_json_address=Module["_itk_wasm_output_json_address"]=Module["asm"]["B"]).apply(null,arguments)};var _itk_wasm_output_json_size=Module["_itk_wasm_output_json_size"]=function(){return(_itk_wasm_output_json_size=Module["_itk_wasm_output_json_size"]=Module["asm"]["C"]).apply(null,arguments)};var _itk_wasm_output_array_address=Module["_itk_wasm_output_array_address"]=function(){return(_itk_wasm_output_array_address=Module["_itk_wasm_output_array_address"]=Module["asm"]["D"]).apply(null,arguments)};var _itk_wasm_output_array_size=Module["_itk_wasm_output_array_size"]=function(){return(_itk_wasm_output_array_size=Module["_itk_wasm_output_array_size"]=Module["asm"]["E"]).apply(null,arguments)};var _itk_wasm_free_all=Module["_itk_wasm_free_all"]=function(){return(_itk_wasm_free_all=Module["_itk_wasm_free_all"]=Module["asm"]["F"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["G"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["H"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["I"]).apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return(stackAlloc=Module["stackAlloc"]=Module["asm"]["J"]).apply(null,arguments)};Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["writeArrayToMemory"]=writeArrayToMemory;Module["writeAsciiToMemory"]=writeAsciiToMemory;Module["addRunDependency"]=addRunDependency;Module["removeRunDependency"]=removeRunDependency;Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createDevice"]=FS.createDevice;Module["FS_unlink"]=FS.unlink;Module["callMain"]=callMain;Module["AsciiToString"]=AsciiToString;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}var calledMain=false;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function callMain(args){var entryFunction=Module["_main"];args=args||[];var argc=args.length+1;var argv=stackAlloc((argc+1)*4);HEAP32[argv>>2]=allocateUTF8OnStack(thisProgram);for(var i=1;i>2)+i]=allocateUTF8OnStack(args[i-1])}HEAP32[(argv>>2)+argc]=0;try{var ret=entryFunction(argc,argv);exit(ret,true);return ret}catch(e){return handleException(e)}finally{calledMain=true}}function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();if(shouldRunNow)callMain(args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;function exit(status,implicit){EXITSTATUS=status;if(keepRuntimeAlive()){}else{exitRuntime()}procExit(status)}function procExit(code){EXITSTATUS=code;if(!keepRuntimeAlive()){if(Module["onExit"])Module["onExit"](code);ABORT=true}quit_(code,new ExitStatus(code))}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=false;if(Module["noInitialRun"])shouldRunNow=false;run();Module.mountContainingDir=function(filePath){if(!ENVIRONMENT_IS_NODE){return}var path=require("path");var containingDir=path.dirname(filePath);if(FS.isDir(containingDir)||containingDir==="/"){return}var currentDir="/";var splitContainingDir=containingDir.split(path.sep);for(var ii=1;ii { var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename; return ( function(BloscZarr) { BloscZarr = BloscZarr || {}; null;var Module=typeof BloscZarr!="undefined"?BloscZarr:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var mStdout=null;var mStderr=null;Module["resetModuleStdout"]=function(){mStdout=""};Module["resetModuleStderr"]=function(){mStderr=""};Module["print"]=function(text){console.log(text);mStdout+=text+"\n"};Module["printErr"]=function(text){console.error(text);mStderr+=text+"\n"};Module["getModuleStdout"]=function(){return mStdout};Module["getModuleStderr"]=function(){return mStderr};var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;function logExceptionOnExit(e){if(e instanceof ExitStatus)return;let toLog=e;err("exiting due to exception: "+toLog)}var fs;var nodePath;var requireNodeFS;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}requireNodeFS=(()=>{if(!nodePath){fs=require("fs");nodePath=require("path")}});read_=function shell_read(filename,binary){requireNodeFS();filename=nodePath["normalize"](filename);return fs.readFileSync(filename,binary?undefined:"utf8")};readBinary=(filename=>{var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret});readAsync=((filename,onload,onerror)=>{requireNodeFS();filename=nodePath["normalize"](filename);fs.readFile(filename,function(err,data){if(err)onerror(err);else onload(data.buffer)})});if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",function(reason){throw reason});quit_=((status,toThrow)=>{if(keepRuntimeAlive()){process["exitCode"]=status;throw toThrow}logExceptionOnExit(toThrow);process["exit"](status)});Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=(url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText});if(ENVIRONMENT_IS_WORKER){readBinary=(url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)})}readAsync=((url,onload,onerror)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=(()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()});xhr.onerror=onerror;xhr.send(null)})}setWindowTitle=(title=>document.title=title)}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort(text)}}function getCFunc(ident){var func=Module["_"+ident];return func}function ccall(ident,returnType,argTypes,args,opts){var toC={"string":function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){var len=(str.length<<2)+1;ret=stackAlloc(len);stringToUTF8(str,ret,len)}return ret},"array":function(arr){var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string")return UTF8ToString(ret);if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}function AsciiToString(ptr){var str="";while(1){var ch=HEAPU8[ptr++>>0];if(!ch)return str;str+=String.fromCharCode(ch)}}function allocateUTF8OnStack(str){var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8Array(str,HEAP8,ret,size);return ret}function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;var runtimeKeepaliveCounter=0;function keepRuntimeAlive(){return noExitRuntime||runtimeKeepaliveCounter>0}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();FS.ignorePermissions=false;TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){{if(Module["onAbort"]){Module["onAbort"](what)}}what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return filename.startsWith(dataURIPrefix)}function isFileURI(filename){return filename.startsWith("file://")}var wasmBinaryFile;wasmBinaryFile="BloscZarr.umd.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch=="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["u"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["y"];addOnInit(Module["asm"]["v"]);removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(function(instance){return instance}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch=="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiationResult,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiationResult)})})}else{return instantiateArrayBuffer(receiveInstantiationResult)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}var tempDouble;var tempI64;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func=="number"){if(callback.arg===undefined){getWasmTableEntry(func)()}else{getWasmTableEntry(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}var wasmTableMirror=[];function getWasmTableEntry(funcPtr){var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func}function handleException(e){if(e instanceof ExitStatus||e=="unwind"){return EXITSTATUS}quit_(1,e)}function ___cxa_allocate_exception(size){return _malloc(size+16)+16}function ExceptionInfo(excPtr){this.excPtr=excPtr;this.ptr=excPtr-16;this.set_type=function(type){HEAP32[this.ptr+4>>2]=type};this.get_type=function(){return HEAP32[this.ptr+4>>2]};this.set_destructor=function(destructor){HEAP32[this.ptr+8>>2]=destructor};this.get_destructor=function(){return HEAP32[this.ptr+8>>2]};this.set_refcount=function(refcount){HEAP32[this.ptr>>2]=refcount};this.set_caught=function(caught){caught=caught?1:0;HEAP8[this.ptr+12>>0]=caught};this.get_caught=function(){return HEAP8[this.ptr+12>>0]!=0};this.set_rethrown=function(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13>>0]=rethrown};this.get_rethrown=function(){return HEAP8[this.ptr+13>>0]!=0};this.init=function(type,destructor){this.set_type(type);this.set_destructor(destructor);this.set_refcount(0);this.set_caught(false);this.set_rethrown(false)};this.add_ref=function(){var value=HEAP32[this.ptr>>2];HEAP32[this.ptr>>2]=value+1};this.release_ref=function(){var prev=HEAP32[this.ptr>>2];HEAP32[this.ptr>>2]=prev-1;return prev===1}}var exceptionLast=0;var uncaughtExceptionCount=0;function ___cxa_throw(ptr,type,destructor){var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw ptr}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};function getRandomDevice(){if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){var randomBuffer=new Uint8Array(1);return function(){crypto.getRandomValues(randomBuffer);return randomBuffer[0]}}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");return function(){return crypto_module["randomBytes"](1)[0]}}catch(e){}}return function(){abort("randomDevice")}}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:function(from,to){from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function mmapAlloc(size){abort()}var MEMFS={ops_table:null,mount:function(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode:function(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}if(!MEMFS.ops_table){MEMFS.ops_table={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}}}var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp}return node},getFileDataAsTypedArray:function(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage:function(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr:function(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr:function(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup:function(parent,name){throw FS.genericErrors[44]},mknod:function(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename:function(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp;old_node.parent=new_dir},unlink:function(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir:function(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir:function(node){var entries=[".",".."];for(var key in node.contents){if(!node.contents.hasOwnProperty(key)){continue}entries.push(key)}return entries},symlink:function(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink:function(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read:function(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{NODEFS.isWindows=!!process.platform.match(/^win/);var flags=process["binding"]("constants");if(flags["fs"]){flags=flags["fs"]}NODEFS.flagsForNodeMap={1024:flags["O_APPEND"],64:flags["O_CREAT"],128:flags["O_EXCL"],256:flags["O_NOCTTY"],0:flags["O_RDONLY"],2:flags["O_RDWR"],4096:flags["O_SYNC"],512:flags["O_TRUNC"],1:flags["O_WRONLY"],131072:flags["O_NOFOLLOW"]}},convertNodeCode:e=>{var code=e.code;return ERRNO_CODES[code]},mount:mount=>{return NODEFS.createNode(null,"/",NODEFS.getMode(mount.opts.root),0)},createNode:(parent,name,mode,dev)=>{if(!FS.isDir(mode)&&!FS.isFile(mode)&&!FS.isLink(mode)){throw new FS.ErrnoError(28)}var node=FS.createNode(parent,name,mode);node.node_ops=NODEFS.node_ops;node.stream_ops=NODEFS.stream_ops;return node},getMode:path=>{var stat;try{stat=fs.lstatSync(path);if(NODEFS.isWindows){stat.mode=stat.mode|(stat.mode&292)>>2}}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}return stat.mode},realPath:node=>{var parts=[];while(node.parent!==node){parts.push(node.name);node=node.parent}parts.push(node.mount.opts.root);parts.reverse();return PATH.join.apply(null,parts)},flagsForNode:flags=>{flags&=~2097152;flags&=~2048;flags&=~32768;flags&=~524288;flags&=~65536;var newFlags=0;for(var k in NODEFS.flagsForNodeMap){if(flags&k){newFlags|=NODEFS.flagsForNodeMap[k];flags^=k}}if(!flags){return newFlags}else{throw new FS.ErrnoError(28)}},node_ops:{getattr:node=>{var path=NODEFS.realPath(node);var stat;try{stat=fs.lstatSync(path)}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}if(NODEFS.isWindows&&!stat.blksize){stat.blksize=4096}if(NODEFS.isWindows&&!stat.blocks){stat.blocks=(stat.size+stat.blksize-1)/stat.blksize|0}return{dev:stat.dev,ino:stat.ino,mode:stat.mode,nlink:stat.nlink,uid:stat.uid,gid:stat.gid,rdev:stat.rdev,size:stat.size,atime:stat.atime,mtime:stat.mtime,ctime:stat.ctime,blksize:stat.blksize,blocks:stat.blocks}},setattr:(node,attr)=>{var path=NODEFS.realPath(node);try{if(attr.mode!==undefined){fs.chmodSync(path,attr.mode);node.mode=attr.mode}if(attr.timestamp!==undefined){var date=new Date(attr.timestamp);fs.utimesSync(path,date,date)}if(attr.size!==undefined){fs.truncateSync(path,attr.size)}}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},lookup:(parent,name)=>{var path=PATH.join2(NODEFS.realPath(parent),name);var mode=NODEFS.getMode(path);return NODEFS.createNode(parent,name,mode)},mknod:(parent,name,mode,dev)=>{var node=NODEFS.createNode(parent,name,mode,dev);var path=NODEFS.realPath(node);try{if(FS.isDir(node.mode)){fs.mkdirSync(path,node.mode)}else{fs.writeFileSync(path,"",{mode:node.mode})}}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}return node},rename:(oldNode,newDir,newName)=>{var oldPath=NODEFS.realPath(oldNode);var newPath=PATH.join2(NODEFS.realPath(newDir),newName);try{fs.renameSync(oldPath,newPath)}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}oldNode.name=newName},unlink:(parent,name)=>{var path=PATH.join2(NODEFS.realPath(parent),name);try{fs.unlinkSync(path)}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},rmdir:(parent,name)=>{var path=PATH.join2(NODEFS.realPath(parent),name);try{fs.rmdirSync(path)}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},readdir:node=>{var path=NODEFS.realPath(node);try{return fs.readdirSync(path)}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},symlink:(parent,newName,oldPath)=>{var newPath=PATH.join2(NODEFS.realPath(parent),newName);try{fs.symlinkSync(oldPath,newPath)}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},readlink:node=>{var path=NODEFS.realPath(node);try{path=fs.readlinkSync(path);path=nodePath.relative(nodePath.resolve(node.mount.opts.root),path);return path}catch(e){if(!e.code)throw e;if(e.code==="UNKNOWN")throw new FS.ErrnoError(28);throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}}},stream_ops:{open:stream=>{var path=NODEFS.realPath(stream.node);try{if(FS.isFile(stream.node.mode)){stream.nfd=fs.openSync(path,NODEFS.flagsForNode(stream.flags))}}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},close:stream=>{try{if(FS.isFile(stream.node.mode)&&stream.nfd){fs.closeSync(stream.nfd)}}catch(e){if(!e.code)throw e;throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},read:(stream,buffer,offset,length,position)=>{if(length===0)return 0;try{return fs.readSync(stream.nfd,Buffer.from(buffer.buffer),offset,length,position)}catch(e){throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},write:(stream,buffer,offset,length,position)=>{try{return fs.writeSync(stream.nfd,Buffer.from(buffer.buffer),offset,length,position)}catch(e){throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}},llseek:(stream,offset,whence)=>{var position=offset;if(whence===1){position+=stream.position}else if(whence===2){if(FS.isFile(stream.node.mode)){try{var stat=fs.fstatSync(stream.nfd);position+=stat.size}catch(e){throw new FS.ErrnoError(NODEFS.convertNodeCode(e))}}}if(position<0){throw new FS.ErrnoError(28)}return position},mmap:(stream,address,length,position,prot,flags)=>{if(address!==0){throw new FS.ErrnoError(28)}if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}var ptr=mmapAlloc(length);NODEFS.stream_ops.read(stream,HEAP8,ptr,length,position);return{ptr:ptr,allocated:true}},msync:(stream,buffer,offset,length,mmapFlags)=>{if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(mmapFlags&2){return 0}var bytesWritten=NODEFS.stream_ops.write(stream,buffer,0,length,offset,false);return 0}}};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,ErrnoError:null,genericErrors:{},filesystems:null,syncFSRequests:0,lookupPath:(path,opts={})=>{path=PATH_FS.resolve(FS.cwd(),path);if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};for(var key in defaults){if(opts[key]===undefined){opts[key]=defaults[key]}}if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(p=>!!p),false);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:node=>{var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:(parentid,name)=>{var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:node=>{var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:node=>{var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:(parent,name)=>{var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:(parent,name,mode,rdev)=>{var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:node=>{FS.hashRemoveNode(node)},isRoot:node=>{return node===node.parent},isMountpoint:node=>{return!!node.mounted},isFile:mode=>{return(mode&61440)===32768},isDir:mode=>{return(mode&61440)===16384},isLink:mode=>{return(mode&61440)===40960},isChrdev:mode=>{return(mode&61440)===8192},isBlkdev:mode=>{return(mode&61440)===24576},isFIFO:mode=>{return(mode&61440)===4096},isSocket:mode=>{return(mode&49152)===49152},flagModes:{"r":0,"r+":2,"w":577,"w+":578,"a":1089,"a+":1090},modeStringToFlags:str=>{var flags=FS.flagModes[str];if(typeof flags=="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:flag=>{var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:(node,perms)=>{if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup:dir=>{var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:(dir,name)=>{try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:(dir,name,isdir)=>{var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:(node,flags)=>{if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:(fd_start=0,fd_end=FS.MAX_OPEN_FDS)=>{for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:fd=>FS.streams[fd],createStream:(stream,fd_start,fd_end)=>{if(!FS.FSStream){FS.FSStream=function(){};FS.FSStream.prototype={object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}}}}stream=Object.assign(new FS.FSStream,stream);var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:fd=>{FS.streams[fd]=null},chrdev_stream_ops:{open:stream=>{var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:()=>{throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice:(dev,ops)=>{FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts:mount=>{var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:(populate,callback)=>{if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:(type,opts,mountpoint)=>{var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:mountpoint=>{var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:(parent,name)=>{return parent.node_ops.lookup(parent,name)},mknod:(path,mode,dev)=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:(path,mode)=>{mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:(path,mode)=>{mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:(path,mode)=>{var dirs=path.split("/");var d="";for(var i=0;i{if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink:(oldpath,newpath)=>{if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename:(old_path,new_path)=>{var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name)}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir:path=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir:path=>{var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node.node_ops.readdir){throw new FS.ErrnoError(54)}return node.node_ops.readdir(node)},unlink:path=>{var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink:path=>{var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return PATH_FS.resolve(FS.getPath(link.parent),link.node_ops.readlink(link))},stat:(path,dontFollow)=>{var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;if(!node){throw new FS.ErrnoError(44)}if(!node.node_ops.getattr){throw new FS.ErrnoError(63)}return node.node_ops.getattr(node)},lstat:path=>{return FS.stat(path,true)},chmod:(path,mode,dontFollow)=>{var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{mode:mode&4095|node.mode&~4095,timestamp:Date.now()})},lchmod:(path,mode)=>{FS.chmod(path,mode,true)},fchmod:(fd,mode)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chmod(stream.node,mode)},chown:(path,uid,gid,dontFollow)=>{var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}node.node_ops.setattr(node,{timestamp:Date.now()})},lchown:(path,uid,gid)=>{FS.chown(path,uid,gid,true)},fchown:(fd,uid,gid)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}FS.chown(stream.node,uid,gid)},truncate:(path,len)=>{if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}if(!node.node_ops.setattr){throw new FS.ErrnoError(63)}if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}node.node_ops.setattr(node,{size:len,timestamp:Date.now()})},ftruncate:(fd,len)=>{var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.truncate(stream.node,len)},utime:(path,atime,mtime)=>{var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;node.node_ops.setattr(node,{timestamp:Math.max(atime,mtime)})},open:(path,flags,mode,fd_start,fd_end)=>{if(path===""){throw new FS.ErrnoError(44)}flags=typeof flags=="string"?FS.modeStringToFlags(flags):flags;mode=typeof mode=="undefined"?438:mode;if(flags&64){mode=mode&4095|32768}else{mode=0}var node;if(typeof path=="object"){node=path}else{path=PATH.normalize(path);try{var lookup=FS.lookupPath(path,{follow:!(flags&131072)});node=lookup.node}catch(e){}}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else{node=FS.mknod(path,mode,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node:node,path:FS.getPath(node),flags:flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false},fd_start,fd_end);if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(Module["logReadFiles"]&&!(flags&1)){if(!FS.readFiles)FS.readFiles={};if(!(path in FS.readFiles)){FS.readFiles[path]=1}}return stream},close:stream=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed:stream=>{return stream.fd===null},llseek:(stream,offset,whence)=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read:(stream,buffer,offset,length,position)=>{if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write:(stream,buffer,offset,length,position,canOwn)=>{if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},allocate:(stream,offset,length)=>{if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(offset<0||length<=0){throw new FS.ErrnoError(28)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(!FS.isFile(stream.node.mode)&&!FS.isDir(stream.node.mode)){throw new FS.ErrnoError(43)}if(!stream.stream_ops.allocate){throw new FS.ErrnoError(138)}stream.stream_ops.allocate(stream,offset,length)},mmap:(stream,address,length,position,prot,flags)=>{if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}return stream.stream_ops.mmap(stream,address,length,position,prot,flags)},msync:(stream,buffer,offset,length,mmapFlags)=>{if(!stream||!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},munmap:stream=>0,ioctl:(stream,cmd,arg)=>{if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile:(path,opts={})=>{opts.flags=opts.flags||0;opts.encoding=opts.encoding||"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){throw new Error('Invalid encoding type "'+opts.encoding+'"')}var ret;var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){ret=UTF8ArrayToString(buf,0)}else if(opts.encoding==="binary"){ret=buf}FS.close(stream);return ret},writeFile:(path,data,opts={})=>{opts.flags=opts.flags||577;var stream=FS.open(path,opts.flags,opts.mode);if(typeof data=="string"){var buf=new Uint8Array(lengthBytesUTF8(data)+1);var actualNumBytes=stringToUTF8Array(data,buf,0,buf.length);FS.write(stream,buf,0,actualNumBytes,undefined,opts.canOwn)}else if(ArrayBuffer.isView(data)){FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn)}else{throw new Error("Unsupported data type")}FS.close(stream)},cwd:()=>FS.currentPath,chdir:path=>{var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories:()=>{FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices:()=>{FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var random_device=getRandomDevice();FS.createDevice("/dev","random",random_device);FS.createDevice("/dev","urandom",random_device);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories:()=>{FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount:()=>{var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup:(parent,name)=>{var fd=+name;var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams:()=>{if(Module["stdin"]){FS.createDevice("/dev","stdin",Module["stdin"])}else{FS.symlink("/dev/tty","/dev/stdin")}if(Module["stdout"]){FS.createDevice("/dev","stdout",null,Module["stdout"])}else{FS.symlink("/dev/tty","/dev/stdout")}if(Module["stderr"]){FS.createDevice("/dev","stderr",null,Module["stderr"])}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},ensureErrnoError:()=>{if(FS.ErrnoError)return;FS.ErrnoError=function ErrnoError(errno,node){this.node=node;this.setErrno=function(errno){this.errno=errno};this.setErrno(errno);this.message="FS error"};FS.ErrnoError.prototype=new Error;FS.ErrnoError.prototype.constructor=FS.ErrnoError;[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack=""})},staticInit:()=>{FS.ensureErrnoError();FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={"MEMFS":MEMFS,"NODEFS":NODEFS}},init:(input,output,error)=>{FS.init.initialized=true;FS.ensureErrnoError();Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit:()=>{FS.init.initialized=false;for(var i=0;i{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode},findObject:(path,dontResolveLastLink)=>{var ret=FS.analyzePath(path,dontResolveLastLink);if(ret.exists){return ret.object}else{return null}},analyzePath:(path,dontResolveLastLink)=>{try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath:(parent,path,canRead,canWrite)=>{parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){}parent=current}return current},createFile:(parent,name,properties,canRead,canWrite)=>{var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS.getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile:(parent,name,data,canRead,canWrite,canOwn)=>{var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS.getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){if(typeof data=="string"){var arr=new Array(data.length);for(var i=0,len=data.length;i{var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS.getMode(!!input,!!output);if(!FS.createDevice.major)FS.createDevice.major=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open:stream=>{stream.seekable=false},close:stream=>{if(output&&output.buffer&&output.buffer.length){output(10)}},read:(stream,buffer,offset,length,pos)=>{var bytesRead=0;for(var i=0;i{for(var i=0;i{if(obj.isDevice||obj.isFolder||obj.link||obj.contents)return true;if(typeof XMLHttpRequest!="undefined"){throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.")}else if(read_){try{obj.contents=intArrayFromString(read_(obj.url),true);obj.usedBytes=obj.contents.length}catch(e){throw new FS.ErrnoError(29)}}else{throw new Error("Cannot load without read() or XMLHttpRequest.")}},createLazyFile:(parent,name,url,canRead,canWrite)=>{function LazyUint8Array(){this.lengthKnown=false;this.chunks=[]}LazyUint8Array.prototype.get=function LazyUint8Array_get(idx){if(idx>this.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});stream_ops.read=((stream,buffer,offset,length,position)=>{FS.forceLoadFile(node);var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency("cp "+fullname);function processData(byteArray){function finish(byteArray){if(preFinish)preFinish();if(!dontCreateFile){FS.createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}if(onload)onload();removeRunDependency(dep)}if(Browser.handledByPreloadPlugin(byteArray,fullname,finish,()=>{if(onerror)onerror();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url,byteArray=>processData(byteArray),onerror)}else{processData(url)}},indexedDB:()=>{return window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB},DB_NAME:()=>{return"EM_FS_"+window.location.pathname},DB_VERSION:20,DB_STORE_NAME:"FILE_DATA",saveFilesToDB:(paths,onload,onerror)=>{onload=onload||(()=>{});onerror=onerror||(()=>{});var indexedDB=FS.indexedDB();try{var openRequest=indexedDB.open(FS.DB_NAME(),FS.DB_VERSION)}catch(e){return onerror(e)}openRequest.onupgradeneeded=(()=>{out("creating db");var db=openRequest.result;db.createObjectStore(FS.DB_STORE_NAME)});openRequest.onsuccess=(()=>{var db=openRequest.result;var transaction=db.transaction([FS.DB_STORE_NAME],"readwrite");var files=transaction.objectStore(FS.DB_STORE_NAME);var ok=0,fail=0,total=paths.length;function finish(){if(fail==0)onload();else onerror()}paths.forEach(path=>{var putRequest=files.put(FS.analyzePath(path).object.contents,path);putRequest.onsuccess=(()=>{ok++;if(ok+fail==total)finish()});putRequest.onerror=(()=>{fail++;if(ok+fail==total)finish()})});transaction.onerror=onerror});openRequest.onerror=onerror},loadFilesFromDB:(paths,onload,onerror)=>{onload=onload||(()=>{});onerror=onerror||(()=>{});var indexedDB=FS.indexedDB();try{var openRequest=indexedDB.open(FS.DB_NAME(),FS.DB_VERSION)}catch(e){return onerror(e)}openRequest.onupgradeneeded=onerror;openRequest.onsuccess=(()=>{var db=openRequest.result;try{var transaction=db.transaction([FS.DB_STORE_NAME],"readonly")}catch(e){onerror(e);return}var files=transaction.objectStore(FS.DB_STORE_NAME);var ok=0,fail=0,total=paths.length;function finish(){if(fail==0)onload();else onerror()}paths.forEach(path=>{var getRequest=files.get(path);getRequest.onsuccess=(()=>{if(FS.analyzePath(path).exists){FS.unlink(path)}FS.createDataFile(PATH.dirname(path),PATH.basename(path),getRequest.result,true,true,true);ok++;if(ok+fail==total)finish()});getRequest.onerror=(()=>{fail++;if(ok+fail==total)finish()})});transaction.onerror=onerror});openRequest.onerror=onerror}};var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt:function(dirfd,path,allowEmpty){if(path[0]==="/"){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=FS.getStream(dirfd);if(!dirstream)throw new FS.ErrnoError(8);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat:function(func,path,buf){try{var stat=func(path)}catch(e){if(e&&e.node&&PATH.normalize(path)!==PATH.normalize(FS.getPath(e.node))){return-54}throw e}HEAP32[buf>>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},doMkdir:function(path,mode){path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0},doMknod:function(path,mode,dev){switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0},doReadlink:function(path,buf,bufsize){if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len},doAccess:function(path,amode){if(amode&~7){return-28}var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0},doDup:function(path,flags,suggestFD){var suggest=FS.getStream(suggestFD);if(suggest)FS.close(suggest);return FS.open(path,flags,0,suggestFD,suggestFD).fd},doReadv:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},get64:function(low,high){return low}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.open(stream.path,stream.flags,0,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 5:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 6:case 7:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_getcwd(buf,size){try{if(size===0)return-28;var cwd=FS.cwd();var cwdLengthInBytes=lengthBytesUTF8(cwd);if(size>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_open(path,flags,varargs){SYSCALLS.varargs=varargs;try{var pathname=SYSCALLS.getStr(path);var mode=varargs?SYSCALLS.get():0;var stream=FS.open(pathname,flags,mode);return stream.fd}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_readlink(path,buf,bufsize){try{path=SYSCALLS.getStr(path);return SYSCALLS.doReadlink(path,buf,bufsize)}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function ___syscall_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.stat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return-e.errno}}function _abort(){abort("")}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_max(){return 2147483648}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){var oldSize=HEAPU8.length;requestedSize=requestedSize>>>0;var maxHeapSize=_emscripten_get_heap_max();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(x+"="+env[x])}getEnvStrings.strings=strings}return getEnvStrings.strings}function _environ_get(__environ,environ_buf){var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAP32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}function _environ_sizes_get(penviron_count,penviron_buf_size){var strings=getEnvStrings();HEAP32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAP32[penviron_buf_size>>2]=bufSize;return 0}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function _fd_fdstat_get(fd,pbuf){try{var stream=SYSCALLS.getStreamFromFD(fd);var type=stream.tty?2:FS.isDir(stream.mode)?3:FS.isLink(stream.mode)?7:4;HEAP8[pbuf>>0]=type;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doReadv(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var stream=SYSCALLS.getStreamFromFD(fd);var HIGH_OFFSET=4294967296;var offset=offset_high*HIGH_OFFSET+(offset_low>>>0);var DOUBLE_LIMIT=9007199254740992;if(offset<=-DOUBLE_LIMIT||offset>=DOUBLE_LIMIT){return-61}FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doWritev(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e instanceof FS.ErrnoError))throw e;return e.errno}}function _getpwnam(){err("missing function: getpwnam");abort(-1)}function __isLeapYear(year){return year%4===0&&(year%100!==0||year%400===0)}function __arraySum(array,index){var sum=0;for(var i=0;i<=index;sum+=array[i++]){}return sum}var __MONTH_DAYS_LEAP=[31,29,31,30,31,30,31,31,30,31,30,31];var __MONTH_DAYS_REGULAR=[31,28,31,30,31,30,31,31,30,31,30,31];function __addDays(date,days){var newDate=new Date(date.getTime());while(days>0){var leap=__isLeapYear(newDate.getFullYear());var currentMonth=newDate.getMonth();var daysInCurrentMonth=(leap?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR)[currentMonth];if(days>daysInCurrentMonth-newDate.getDate()){days-=daysInCurrentMonth-newDate.getDate()+1;newDate.setDate(1);if(currentMonth<11){newDate.setMonth(currentMonth+1)}else{newDate.setMonth(0);newDate.setFullYear(newDate.getFullYear()+1)}}else{newDate.setDate(newDate.getDate()+days);return newDate}}return newDate}function _strftime(s,maxsize,format,tm){var tm_zone=HEAP32[tm+40>>2];var date={tm_sec:HEAP32[tm>>2],tm_min:HEAP32[tm+4>>2],tm_hour:HEAP32[tm+8>>2],tm_mday:HEAP32[tm+12>>2],tm_mon:HEAP32[tm+16>>2],tm_year:HEAP32[tm+20>>2],tm_wday:HEAP32[tm+24>>2],tm_yday:HEAP32[tm+28>>2],tm_isdst:HEAP32[tm+32>>2],tm_gmtoff:HEAP32[tm+36>>2],tm_zone:tm_zone?UTF8ToString(tm_zone):""};var pattern=UTF8ToString(format);var EXPANSION_RULES_1={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var rule in EXPANSION_RULES_1){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_1[rule])}var WEEKDAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var MONTHS=["January","February","March","April","May","June","July","August","September","October","November","December"];function leadingSomething(value,digits,character){var str=typeof value=="number"?value.toString():value||"";while(str.length0?1:0}var compare;if((compare=sgn(date1.getFullYear()-date2.getFullYear()))===0){if((compare=sgn(date1.getMonth()-date2.getMonth()))===0){compare=sgn(date1.getDate()-date2.getDate())}}return compare}function getFirstWeekStartDate(janFourth){switch(janFourth.getDay()){case 0:return new Date(janFourth.getFullYear()-1,11,29);case 1:return janFourth;case 2:return new Date(janFourth.getFullYear(),0,3);case 3:return new Date(janFourth.getFullYear(),0,2);case 4:return new Date(janFourth.getFullYear(),0,1);case 5:return new Date(janFourth.getFullYear()-1,11,31);case 6:return new Date(janFourth.getFullYear()-1,11,30)}}function getWeekBasedYear(date){var thisDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);var janFourthThisYear=new Date(thisDate.getFullYear(),0,4);var janFourthNextYear=new Date(thisDate.getFullYear()+1,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);if(compareByDay(firstWeekStartThisYear,thisDate)<=0){if(compareByDay(firstWeekStartNextYear,thisDate)<=0){return thisDate.getFullYear()+1}else{return thisDate.getFullYear()}}else{return thisDate.getFullYear()-1}}var EXPANSION_RULES_2={"%a":function(date){return WEEKDAYS[date.tm_wday].substring(0,3)},"%A":function(date){return WEEKDAYS[date.tm_wday]},"%b":function(date){return MONTHS[date.tm_mon].substring(0,3)},"%B":function(date){return MONTHS[date.tm_mon]},"%C":function(date){var year=date.tm_year+1900;return leadingNulls(year/100|0,2)},"%d":function(date){return leadingNulls(date.tm_mday,2)},"%e":function(date){return leadingSomething(date.tm_mday,2," ")},"%g":function(date){return getWeekBasedYear(date).toString().substring(2)},"%G":function(date){return getWeekBasedYear(date)},"%H":function(date){return leadingNulls(date.tm_hour,2)},"%I":function(date){var twelveHour=date.tm_hour;if(twelveHour==0)twelveHour=12;else if(twelveHour>12)twelveHour-=12;return leadingNulls(twelveHour,2)},"%j":function(date){return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900)?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,date.tm_mon-1),3)},"%m":function(date){return leadingNulls(date.tm_mon+1,2)},"%M":function(date){return leadingNulls(date.tm_min,2)},"%n":function(){return"\n"},"%p":function(date){if(date.tm_hour>=0&&date.tm_hour<12){return"AM"}else{return"PM"}},"%S":function(date){return leadingNulls(date.tm_sec,2)},"%t":function(){return"\t"},"%u":function(date){return date.tm_wday||7},"%U":function(date){var janFirst=new Date(date.tm_year+1900,0,1);var firstSunday=janFirst.getDay()===0?janFirst:__addDays(janFirst,7-janFirst.getDay());var endDate=new Date(date.tm_year+1900,date.tm_mon,date.tm_mday);if(compareByDay(firstSunday,endDate)<0){var februaryFirstUntilEndMonth=__arraySum(__isLeapYear(endDate.getFullYear())?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,endDate.getMonth()-1)-31;var firstSundayUntilEndJanuary=31-firstSunday.getDate();var days=firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate();return leadingNulls(Math.ceil(days/7),2)}return compareByDay(firstSunday,janFirst)===0?"01":"00"},"%V":function(date){var janFourthThisYear=new Date(date.tm_year+1900,0,4);var janFourthNextYear=new Date(date.tm_year+1901,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);var endDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);if(compareByDay(endDate,firstWeekStartThisYear)<0){return"53"}if(compareByDay(firstWeekStartNextYear,endDate)<=0){return"01"}var daysDifference;if(firstWeekStartThisYear.getFullYear()=0;off=Math.abs(off)/60;off=off/60*100+off%60;return(ahead?"+":"-")+String("0000"+off).slice(-4)},"%Z":function(date){return date.tm_zone},"%%":function(){return"%"}};pattern=pattern.replace(/%%/g,"\0\0");for(var rule in EXPANSION_RULES_2){if(pattern.includes(rule)){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_2[rule](date))}}pattern=pattern.replace(/\0\0/g,"%");var bytes=intArrayFromString(pattern,false);if(bytes.length>maxsize){return 0}writeArrayToMemory(bytes,s);return bytes.length-1}function _strftime_l(s,maxsize,format,tm){return _strftime(s,maxsize,format,tm)}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createDevice"]=FS.createDevice;Module["FS_unlink"]=FS.unlink;if(ENVIRONMENT_IS_NODE){requireNodeFS();NODEFS.staticInit()}ERRNO_CODES={"EPERM":63,"ENOENT":44,"ESRCH":71,"EINTR":27,"EIO":29,"ENXIO":60,"E2BIG":1,"ENOEXEC":45,"EBADF":8,"ECHILD":12,"EAGAIN":6,"EWOULDBLOCK":6,"ENOMEM":48,"EACCES":2,"EFAULT":21,"ENOTBLK":105,"EBUSY":10,"EEXIST":20,"EXDEV":75,"ENODEV":43,"ENOTDIR":54,"EISDIR":31,"EINVAL":28,"ENFILE":41,"EMFILE":33,"ENOTTY":59,"ETXTBSY":74,"EFBIG":22,"ENOSPC":51,"ESPIPE":70,"EROFS":69,"EMLINK":34,"EPIPE":64,"EDOM":18,"ERANGE":68,"ENOMSG":49,"EIDRM":24,"ECHRNG":106,"EL2NSYNC":156,"EL3HLT":107,"EL3RST":108,"ELNRNG":109,"EUNATCH":110,"ENOCSI":111,"EL2HLT":112,"EDEADLK":16,"ENOLCK":46,"EBADE":113,"EBADR":114,"EXFULL":115,"ENOANO":104,"EBADRQC":103,"EBADSLT":102,"EDEADLOCK":16,"EBFONT":101,"ENOSTR":100,"ENODATA":116,"ETIME":117,"ENOSR":118,"ENONET":119,"ENOPKG":120,"EREMOTE":121,"ENOLINK":47,"EADV":122,"ESRMNT":123,"ECOMM":124,"EPROTO":65,"EMULTIHOP":36,"EDOTDOT":125,"EBADMSG":9,"ENOTUNIQ":126,"EBADFD":127,"EREMCHG":128,"ELIBACC":129,"ELIBBAD":130,"ELIBSCN":131,"ELIBMAX":132,"ELIBEXEC":133,"ENOSYS":52,"ENOTEMPTY":55,"ENAMETOOLONG":37,"ELOOP":32,"EOPNOTSUPP":138,"EPFNOSUPPORT":139,"ECONNRESET":15,"ENOBUFS":42,"EAFNOSUPPORT":5,"EPROTOTYPE":67,"ENOTSOCK":57,"ENOPROTOOPT":50,"ESHUTDOWN":140,"ECONNREFUSED":14,"EADDRINUSE":3,"ECONNABORTED":13,"ENETUNREACH":40,"ENETDOWN":38,"ETIMEDOUT":73,"EHOSTDOWN":142,"EHOSTUNREACH":23,"EINPROGRESS":26,"EALREADY":7,"EDESTADDRREQ":17,"EMSGSIZE":35,"EPROTONOSUPPORT":66,"ESOCKTNOSUPPORT":137,"EADDRNOTAVAIL":4,"ENETRESET":39,"EISCONN":30,"ENOTCONN":53,"ETOOMANYREFS":141,"EUSERS":136,"EDQUOT":19,"ESTALE":72,"ENOTSUP":138,"ENOMEDIUM":148,"EILSEQ":25,"EOVERFLOW":61,"ECANCELED":11,"ENOTRECOVERABLE":56,"EOWNERDEAD":62,"ESTRPIPE":135};function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var asmLibraryArg={"a":___cxa_allocate_exception,"b":___cxa_throw,"e":___syscall_fcntl64,"t":___syscall_getcwd,"j":___syscall_ioctl,"k":___syscall_open,"o":___syscall_readlink,"p":___syscall_stat64,"c":_abort,"l":_emscripten_memcpy_big,"d":_emscripten_resize_heap,"r":_environ_get,"s":_environ_sizes_get,"f":_fd_close,"q":_fd_fdstat_get,"i":_fd_read,"m":_fd_seek,"h":_fd_write,"g":_getpwnam,"n":_strftime_l};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["v"]).apply(null,arguments)};var _main=Module["_main"]=function(){return(_main=Module["_main"]=Module["asm"]["w"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["x"]).apply(null,arguments)};var _itk_wasm_input_array_alloc=Module["_itk_wasm_input_array_alloc"]=function(){return(_itk_wasm_input_array_alloc=Module["_itk_wasm_input_array_alloc"]=Module["asm"]["z"]).apply(null,arguments)};var _itk_wasm_input_json_alloc=Module["_itk_wasm_input_json_alloc"]=function(){return(_itk_wasm_input_json_alloc=Module["_itk_wasm_input_json_alloc"]=Module["asm"]["A"]).apply(null,arguments)};var _itk_wasm_output_json_address=Module["_itk_wasm_output_json_address"]=function(){return(_itk_wasm_output_json_address=Module["_itk_wasm_output_json_address"]=Module["asm"]["B"]).apply(null,arguments)};var _itk_wasm_output_json_size=Module["_itk_wasm_output_json_size"]=function(){return(_itk_wasm_output_json_size=Module["_itk_wasm_output_json_size"]=Module["asm"]["C"]).apply(null,arguments)};var _itk_wasm_output_array_address=Module["_itk_wasm_output_array_address"]=function(){return(_itk_wasm_output_array_address=Module["_itk_wasm_output_array_address"]=Module["asm"]["D"]).apply(null,arguments)};var _itk_wasm_output_array_size=Module["_itk_wasm_output_array_size"]=function(){return(_itk_wasm_output_array_size=Module["_itk_wasm_output_array_size"]=Module["asm"]["E"]).apply(null,arguments)};var _itk_wasm_free_all=Module["_itk_wasm_free_all"]=function(){return(_itk_wasm_free_all=Module["_itk_wasm_free_all"]=Module["asm"]["F"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["G"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["H"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["I"]).apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return(stackAlloc=Module["stackAlloc"]=Module["asm"]["J"]).apply(null,arguments)};Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["writeArrayToMemory"]=writeArrayToMemory;Module["writeAsciiToMemory"]=writeAsciiToMemory;Module["addRunDependency"]=addRunDependency;Module["removeRunDependency"]=removeRunDependency;Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createDevice"]=FS.createDevice;Module["FS_unlink"]=FS.unlink;Module["callMain"]=callMain;Module["AsciiToString"]=AsciiToString;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}var calledMain=false;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function callMain(args){var entryFunction=Module["_main"];args=args||[];var argc=args.length+1;var argv=stackAlloc((argc+1)*4);HEAP32[argv>>2]=allocateUTF8OnStack(thisProgram);for(var i=1;i>2)+i]=allocateUTF8OnStack(args[i-1])}HEAP32[(argv>>2)+argc]=0;try{var ret=entryFunction(argc,argv);exit(ret,true);return ret}catch(e){return handleException(e)}finally{calledMain=true}}function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();if(shouldRunNow)callMain(args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;function exit(status,implicit){EXITSTATUS=status;if(keepRuntimeAlive()){}else{exitRuntime()}procExit(status)}function procExit(code){EXITSTATUS=code;if(!keepRuntimeAlive()){if(Module["onExit"])Module["onExit"](code);ABORT=true}quit_(code,new ExitStatus(code))}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=false;if(Module["noInitialRun"])shouldRunNow=false;run();Module.mountContainingDir=function(filePath){if(!ENVIRONMENT_IS_NODE){return}var path=require("path");var containingDir=path.dirname(filePath);if(FS.isDir(containingDir)||containingDir==="/"){return}var currentDir="/";var splitContainingDir=containingDir.split(path.sep);for(var ii=1;ii a * b) const elementSize = getSize(dtype) if (!elementSize) throw Error('Unknown dtype in .zarray metadata') const outputSize = nElements * elementSize const inputs = [ { type: InterfaceTypes.BinaryStream, data: { data: new Uint8Array(compressedChunk) }, }, ] const args = [ '0', '0', zarrayMetadata.compressor.cname, compressedChunk.byteLength.toString(), '--output-size', outputSize.toString(), '--decompress', '--memory-io', ] taskArgsArray.push(['BloscZarr', args, desiredOutputs, inputs]) } const results = await workerPool.runTasks(taskArgsArray).promise const decompressedChunks = [] for (let index = 0; index < results.length; index++) { // console.log(results[index].stdout) // console.error(results[index].stderr) decompressedChunks.push(results[index].outputs[0].data.data.buffer) } return decompressedChunks } export default bloscZarrDecompress ================================================ FILE: src/Context/ImageActorContext.js ================================================ export const defaultCompare = { method: 'green-magenta', // 'checkerboard', 'cyan-magenta' | 'blend' | 'disabled' imageMix: 0.5, // changes opaqueness of moving vs fixed image. Value of 1 means max opacity for moving image, 0 for fixed image. checkerboard: false, // mixes the 2 images with an alternating pattern. pattern: [4, 4, 4], // when checkerboard, number of "squares" across each dimension swapImageOrder: false, // when checkerboard, switches moving/fixed image in each square } class ImageActorContext { // MultiscaleSpatialImage to be visualized image = null // The successfully loaded scale loadedScale = null // Automatically adjust the rendered scale isFramerateScalePickingOn = true // MultiscaleSpatialImage label image to be visualized labelImage = null // MultiscaleSpatialImage label image to be visualized for use with // interactive, manual editing as opposed to stored or algorithmic results editorLabelImage = null // Whether the image components are dependent, e.g. RGB, are independent, in // which they are passed through separate color maps // An initial null value will be replaced by heuristics based on the image // component type and number of components. independentComponents = null // Enable interpolation on slicing planes interpolationEnabled = true // For multi-component images, the selected component index selectedComponent = 0 // Maximum number of intensity components that can be visualized maxIntensityComponents = 3 // The index of the last component whose visibility changed lastComponentVisibilityChanged = 0 // Whether a given image intensity component is visible componentVisibilities = [true] // Map of image intensity component to color map names colorMaps = new Map() // Map of image intensity component to array of [minValue, maxValue] for // mapping colors colorRanges = new Map() // Keep growing component ranges as new parts of the image are loaded // Map of image intensity component to Boolean colorRangeMinAutoAdjust = null colorRangeMaxAutoAdjust = null // Map of image intensity component to array of [minBound, maxBound] for // limiting the color range in the UI colorRangeBounds = new Map() // Keep growing component bounds as new parts of the image are loaded // Map of image intensity component to Boolean colorRangeBoundsAutoAdjust = null // Map of the image intensity component to an object representing the // piecewise function. This object has two properties: range and nodes. // The range property is a [min, max] array of intensity values. The nodes // property is a an array of { x, y, midpoint, sharpness } objects // characterizing a VTK piecewise function piecewiseFunctions = new Map() // Not used! // Map of the image intensity component to the array of // { position, width, xBias, yBias } gaussian parameters that define the // piecewise functions piecewiseFunctionGaussians = new Map() // Map of x,y point in 0 to 1 range. Scaled by colorRanges. piecewiseFunctionPoints = new Map() // Boolean map by component of whether the points should be auto adjusted // Set to false when the user manually adjusts the points piecewiseFunctionPointsAutoAdjust = null // Use gradient-based shadows in the volume rendering shadowEnabled = true // Gradient opacity weight in the volume rendering gradientOpacity = 0.5 // Gradient opacity scale in the volume rendering gradientOpacityScale = 0.5 // Distance in depth samples for the volume rendering volumeSampleDistance = 0.2 // Volume rendering blend mode blendMode = 'Composite' cinematicParameters = { isCinematicPossible: true, // computed to false if number of components is more than 1 scatteringBlend: 0.0, diffuse: 1.2, ambient: 0.6, } // Name of the labelImage layer labelImageName = null // Blend, 0.0 to 1.0 of the label image into the image labelImageBlend = 0.5 // Color lookup table name for the label image lookupTable = 'glasbey' // Rendering weights assigned to to labels, Map of label value to weight labelImageWeights = new Map() labelImageToggleWeight = 0.1 // String names for the label values labelNames = new Map() // Label index selected for changes in the UI, or special 'all' value that // identifies all non-background (index 0) labels selectedLabel = 'all' // Cached histogram by component for use by UI when switching selected component histograms = new Map() // Option config object for fusing 2 images, i.e. 'checkerboard'. See defaultCompare for object shape. compare = {} // mostly object allows initial update "diff" to detect changes and apply parameters // Override default left button mouse interactor to use window width/level interactor windowLevelEnabled = false } export default ImageActorContext ================================================ FILE: src/Context/ImagesMachineContext.js ================================================ class ImagesMachineContext { // Actors for rendering images and label images imageRenderingActors = new Map() // Context for the image actors actorContext = new Map() // Name of the selected image selectedName = null // Name of the image whose data needs to be updated updateRenderedName = null } export default ImagesMachineContext ================================================ FILE: src/Context/LayerActorContext.js ================================================ class LayerActorContext { // One of "image", "labelImage", "geometry", "pointSet", or "widget" type = 'image' // Boolean indicating whether the dataset is visible visible = true // img element icon for the layer or null icon = null // Boolean indicating whether the dataset bounding box is visible bbox = false } export default LayerActorContext ================================================ FILE: src/Context/LayersMachineContext.js ================================================ class LayersMachineContext { // Actors for providing an interface to the layers layerUIActors = new Map() // Context for the layer actors actorContext = new Map() // A { name, data } object, queued for creation of the data's actor lastAddedData = null } export default LayersMachineContext ================================================ FILE: src/Context/MainMachineContext.js ================================================ class MainMachineContext { constructor(config) { if (config) { if (typeof config.backgroundColor !== 'undefined') { this.backgroundColor = config.backgroundColor } if (typeof config.units !== 'undefined') { this.units = config.units } // Todo: more } } getConfig() { const config = { backgroundColor: [...this.backgroundColor], units: this.units, } return config } // Background color of the renderer backgroundColor = [0.5, 0.5, 0.5] // Background colors to step through when clicking the background color // button backgroundColors = [ [0.5, 0.5, 0.5], [0, 0, 0], [1, 1, 1], ] // Index of the selected background color from the backgroundColors selectedBackgroundColor = 0 // Is fullscreen mode enabled? fullscreenEnabled = false // Are annotations, e.g. pixel values, an orientation widget, displayed? annotationsEnabled = true // Continuously rotate the 3D rendering? rotateEnabled = false // Visualize the spatial axes on the viewable scene content axesEnabled = false // Spatial length units displayed in the scale bar units = '' // Cropping planes widget enabled croppingPlanesEnabled = false areCroppingPlanesTouched = false // Cropping planes. These typically define a box containing a region of // interest in space. The visualization is cropped outside of these planes. // Each is characterized with: { origin, normal }. // // origin: x,y,z point at a point in the plane // normal: 3-component vector defining the normal to the plane // // An example: An array of six planes. When the planes are axis aligned: // -x, +x, -y, +y, -z, +z croppingPlanes = null // In the single view layout, the an X plane, Y plane, Z plane, or volume // rendering. viewMode = 'Volume' // Current viewer frames per second fps = null // Slicing planes specification slicingPlanes = { x: { min: 0.0, max: 1.0, step: 0.1, scroll: false, scrollDirection: 1, visible: false, }, y: { min: 0.0, max: 1.0, step: 0.1, scroll: false, scrollDirection: 1, visible: false, }, z: { min: 0.0, max: 1.0, step: 0.1, scroll: false, scrollDirection: 1, visible: false, }, } // x slice value xSlice = null // y slice value ySlice = null // z slice value zSlice = null } export default MainMachineContext ================================================ FILE: src/Context/ViewerMachineContext.js ================================================ import MainMachineContext from './MainMachineContext' import LayersMachineContext from './LayersMachineContext' import ImagesMachineContext from './ImagesMachineContext' import WidgetsMachineContext from './WidgetsMachineContext' export const MAX_CONCURRENCY = 128 const defaultRenderingViewContainerStyle = { position: 'relative', width: '100%', height: 'auto', //height: '100%', minHeight: '200px', minWidth: '450px', margin: '0', padding: '0', top: '0', left: '0', flex: '1 1 0px', overflow: 'hidden', } class ViewerMachineContext { constructor(config) { this.id = `itk-vtk-viewer-${performance .now() .toString() .replace('.', '')}` if ( config && (typeof config.viewerConfigVersion === 'undefined' || parseInt(config.viewerConfigVersion.split('.')[0]) === parseInt(this.viewerConfigVersion.split('.')[0])) ) { if (typeof config.uiMachineOptions !== 'undefined') { this.uiMachineOptions = config.uiMachineOptions } if (typeof config.xyLowerLeft !== 'undefined') { this.xyLowerLeft = config.xyLowerLeft } if (typeof config.renderingViewContainerStyle !== 'undefined') { this.renderingViewContainerStyle = config.renderingViewContainerStyle } if (typeof config.uiCollapsed !== 'undefined') { this.uiCollapsed = config.uiCollapsed } if (typeof config.maxConcurrency !== 'undefined') { this.maxConcurrency = config.maxConcurrency } this.main = new MainMachineContext(config.main) } else { this.main = new MainMachineContext() } // Todo: add config serialization / deserializeation this.layers = new LayersMachineContext() this.images = new ImagesMachineContext() this.widgets = new WidgetsMachineContext() } getConfig() { let uiMachineOptions = 'reference' if (this.uiMachineOptions.href) { uiMachineOptions = this.uiMachineOptions } const config = { uiMachineOptions, viewerConfigVersion: this.viewerConfigVersion, xyLowerLeft: this.xyLowerLeft, renderingViewContainerStyle: { ...this.renderingViewContainerStyle }, uiCollapsed: this.uiCollapsed, maxConcurrency: this.maxConcurrency, main: this.main.getConfig(), } return config } // Contains the viewer container div and optionally the debugger rootContainer = null // Contains the viewer container = null // Version for compatibility check viewerConfigVersion = '0.3' // How to render the user interface, Either 'reference' or { href: 'https://url.to/uiMachineOptionsESM.js, export: 'default' }, or a JavaScript object with the ui machine options uiMachineOptions = 'reference' // Unique identifier used to identify a viewer in the DOM when multiple are // on a page id = 'itk-vtk-viewer' // A 2D viewer versus a 3D viewer use2D = false // When viewing the Z slice, the X-Y plane, is the origin in the lower left // or upper left? xyLowerLeft = false // Container's (html div's) containing rendering views renderingViewContainers = new Map() // Style of the container for the rendering views renderingViewContainerStyle = defaultRenderingViewContainerStyle // Is a "dark mode" enabled in the user interface? uiDarkMode = false // Has the user interface been collapsed, leaving on the interactive // rendering? uiCollapsed = false // Has the user set the number of available processors, or will we determine // that dynamically from the client? maxConcurrency = MAX_CONCURRENCY // Main machine context main = null // Widgets machine context widgets = null // Layers machine context layers = null // Image machine context images = null } export default ViewerMachineContext ================================================ FILE: src/Context/WidgetsMachineContext.js ================================================ class WidgetsMachineContext { // Whether the distance widget is enabled distanceEnabled = false // Distance/length measured by the distance widget distanceValue = 0.0 } export default WidgetsMachineContext ================================================ FILE: src/IO/Analyze/ComputeRanges.worker.js ================================================ import registerWebworker from 'webworker-promise/lib/register' import { createRangeHelper } from './createRangeHelper' const computeRangesInSplit = ({ split, numberOfSplits, values, numberOfComponents, }) => { const helpers = [...Array(numberOfComponents)].map(createRangeHelper) const start = Math.floor(values.length / numberOfSplits) * split const end = split === numberOfSplits - 1 ? values.length : Math.floor(values.length / numberOfSplits) * (split + 1) for (let i = start; i < end; i++) { helpers[i % numberOfComponents].add(values[i]) } return helpers.map(h => h.getRange()) } registerWebworker().operation('computeRanges', computeRangesInSplit) ================================================ FILE: src/IO/Analyze/UpdateHistogram.worker.js ================================================ import registerWebworker from 'webworker-promise/lib/register' registerWebworker().operation( 'updateHistogram', ({ values, min, max, numberOfBins, component = 0, numberOfComponents = 1, }) => { const offset = component const step = numberOfComponents const delta = max - min const histogram = new Float32Array(numberOfBins) histogram.fill(0) const len = values.length for (let i = offset; i < len; i += step) { const idx = Math.floor( ((numberOfBins - 1) * (Number(values[i]) - min)) / delta ) histogram[idx] += 1 } return Promise.resolve( new registerWebworker.TransferableResponse(histogram, [histogram.buffer]) ) } ) ================================================ FILE: src/IO/Analyze/computeHistograms.js ================================================ const haveSharedArrayBuffer = typeof window.SharedArrayBuffer === 'function' import webWorkerPromiseWorkerPool from './webWorkerPromiseWorkerPool' import UpdateHistogramWorker from './UpdateHistogram.worker' const numberOfWorkers = navigator.hardwareConcurrency ? Math.min(navigator.hardwareConcurrency, 6) : 4 const updateHistogramWorkerPool = webWorkerPromiseWorkerPool( numberOfWorkers, () => new UpdateHistogramWorker(), 'updateHistogram' ) const BIN_COUNT_DEFAULT = 256 export const computeHistogram = async ( values, component, numberOfComponents, [min, max] ) => { const numberOfSplits = numberOfWorkers const isFloatValues = values instanceof Float32Array || values instanceof Float64Array const numberOfBins = isFloatValues ? BIN_COUNT_DEFAULT : // only need a bin for each possible integer value Math.min(max - min + 1, BIN_COUNT_DEFAULT) const taskArgs = new Array(numberOfSplits) if (haveSharedArrayBuffer && values.buffer instanceof SharedArrayBuffer) { for (let split = 0; split < numberOfSplits; split++) { taskArgs[split] = [ { values, min, max, numberOfBins, component, numberOfComponents, }, ] } } else { let arrayStride = Math.floor(values.length / numberOfSplits) || 1 arrayStride += arrayStride % numberOfComponents let arrayIndex = 0 for (let split = 0; split < numberOfSplits; split++) { const arrayStart = arrayIndex const arrayEnd = Math.min(arrayIndex + arrayStride, values.length - 1) const subArray = values.slice(arrayStart, arrayEnd + 1) taskArgs[split] = [ { values: subArray, min, max, numberOfBins, component, numberOfComponents, }, [subArray.buffer], ] arrayIndex += arrayStride } } const histograms = await updateHistogramWorkerPool.runTasks(taskArgs).promise const histogram = new Float32Array(numberOfBins) histogram.fill(0.0) for (let ii = 0; ii < histograms.length; ii++) { for (let jj = 0; jj < numberOfBins; jj++) { histogram[jj] += histograms[ii].result[jj] } } let maxHistogram = 0.0 for (let ii = 0; ii < numberOfBins; ii++) { maxHistogram = Math.max(histogram[ii], maxHistogram) } for (let ii = 0; ii < numberOfBins; ii++) { histogram[ii] /= maxHistogram } return histogram } ================================================ FILE: src/IO/Analyze/computeRanges.js ================================================ import webWorkerPromiseWorkerPool from './webWorkerPromiseWorkerPool' import { createRangeHelper } from './createRangeHelper' import ComputeRangesWorker from './ComputeRanges.worker' const haveSharedArrayBuffer = typeof globalThis.SharedArrayBuffer === 'function' const numberOfWorkers = navigator.hardwareConcurrency ? Math.min(navigator.hardwareConcurrency, 8) : 4 const computeRangeWorkerPool = webWorkerPromiseWorkerPool( numberOfWorkers, () => new ComputeRangesWorker(), 'computeRanges' ) export async function computeRanges(values, numberOfComponents = 1) { const numberOfSplits = numberOfWorkers const taskArgs = new Array(numberOfSplits) if (haveSharedArrayBuffer && values.buffer instanceof SharedArrayBuffer) { for (let split = 0; split < numberOfSplits; split++) { taskArgs[split] = [ { split, numberOfSplits, values, numberOfComponents, }, ] } } else { let arrayStride = Math.floor(values.length / numberOfSplits) || 1 arrayStride += arrayStride % numberOfComponents let arrayIndex = 0 for (let split = 0; split < numberOfSplits; split++) { const arrayStart = arrayIndex const arrayEnd = Math.min(arrayIndex + arrayStride, values.length - 1) const subArray = values.slice(arrayStart, arrayEnd + 1) taskArgs[split] = [ { split: 0, // 0 because array already split numberOfSplits: 1, values: subArray, numberOfComponents, }, [subArray.buffer], ] arrayIndex += arrayStride } } const rangesBySplit = await computeRangeWorkerPool.runTasks(taskArgs).promise const helpers = [...Array(numberOfComponents)].map(createRangeHelper) rangesBySplit.forEach(({ result: ranges }) => { ranges.forEach(({ min, max }, compIdx) => { helpers[compIdx].add(min) helpers[compIdx].add(max) }) }) return helpers.map(h => h.getRange()) } ================================================ FILE: src/IO/Analyze/createRangeHelper.js ================================================ export function createRangeHelper() { let min = Number.POSITIVE_INFINITY let max = Number.NEGATIVE_INFINITY return { add(value) { if (min > value) { min = value } if (max < value) { max = value } }, getRange() { return { min, max } }, } } ================================================ FILE: src/IO/Analyze/webWorkerPromiseWorkerPool.js ================================================ import { WorkerPool } from 'itk-wasm' import WebworkerPromise from 'webworker-promise' function webWorkerPromiseWorkerPool( numberOfWorkers, makeWorker, operationName ) { const createWorker = existingWorker => { const worker = existingWorker ?? makeWorker() const webWorkerPromise = new WebworkerPromise(worker) return { webWorkerPromise, worker } } const compute = async (webWorker, ...args) => { const { webWorkerPromise, worker } = createWorker(webWorker) const result = await webWorkerPromise.exec(operationName, ...args) return { result, webWorker: worker } } const workerPool = new WorkerPool(numberOfWorkers, compute) return workerPool } export default webWorkerPromiseWorkerPool ================================================ FILE: src/IO/Compare/.gitignore ================================================ /emscripten-build/* !emscripten-build/Compare* output.png ================================================ FILE: src/IO/Compare/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) project(Compare) set(CMAKE_CXX_STANDARD 17) set(io_components) if (NOT EMSCRIPTEN AND NOT WASI) set(io_components ITKIOPNG ITKIOMeta ITKIONRRD ) endif() find_package(ITK REQUIRED COMPONENTS ${io_components} WebAssemblyInterface ITKImageGrid ITKImageFunction GenericLabelInterpolator ITKImageCompare ) include(${ITK_USE_FILE}) add_executable(Compare Compare.cxx) target_link_libraries(Compare PUBLIC ${ITK_LIBRARIES}) ================================================ FILE: src/IO/Compare/Compare.cxx ================================================ /*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include #include "itkVectorImage.h" #include "itkResampleImageFilter.h" #include "itkLinearInterpolateImageFunction.h" #include "itkImageRegionSplitterSlowDimension.h" #include "itkExtractImageFilter.h" #include "itkRGBPixel.h" #include "itkRGBAPixel.h" #include "itkVectorImage.h" #include "itkVector.h" #include "itkPoint.h" #include "itkCovariantVector.h" #include "itkFixedArray.h" #include "itkArray.h" #include "itkVariableLengthVector.h" #include "itkPipeline.h" #include "itkInputImage.h" #include "itkOutputImage.h" #include "itkOutputTextStream.h" #include "itkSupportInputImageTypes.h" #include "itkCheckerBoardImageFilter.h" #include "itkIntensityWindowingImageFilter.h" #include "itkCastImageFilter.h" #include "itkVectorMagnitudeImageFilter.h" #include "itkRGBToLuminanceImageFilter.h" #include "itkComposeImageFilter.h" template int Compare(itk::wasm::Pipeline &pipeline, const TMovingImage *movingImage, const TFixedImage *fixedImage) { using ImageType = TMovingImage; using FixedImageType = TFixedImage; pipeline.get_option("input-image")->required()->type_name("INPUT_IMAGE"); pipeline.get_option("fixed-image")->required()->type_name("INPUT_IMAGE"); bool checkerboard; pipeline.add_option("-c,--checkerboard", checkerboard, "Do checkerboard filter"); std::vector range; pipeline.add_option("-r,--range", range, "Min and max intensity values of output image")->expected(2)->delimiter(','); std::vector pattern; pipeline.add_option("-p,--pattern", pattern, "Number of boxes for each dimension")->expected(2, 3)->delimiter(','); // split args unsigned int maxTotalSplits = 1; pipeline.add_option("-m,--max-total-splits", maxTotalSplits, "Maximum total splits when processed in parallel"); unsigned int split = 0; pipeline.add_option("-s,--split", split, "Current processed split"); itk::wasm::OutputTextStream numberOfSplitsStream; auto numberOfSplitsStreamOption = pipeline.add_option("--number-of-splits", numberOfSplitsStream, "Number of splits"); ITK_WASM_PRE_PARSE(pipeline); // Resample moving to fixed image using ResampleFilterType = itk::ResampleImageFilter; auto resampleFilter = ResampleFilterType::New(); resampleFilter->ReleaseDataFlagOn(); resampleFilter->SetInput(movingImage); resampleFilter->SetReferenceImage(fixedImage); resampleFilter->UseReferenceImageOn(); // rescale intensity and cast PixelType of moving to fixed using RescaleFilterType = itk::IntensityWindowingImageFilter; auto rescaleFilter = RescaleFilterType::New(); rescaleFilter->ReleaseDataFlagOn(); rescaleFilter->SetInput(resampleFilter->GetOutput()); rescaleFilter->SetWindowMinimum(range[0]); rescaleFilter->SetWindowMaximum(range[1]); rescaleFilter->SetOutputMinimum(range[0]); rescaleFilter->SetOutputMaximum(range[1]); using RescaleFilterTypeFixed = itk::IntensityWindowingImageFilter; auto rescaleFilterFixed = RescaleFilterTypeFixed::New(); rescaleFilterFixed->ReleaseDataFlagOn(); rescaleFilterFixed->SetInput(fixedImage); rescaleFilterFixed->SetWindowMinimum(range[0]); rescaleFilterFixed->SetWindowMaximum(range[1]); rescaleFilterFixed->SetOutputMinimum(range[0]); rescaleFilterFixed->SetOutputMaximum(range[1]); using PipelineOutputType = itk::Image, FixedImageType::ImageDimension>; using FilterType = itk::ComposeImageFilter; auto compose = FilterType::New(); compose->ReleaseDataFlagOn(); if (checkerboard) { // Checkerboard images using FilterType = itk::CheckerBoardImageFilter; auto check0 = FilterType::New(); auto check1 = FilterType::New(); check0->ReleaseDataFlagOn(); check1->ReleaseDataFlagOn(); check0->SetInput1(rescaleFilterFixed->GetOutput()); check0->SetInput2(rescaleFilter->GetOutput()); check1->SetInput1(rescaleFilter->GetOutput()); check1->SetInput2(rescaleFilterFixed->GetOutput()); const int dims = pattern.size(); if (dims > 0) { typename FilterType::PatternArrayType checkerPattern; for (int i = 0; i < dims; ++i) { checkerPattern[i] = pattern[i]; } check0->SetCheckerPattern(checkerPattern); check1->SetCheckerPattern(checkerPattern); } // Without UpdateLargestPossibleRegion there is a runtime error check0->UpdateLargestPossibleRegion(); check1->UpdateLargestPossibleRegion(); compose->SetInput(0, check0->GetOutput()); compose->SetInput(1, check1->GetOutput()); } else { // blend green-magenta, cyan-red, or cyan-magenta method compose->SetInput(0, rescaleFilterFixed->GetOutput()); compose->SetInput(1, rescaleFilter->GetOutput()); } using OutputImageType = itk::wasm::OutputImage; OutputImageType outputImage; pipeline.add_option("output-image", outputImage, "Output image")->required()->type_name("OUTPUT_IMAGE"); ITK_WASM_PARSE(pipeline); // Split handling using ROIFilterType = itk::ExtractImageFilter; compose->UpdateOutputInformation(); using RegionType = typename PipelineOutputType::RegionType; const RegionType largestRegion(compose->GetOutput()->GetLargestPossibleRegion()); using SplitterType = itk::ImageRegionSplitterSlowDimension; auto splitter = SplitterType::New(); const unsigned int numberOfSplits = splitter->GetNumberOfSplits(largestRegion, maxTotalSplits); if (split >= numberOfSplits) { std::cerr << "Error: requested split: " << split << " is outside the number of splits: " << numberOfSplits << std::endl; return EXIT_FAILURE; } if (!numberOfSplitsStreamOption->empty()) { numberOfSplitsStream.Get() << numberOfSplits; } RegionType requestedRegion(largestRegion); splitter->GetSplit(split, numberOfSplits, requestedRegion); auto roiFilter = ROIFilterType::New(); roiFilter->SetExtractionRegion(requestedRegion); roiFilter->SetInput(compose->GetOutput()); try { roiFilter->Update(); } catch (std::exception &error) { std::cerr << "Error: " << error.what() << std::endl; return EXIT_FAILURE; } outputImage.Set(roiFilter->GetOutput()); return EXIT_SUCCESS; } using MagnitudePixelType = float; template using OneComponentImage = typename itk::Image::ConstPointer; template using IsRGB = std::disjunction< std::is_same>, std::is_same>>; template using IsOneComponent = std::is_same::ValueType>; // Vector PixelType, use magnitude filter template typename std::enable_if::value && !IsOneComponent::value, OneComponentImage>::type EnsureOneComponent(const TImage *image) { using MagnitudeImageType = itk::Image; using VectorMagnitudeFilterType = itk::VectorMagnitudeImageFilter; auto magnitudeFilter = VectorMagnitudeFilterType::New(); magnitudeFilter->SetInput(image); magnitudeFilter->Update(); return magnitudeFilter->GetOutput(); } // RGB PixelType, use luminance filter template typename std::enable_if::value && !IsOneComponent::value, OneComponentImage>::type EnsureOneComponent(const TImage *image) { using MagnitudeImageType = itk::Image; using LuminanceFilter = itk::RGBToLuminanceImageFilter; auto filter = LuminanceFilter::New(); filter->SetInput(image); filter->Update(); return filter->GetOutput(); } // Scalar type, passthrough image template typename std::enable_if::value, typename TImage::ConstPointer>::type EnsureOneComponent(const TImage *image) { return typename TImage::ConstPointer(image); } template class InputImagePipelineFunctor { public: int operator()(itk::wasm::Pipeline &pipeline) { using ImageType = TImage; using InputImageType = itk::wasm::InputImage; InputImageType inputImage; pipeline.add_option("input-image", inputImage, "Input image"); ITK_WASM_PRE_PARSE(pipeline); typename TImage::ConstPointer image = inputImage.Get(); parsedImage = image; return itk::wasm::SupportInputImageTypes, itk::RGBAPixel, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::CovariantVector, itk::CovariantVector, itk::CovariantVector, itk::CovariantVector>::template Dimensions("fixed-image", pipeline); } private: template class FixedImagePipelineFunctor { public: int operator()(itk::wasm::Pipeline &pipeline) { using FixedImageType = itk::wasm::InputImage; FixedImageType fixedImage; pipeline.add_option("fixed-image", fixedImage, "Fixed image"); ITK_WASM_PRE_PARSE(pipeline); const TFixedImage *fixed = fixedImage.Get(); auto movingScalarImage = EnsureOneComponent(parsedImage); auto fixedScalarImage = EnsureOneComponent(fixed); // The images as SmartPointers may be important to avoid silent failure of Compare() (due to cleanup of image memory?) return Compare(pipeline, movingScalarImage.GetPointer(), fixedScalarImage.GetPointer()); } }; static inline const TImage *parsedImage; }; int main(int argc, char *argv[]) { itk::wasm::Pipeline pipeline("compare", "Combine two images by method", argc, argv); return itk::wasm::SupportInputImageTypes, itk::RGBAPixel, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::CovariantVector, itk::CovariantVector, itk::CovariantVector, itk::CovariantVector>::Dimensions<2U, 3U>("input-image", pipeline); } ================================================ FILE: src/IO/Compare/createCompareImage.js ================================================ import { runWasm } from '../itkWasmUtils.js' export async function createCompareImage( movingImage, fixedImage, { minMax, checkerboard, pattern = undefined } ) { const clampedPattern = pattern ? fixedImage.size.map((s, idx) => Math.min(s, pattern[idx])) : [] const args = [ '--checkerboard', checkerboard.toString(), '--range', minMax.join(','), '--pattern', clampedPattern.join(','), ] const image = await runWasm({ pipeline: 'Compare', args, images: [movingImage, fixedImage], maxSplits: 1, }) image.ranges = [minMax, minMax] return image } ================================================ FILE: src/IO/Compare/emscripten-build/Compare.js ================================================ var Compare = (() => { var _scriptDir = import.meta.url return async function(Compare) { Compare = Compare || {} var Module = typeof Compare != 'undefined' ? Compare : {} var readyPromiseResolve, readyPromiseReject Module['ready'] = new Promise(function(resolve, reject) { readyPromiseResolve = resolve readyPromiseReject = reject }) var mStdout = null var mStderr = null Module['resetModuleStdout'] = function() { mStdout = '' } Module['resetModuleStderr'] = function() { mStderr = '' } Module['print'] = function(text) { console.log(text) mStdout += text + '\n' } Module['printErr'] = function(text) { console.error(text) mStderr += text + '\n' } Module['getModuleStdout'] = function() { return mStdout } Module['getModuleStderr'] = function() { return mStderr } var moduleOverrides = Object.assign({}, Module) var arguments_ = [] var thisProgram = './this.program' var quit_ = (status, toThrow) => { throw toThrow } var ENVIRONMENT_IS_WEB = typeof window == 'object' var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function' var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string' var scriptDirectory = '' function locateFile(path) { if (Module['locateFile']) { return Module['locateFile'](path, scriptDirectory) } return scriptDirectory + path } var read_, readAsync, readBinary, setWindowTitle function logExceptionOnExit(e) { if (e instanceof ExitStatus) return let toLog = e err('exiting due to exception: ' + toLog) } if (ENVIRONMENT_IS_NODE) { const { createRequire: createRequire } = await import('module') var require = createRequire(import.meta.url) var fs = require('fs') var nodePath = require('path') if (ENVIRONMENT_IS_WORKER) { scriptDirectory = nodePath.dirname(scriptDirectory) + '/' } else { scriptDirectory = require('url').fileURLToPath( new URL('./', import.meta.url) ) } read_ = (filename, binary) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) return fs.readFileSync(filename, binary ? undefined : 'utf8') } readBinary = filename => { var ret = read_(filename, true) if (!ret.buffer) { ret = new Uint8Array(ret) } return ret } readAsync = (filename, onload, onerror) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) fs.readFile(filename, function(err, data) { if (err) onerror(err) else onload(data.buffer) }) } if (process['argv'].length > 1) { thisProgram = process['argv'][1].replace(/\\/g, '/') } arguments_ = process['argv'].slice(2) process['on']('uncaughtException', function(ex) { if (!(ex instanceof ExitStatus)) { throw ex } }) process['on']('unhandledRejection', function(reason) { throw reason }) quit_ = (status, toThrow) => { if (keepRuntimeAlive()) { process['exitCode'] = status throw toThrow } logExceptionOnExit(toThrow) process['exit'](status) } Module['inspect'] = function() { return '[Emscripten Module object]' } } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = self.location.href } else if (typeof document != 'undefined' && document.currentScript) { scriptDirectory = document.currentScript.src } if (_scriptDir) { scriptDirectory = _scriptDir } if (scriptDirectory.indexOf('blob:') !== 0) { scriptDirectory = scriptDirectory.substr( 0, scriptDirectory.replace(/[?#].*/, '').lastIndexOf('/') + 1 ) } else { scriptDirectory = '' } { read_ = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.send(null) return xhr.responseText } if (ENVIRONMENT_IS_WORKER) { readBinary = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.responseType = 'arraybuffer' xhr.send(null) return new Uint8Array(xhr.response) } } readAsync = (url, onload, onerror) => { var xhr = new XMLHttpRequest() xhr.open('GET', url, true) xhr.responseType = 'arraybuffer' xhr.onload = () => { if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { onload(xhr.response) return } onerror() } xhr.onerror = onerror xhr.send(null) } } setWindowTitle = title => (document.title = title) } else { } var out = Module['print'] || console.log.bind(console) var err = Module['printErr'] || console.warn.bind(console) Object.assign(Module, moduleOverrides) moduleOverrides = null if (Module['arguments']) arguments_ = Module['arguments'] if (Module['thisProgram']) thisProgram = Module['thisProgram'] if (Module['quit']) quit_ = Module['quit'] var wasmBinary if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'] var noExitRuntime = Module['noExitRuntime'] || true if (typeof WebAssembly != 'object') { abort('no native wasm support detected') } var wasmMemory var ABORT = false var EXITSTATUS function assert(condition, text) { if (!condition) { abort(text) } } var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { var endIdx = idx + maxBytesToRead var endPtr = idx while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)) } var str = '' while (idx < endPtr) { var u0 = heapOrArray[idx++] if (!(u0 & 128)) { str += String.fromCharCode(u0) continue } var u1 = heapOrArray[idx++] & 63 if ((u0 & 224) == 192) { str += String.fromCharCode(((u0 & 31) << 6) | u1) continue } var u2 = heapOrArray[idx++] & 63 if ((u0 & 240) == 224) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2 } else { u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63) } if (u0 < 65536) { str += String.fromCharCode(u0) } else { var ch = u0 - 65536 str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)) } } return str } function UTF8ToString(ptr, maxBytesToRead) { return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '' } function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { if (!(maxBytesToWrite > 0)) return 0 var startIdx = outIdx var endIdx = outIdx + maxBytesToWrite - 1 for (var i = 0; i < str.length; ++i) { var u = str.charCodeAt(i) if (u >= 55296 && u <= 57343) { var u1 = str.charCodeAt(++i) u = (65536 + ((u & 1023) << 10)) | (u1 & 1023) } if (u <= 127) { if (outIdx >= endIdx) break heap[outIdx++] = u } else if (u <= 2047) { if (outIdx + 1 >= endIdx) break heap[outIdx++] = 192 | (u >> 6) heap[outIdx++] = 128 | (u & 63) } else if (u <= 65535) { if (outIdx + 2 >= endIdx) break heap[outIdx++] = 224 | (u >> 12) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } else { if (outIdx + 3 >= endIdx) break heap[outIdx++] = 240 | (u >> 18) heap[outIdx++] = 128 | ((u >> 12) & 63) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } } heap[outIdx] = 0 return outIdx - startIdx } function stringToUTF8(str, outPtr, maxBytesToWrite) { return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite) } function lengthBytesUTF8(str) { var len = 0 for (var i = 0; i < str.length; ++i) { var c = str.charCodeAt(i) if (c <= 127) { len++ } else if (c <= 2047) { len += 2 } else if (c >= 55296 && c <= 57343) { len += 4 ++i } else { len += 3 } } return len } var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64 function updateGlobalBufferAndViews(buf) { buffer = buf Module['HEAP8'] = HEAP8 = new Int8Array(buf) Module['HEAP16'] = HEAP16 = new Int16Array(buf) Module['HEAP32'] = HEAP32 = new Int32Array(buf) Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf) Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf) Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf) Module['HEAPF32'] = HEAPF32 = new Float32Array(buf) Module['HEAPF64'] = HEAPF64 = new Float64Array(buf) } var INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 16777216 var wasmTable var __ATPRERUN__ = [] var __ATINIT__ = [] var __ATMAIN__ = [] var __ATPOSTRUN__ = [] var runtimeInitialized = false function keepRuntimeAlive() { return noExitRuntime } function preRun() { if (Module['preRun']) { if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']] while (Module['preRun'].length) { addOnPreRun(Module['preRun'].shift()) } } callRuntimeCallbacks(__ATPRERUN__) } function initRuntime() { runtimeInitialized = true if (!Module['noFSInit'] && !FS.init.initialized) FS.init() FS.ignorePermissions = false TTY.init() callRuntimeCallbacks(__ATINIT__) } function preMain() { callRuntimeCallbacks(__ATMAIN__) } function postRun() { if (Module['postRun']) { if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']] while (Module['postRun'].length) { addOnPostRun(Module['postRun'].shift()) } } callRuntimeCallbacks(__ATPOSTRUN__) } function addOnPreRun(cb) { __ATPRERUN__.unshift(cb) } function addOnInit(cb) { __ATINIT__.unshift(cb) } function addOnPostRun(cb) { __ATPOSTRUN__.unshift(cb) } var runDependencies = 0 var runDependencyWatcher = null var dependenciesFulfilled = null function getUniqueRunDependency(id) { return id } function addRunDependency(id) { runDependencies++ if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } } function removeRunDependency(id) { runDependencies-- if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } if (runDependencies == 0) { if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher) runDependencyWatcher = null } if (dependenciesFulfilled) { var callback = dependenciesFulfilled dependenciesFulfilled = null callback() } } } function abort(what) { if (Module['onAbort']) { Module['onAbort'](what) } what = 'Aborted(' + what + ')' err(what) ABORT = true EXITSTATUS = 1 what += '. Build with -sASSERTIONS for more info.' var e = new WebAssembly.RuntimeError(what) readyPromiseReject(e) throw e } var dataURIPrefix = 'data:application/octet-stream;base64,' function isDataURI(filename) { return filename.startsWith(dataURIPrefix) } function isFileURI(filename) { return filename.startsWith('file://') } var wasmBinaryFile if (Module['locateFile']) { wasmBinaryFile = 'Compare.wasm' if (!isDataURI(wasmBinaryFile)) { wasmBinaryFile = locateFile(wasmBinaryFile) } } else { wasmBinaryFile = new URL('Compare.wasm', import.meta.url).href } function getBinary(file) { try { if (file == wasmBinaryFile && wasmBinary) { return new Uint8Array(wasmBinary) } if (readBinary) { return readBinary(file) } throw 'both async and sync fetching of the wasm failed' } catch (err) { abort(err) } } function getBinaryPromise() { if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { if (typeof fetch == 'function' && !isFileURI(wasmBinaryFile)) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }) .then(function(response) { if (!response['ok']) { throw "failed to load wasm binary file at '" + wasmBinaryFile + "'" } return response['arrayBuffer']() }) .catch(function() { return getBinary(wasmBinaryFile) }) } else { if (readAsync) { return new Promise(function(resolve, reject) { readAsync( wasmBinaryFile, function(response) { resolve(new Uint8Array(response)) }, reject ) }) } } } return Promise.resolve().then(function() { return getBinary(wasmBinaryFile) }) } function createWasm() { var info = { a: asmLibraryArg } function receiveInstance(instance, module) { var exports = instance.exports Module['asm'] = exports wasmMemory = Module['asm']['s'] updateGlobalBufferAndViews(wasmMemory.buffer) wasmTable = Module['asm']['D'] addOnInit(Module['asm']['t']) removeRunDependency('wasm-instantiate') } addRunDependency('wasm-instantiate') function receiveInstantiationResult(result) { receiveInstance(result['instance']) } function instantiateArrayBuffer(receiver) { return getBinaryPromise() .then(function(binary) { return WebAssembly.instantiate(binary, info) }) .then(function(instance) { return instance }) .then(receiver, function(reason) { err('failed to asynchronously prepare wasm: ' + reason) abort(reason) }) } function instantiateAsync() { if ( !wasmBinary && typeof WebAssembly.instantiateStreaming == 'function' && !isDataURI(wasmBinaryFile) && !isFileURI(wasmBinaryFile) && !ENVIRONMENT_IS_NODE && typeof fetch == 'function' ) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then( function(response) { var result = WebAssembly.instantiateStreaming(response, info) return result.then(receiveInstantiationResult, function(reason) { err('wasm streaming compile failed: ' + reason) err('falling back to ArrayBuffer instantiation') return instantiateArrayBuffer(receiveInstantiationResult) }) } ) } else { return instantiateArrayBuffer(receiveInstantiationResult) } } if (Module['instantiateWasm']) { try { var exports = Module['instantiateWasm'](info, receiveInstance) return exports } catch (e) { err('Module.instantiateWasm callback failed with error: ' + e) readyPromiseReject(e) } } instantiateAsync().catch(readyPromiseReject) return {} } var tempDouble var tempI64 function ExitStatus(status) { this.name = 'ExitStatus' this.message = 'Program terminated with exit(' + status + ')' this.status = status } function callRuntimeCallbacks(callbacks) { while (callbacks.length > 0) { callbacks.shift()(Module) } } function ExceptionInfo(excPtr) { this.excPtr = excPtr this.ptr = excPtr - 24 this.set_type = function(type) { HEAPU32[(this.ptr + 4) >> 2] = type } this.get_type = function() { return HEAPU32[(this.ptr + 4) >> 2] } this.set_destructor = function(destructor) { HEAPU32[(this.ptr + 8) >> 2] = destructor } this.get_destructor = function() { return HEAPU32[(this.ptr + 8) >> 2] } this.set_refcount = function(refcount) { HEAP32[this.ptr >> 2] = refcount } this.set_caught = function(caught) { caught = caught ? 1 : 0 HEAP8[(this.ptr + 12) >> 0] = caught } this.get_caught = function() { return HEAP8[(this.ptr + 12) >> 0] != 0 } this.set_rethrown = function(rethrown) { rethrown = rethrown ? 1 : 0 HEAP8[(this.ptr + 13) >> 0] = rethrown } this.get_rethrown = function() { return HEAP8[(this.ptr + 13) >> 0] != 0 } this.init = function(type, destructor) { this.set_adjusted_ptr(0) this.set_type(type) this.set_destructor(destructor) this.set_refcount(0) this.set_caught(false) this.set_rethrown(false) } this.add_ref = function() { var value = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = value + 1 } this.release_ref = function() { var prev = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = prev - 1 return prev === 1 } this.set_adjusted_ptr = function(adjustedPtr) { HEAPU32[(this.ptr + 16) >> 2] = adjustedPtr } this.get_adjusted_ptr = function() { return HEAPU32[(this.ptr + 16) >> 2] } this.get_exception_ptr = function() { var isPointer = ___cxa_is_pointer_type(this.get_type()) if (isPointer) { return HEAPU32[this.excPtr >> 2] } var adjusted = this.get_adjusted_ptr() if (adjusted !== 0) return adjusted return this.excPtr } } var exceptionLast = 0 var uncaughtExceptionCount = 0 function ___cxa_throw(ptr, type, destructor) { var info = new ExceptionInfo(ptr) info.init(type, destructor) exceptionLast = ptr uncaughtExceptionCount++ throw ptr } function setErrNo(value) { HEAP32[___errno_location() >> 2] = value return value } var PATH = { isAbs: path => path.charAt(0) === '/', splitPath: filename => { var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/ return splitPathRe.exec(filename).slice(1) }, normalizeArray: (parts, allowAboveRoot) => { var up = 0 for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i] if (last === '.') { parts.splice(i, 1) } else if (last === '..') { parts.splice(i, 1) up++ } else if (up) { parts.splice(i, 1) up-- } } if (allowAboveRoot) { for (; up; up--) { parts.unshift('..') } } return parts }, normalize: path => { var isAbsolute = PATH.isAbs(path), trailingSlash = path.substr(-1) === '/' path = PATH.normalizeArray( path.split('/').filter(p => !!p), !isAbsolute ).join('/') if (!path && !isAbsolute) { path = '.' } if (path && trailingSlash) { path += '/' } return (isAbsolute ? '/' : '') + path }, dirname: path => { var result = PATH.splitPath(path), root = result[0], dir = result[1] if (!root && !dir) { return '.' } if (dir) { dir = dir.substr(0, dir.length - 1) } return root + dir }, basename: path => { if (path === '/') return '/' path = PATH.normalize(path) path = path.replace(/\/$/, '') var lastSlash = path.lastIndexOf('/') if (lastSlash === -1) return path return path.substr(lastSlash + 1) }, join: function() { var paths = Array.prototype.slice.call(arguments) return PATH.normalize(paths.join('/')) }, join2: (l, r) => { return PATH.normalize(l + '/' + r) }, } function getRandomDevice() { if ( typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function' ) { var randomBuffer = new Uint8Array(1) return () => { crypto.getRandomValues(randomBuffer) return randomBuffer[0] } } else if (ENVIRONMENT_IS_NODE) { try { var crypto_module = require('crypto') return () => crypto_module['randomBytes'](1)[0] } catch (e) {} } return () => abort('randomDevice') } var PATH_FS = { resolve: function() { var resolvedPath = '', resolvedAbsolute = false for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = i >= 0 ? arguments[i] : FS.cwd() if (typeof path != 'string') { throw new TypeError('Arguments to path.resolve must be strings') } else if (!path) { return '' } resolvedPath = path + '/' + resolvedPath resolvedAbsolute = PATH.isAbs(path) } resolvedPath = PATH.normalizeArray( resolvedPath.split('/').filter(p => !!p), !resolvedAbsolute ).join('/') return (resolvedAbsolute ? '/' : '') + resolvedPath || '.' }, relative: (from, to) => { from = PATH_FS.resolve(from).substr(1) to = PATH_FS.resolve(to).substr(1) function trim(arr) { var start = 0 for (; start < arr.length; start++) { if (arr[start] !== '') break } var end = arr.length - 1 for (; end >= 0; end--) { if (arr[end] !== '') break } if (start > end) return [] return arr.slice(start, end - start + 1) } var fromParts = trim(from.split('/')) var toParts = trim(to.split('/')) var length = Math.min(fromParts.length, toParts.length) var samePartsLength = length for (var i = 0; i < length; i++) { if (fromParts[i] !== toParts[i]) { samePartsLength = i break } } var outputParts = [] for (var i = samePartsLength; i < fromParts.length; i++) { outputParts.push('..') } outputParts = outputParts.concat(toParts.slice(samePartsLength)) return outputParts.join('/') }, } function intArrayFromString(stringy, dontAddNull, length) { var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1 var u8array = new Array(len) var numBytesWritten = stringToUTF8Array( stringy, u8array, 0, u8array.length ) if (dontAddNull) u8array.length = numBytesWritten return u8array } var TTY = { ttys: [], init: function() {}, shutdown: function() {}, register: function(dev, ops) { TTY.ttys[dev] = { input: [], output: [], ops: ops } FS.registerDevice(dev, TTY.stream_ops) }, stream_ops: { open: function(stream) { var tty = TTY.ttys[stream.node.rdev] if (!tty) { throw new FS.ErrnoError(43) } stream.tty = tty stream.seekable = false }, close: function(stream) { stream.tty.ops.fsync(stream.tty) }, fsync: function(stream) { stream.tty.ops.fsync(stream.tty) }, read: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.get_char) { throw new FS.ErrnoError(60) } var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = stream.tty.ops.get_char(stream.tty) } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.put_char) { throw new FS.ErrnoError(60) } try { for (var i = 0; i < length; i++) { stream.tty.ops.put_char(stream.tty, buffer[offset + i]) } } catch (e) { throw new FS.ErrnoError(29) } if (length) { stream.node.timestamp = Date.now() } return i }, }, default_tty_ops: { get_char: function(tty) { if (!tty.input.length) { var result = null if (ENVIRONMENT_IS_NODE) { var BUFSIZE = 256 var buf = Buffer.alloc(BUFSIZE) var bytesRead = 0 try { bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE, -1) } catch (e) { if (e.toString().includes('EOF')) bytesRead = 0 else throw e } if (bytesRead > 0) { result = buf.slice(0, bytesRead).toString('utf-8') } else { result = null } } else if ( typeof window != 'undefined' && typeof window.prompt == 'function' ) { result = window.prompt('Input: ') if (result !== null) { result += '\n' } } else if (typeof readline == 'function') { result = readline() if (result !== null) { result += '\n' } } if (!result) { return null } tty.input = intArrayFromString(result, true) } return tty.input.shift() }, put_char: function(tty, val) { if (val === null || val === 10) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, default_tty1_ops: { put_char: function(tty, val) { if (val === null || val === 10) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, } function mmapAlloc(size) { abort() } var MEMFS = { ops_table: null, mount: function(mount) { return MEMFS.createNode(null, '/', 16384 | 511, 0) }, createNode: function(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { throw new FS.ErrnoError(63) } if (!MEMFS.ops_table) { MEMFS.ops_table = { dir: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, lookup: MEMFS.node_ops.lookup, mknod: MEMFS.node_ops.mknod, rename: MEMFS.node_ops.rename, unlink: MEMFS.node_ops.unlink, rmdir: MEMFS.node_ops.rmdir, readdir: MEMFS.node_ops.readdir, symlink: MEMFS.node_ops.symlink, }, stream: { llseek: MEMFS.stream_ops.llseek }, }, file: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: { llseek: MEMFS.stream_ops.llseek, read: MEMFS.stream_ops.read, write: MEMFS.stream_ops.write, allocate: MEMFS.stream_ops.allocate, mmap: MEMFS.stream_ops.mmap, msync: MEMFS.stream_ops.msync, }, }, link: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, readlink: MEMFS.node_ops.readlink, }, stream: {}, }, chrdev: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: FS.chrdev_stream_ops, }, } } var node = FS.createNode(parent, name, mode, dev) if (FS.isDir(node.mode)) { node.node_ops = MEMFS.ops_table.dir.node node.stream_ops = MEMFS.ops_table.dir.stream node.contents = {} } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node node.stream_ops = MEMFS.ops_table.file.stream node.usedBytes = 0 node.contents = null } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node node.stream_ops = MEMFS.ops_table.link.stream } else if (FS.isChrdev(node.mode)) { node.node_ops = MEMFS.ops_table.chrdev.node node.stream_ops = MEMFS.ops_table.chrdev.stream } node.timestamp = Date.now() if (parent) { parent.contents[name] = node parent.timestamp = node.timestamp } return node }, getFileDataAsTypedArray: function(node) { if (!node.contents) return new Uint8Array(0) if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes) return new Uint8Array(node.contents) }, expandFileStorage: function(node, newCapacity) { var prevCapacity = node.contents ? node.contents.length : 0 if (prevCapacity >= newCapacity) return var CAPACITY_DOUBLING_MAX = 1024 * 1024 newCapacity = Math.max( newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2 : 1.125)) >>> 0 ) if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256) var oldContents = node.contents node.contents = new Uint8Array(newCapacity) if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0) }, resizeFileStorage: function(node, newSize) { if (node.usedBytes == newSize) return if (newSize == 0) { node.contents = null node.usedBytes = 0 } else { var oldContents = node.contents node.contents = new Uint8Array(newSize) if (oldContents) { node.contents.set( oldContents.subarray(0, Math.min(newSize, node.usedBytes)) ) } node.usedBytes = newSize } }, node_ops: { getattr: function(node) { var attr = {} attr.dev = FS.isChrdev(node.mode) ? node.id : 1 attr.ino = node.id attr.mode = node.mode attr.nlink = 1 attr.uid = 0 attr.gid = 0 attr.rdev = node.rdev if (FS.isDir(node.mode)) { attr.size = 4096 } else if (FS.isFile(node.mode)) { attr.size = node.usedBytes } else if (FS.isLink(node.mode)) { attr.size = node.link.length } else { attr.size = 0 } attr.atime = new Date(node.timestamp) attr.mtime = new Date(node.timestamp) attr.ctime = new Date(node.timestamp) attr.blksize = 4096 attr.blocks = Math.ceil(attr.size / attr.blksize) return attr }, setattr: function(node, attr) { if (attr.mode !== undefined) { node.mode = attr.mode } if (attr.timestamp !== undefined) { node.timestamp = attr.timestamp } if (attr.size !== undefined) { MEMFS.resizeFileStorage(node, attr.size) } }, lookup: function(parent, name) { throw FS.genericErrors[44] }, mknod: function(parent, name, mode, dev) { return MEMFS.createNode(parent, name, mode, dev) }, rename: function(old_node, new_dir, new_name) { if (FS.isDir(old_node.mode)) { var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (new_node) { for (var i in new_node.contents) { throw new FS.ErrnoError(55) } } } delete old_node.parent.contents[old_node.name] old_node.parent.timestamp = Date.now() old_node.name = new_name new_dir.contents[new_name] = old_node new_dir.timestamp = old_node.parent.timestamp old_node.parent = new_dir }, unlink: function(parent, name) { delete parent.contents[name] parent.timestamp = Date.now() }, rmdir: function(parent, name) { var node = FS.lookupNode(parent, name) for (var i in node.contents) { throw new FS.ErrnoError(55) } delete parent.contents[name] parent.timestamp = Date.now() }, readdir: function(node) { var entries = ['.', '..'] for (var key in node.contents) { if (!node.contents.hasOwnProperty(key)) { continue } entries.push(key) } return entries }, symlink: function(parent, newname, oldpath) { var node = MEMFS.createNode(parent, newname, 511 | 40960, 0) node.link = oldpath return node }, readlink: function(node) { if (!FS.isLink(node.mode)) { throw new FS.ErrnoError(28) } return node.link }, }, stream_ops: { read: function(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= stream.node.usedBytes) return 0 var size = Math.min(stream.node.usedBytes - position, length) if (size > 8 && contents.subarray) { buffer.set(contents.subarray(position, position + size), offset) } else { for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i] } return size }, write: function(stream, buffer, offset, length, position, canOwn) { if (buffer.buffer === HEAP8.buffer) { canOwn = false } if (!length) return 0 var node = stream.node node.timestamp = Date.now() if (buffer.subarray && (!node.contents || node.contents.subarray)) { if (canOwn) { node.contents = buffer.subarray(offset, offset + length) node.usedBytes = length return length } else if (node.usedBytes === 0 && position === 0) { node.contents = buffer.slice(offset, offset + length) node.usedBytes = length return length } else if (position + length <= node.usedBytes) { node.contents.set( buffer.subarray(offset, offset + length), position ) return length } } MEMFS.expandFileStorage(node, position + length) if (node.contents.subarray && buffer.subarray) { node.contents.set( buffer.subarray(offset, offset + length), position ) } else { for (var i = 0; i < length; i++) { node.contents[position + i] = buffer[offset + i] } } node.usedBytes = Math.max(node.usedBytes, position + length) return length }, llseek: function(stream, offset, whence) { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { position += stream.node.usedBytes } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, allocate: function(stream, offset, length) { MEMFS.expandFileStorage(stream.node, offset + length) stream.node.usedBytes = Math.max( stream.node.usedBytes, offset + length ) }, mmap: function(stream, length, position, prot, flags) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr var allocated var contents = stream.node.contents if (!(flags & 2) && contents.buffer === buffer) { allocated = false ptr = contents.byteOffset } else { if (position > 0 || position + length < contents.length) { if (contents.subarray) { contents = contents.subarray(position, position + length) } else { contents = Array.prototype.slice.call( contents, position, position + length ) } } allocated = true ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } HEAP8.set(contents, ptr) } return { ptr: ptr, allocated: allocated } }, msync: function(stream, buffer, offset, length, mmapFlags) { MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } function asyncLoad(url, onload, onerror, noRunDep) { var dep = !noRunDep ? getUniqueRunDependency('al ' + url) : '' readAsync( url, arrayBuffer => { assert( arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).' ) onload(new Uint8Array(arrayBuffer)) if (dep) removeRunDependency(dep) }, event => { if (onerror) { onerror() } else { throw 'Loading data file "' + url + '" failed.' } } ) if (dep) addRunDependency(dep) } var ERRNO_CODES = {} var NODEFS = { isWindows: false, staticInit: () => { NODEFS.isWindows = !!process.platform.match(/^win/) var flags = process['binding']('constants') if (flags['fs']) { flags = flags['fs'] } NODEFS.flagsForNodeMap = { 1024: flags['O_APPEND'], 64: flags['O_CREAT'], 128: flags['O_EXCL'], 256: flags['O_NOCTTY'], 0: flags['O_RDONLY'], 2: flags['O_RDWR'], 4096: flags['O_SYNC'], 512: flags['O_TRUNC'], 1: flags['O_WRONLY'], 131072: flags['O_NOFOLLOW'], } }, convertNodeCode: e => { var code = e.code return ERRNO_CODES[code] }, mount: mount => { return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0) }, createNode: (parent, name, mode, dev) => { if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { throw new FS.ErrnoError(28) } var node = FS.createNode(parent, name, mode) node.node_ops = NODEFS.node_ops node.stream_ops = NODEFS.stream_ops return node }, getMode: path => { var stat try { stat = fs.lstatSync(path) if (NODEFS.isWindows) { stat.mode = stat.mode | ((stat.mode & 292) >> 2) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return stat.mode }, realPath: node => { var parts = [] while (node.parent !== node) { parts.push(node.name) node = node.parent } parts.push(node.mount.opts.root) parts.reverse() return PATH.join.apply(null, parts) }, flagsForNode: flags => { flags &= ~2097152 flags &= ~2048 flags &= ~32768 flags &= ~524288 flags &= ~65536 var newFlags = 0 for (var k in NODEFS.flagsForNodeMap) { if (flags & k) { newFlags |= NODEFS.flagsForNodeMap[k] flags ^= k } } if (flags) { throw new FS.ErrnoError(28) } return newFlags }, node_ops: { getattr: node => { var path = NODEFS.realPath(node) var stat try { stat = fs.lstatSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } if (NODEFS.isWindows && !stat.blksize) { stat.blksize = 4096 } if (NODEFS.isWindows && !stat.blocks) { stat.blocks = ((stat.size + stat.blksize - 1) / stat.blksize) | 0 } return { dev: stat.dev, ino: stat.ino, mode: stat.mode, nlink: stat.nlink, uid: stat.uid, gid: stat.gid, rdev: stat.rdev, size: stat.size, atime: stat.atime, mtime: stat.mtime, ctime: stat.ctime, blksize: stat.blksize, blocks: stat.blocks, } }, setattr: (node, attr) => { var path = NODEFS.realPath(node) try { if (attr.mode !== undefined) { fs.chmodSync(path, attr.mode) node.mode = attr.mode } if (attr.timestamp !== undefined) { var date = new Date(attr.timestamp) fs.utimesSync(path, date, date) } if (attr.size !== undefined) { fs.truncateSync(path, attr.size) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, lookup: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) var mode = NODEFS.getMode(path) return NODEFS.createNode(parent, name, mode) }, mknod: (parent, name, mode, dev) => { var node = NODEFS.createNode(parent, name, mode, dev) var path = NODEFS.realPath(node) try { if (FS.isDir(node.mode)) { fs.mkdirSync(path, node.mode) } else { fs.writeFileSync(path, '', { mode: node.mode }) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return node }, rename: (oldNode, newDir, newName) => { var oldPath = NODEFS.realPath(oldNode) var newPath = PATH.join2(NODEFS.realPath(newDir), newName) try { fs.renameSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } oldNode.name = newName }, unlink: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.unlinkSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, rmdir: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.rmdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readdir: node => { var path = NODEFS.realPath(node) try { return fs.readdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, symlink: (parent, newName, oldPath) => { var newPath = PATH.join2(NODEFS.realPath(parent), newName) try { fs.symlinkSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readlink: node => { var path = NODEFS.realPath(node) try { path = fs.readlinkSync(path) path = nodePath.relative( nodePath.resolve(node.mount.opts.root), path ) return path } catch (e) { if (!e.code) throw e if (e.code === 'UNKNOWN') throw new FS.ErrnoError(28) throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, }, stream_ops: { open: stream => { var path = NODEFS.realPath(stream.node) try { if (FS.isFile(stream.node.mode)) { stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags)) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, close: stream => { try { if (FS.isFile(stream.node.mode) && stream.nfd) { fs.closeSync(stream.nfd) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, read: (stream, buffer, offset, length, position) => { if (length === 0) return 0 try { return fs.readSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, write: (stream, buffer, offset, length, position) => { try { return fs.writeSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, llseek: (stream, offset, whence) => { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { try { var stat = fs.fstatSync(stream.nfd) position += stat.size } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, mmap: (stream, length, position, prot, flags) => { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr = mmapAlloc(length) NODEFS.stream_ops.read(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } }, msync: (stream, buffer, offset, length, mmapFlags) => { NODEFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } var FS = { root: null, mounts: [], devices: {}, streams: [], nextInode: 1, nameTable: null, currentPath: '/', initialized: false, ignorePermissions: true, ErrnoError: null, genericErrors: {}, filesystems: null, syncFSRequests: 0, lookupPath: (path, opts = {}) => { path = PATH_FS.resolve(path) if (!path) return { path: '', node: null } var defaults = { follow_mount: true, recurse_count: 0 } opts = Object.assign(defaults, opts) if (opts.recurse_count > 8) { throw new FS.ErrnoError(32) } var parts = path.split('/').filter(p => !!p) var current = FS.root var current_path = '/' for (var i = 0; i < parts.length; i++) { var islast = i === parts.length - 1 if (islast && opts.parent) { break } current = FS.lookupNode(current, parts[i]) current_path = PATH.join2(current_path, parts[i]) if (FS.isMountpoint(current)) { if (!islast || (islast && opts.follow_mount)) { current = current.mounted.root } } if (!islast || opts.follow) { var count = 0 while (FS.isLink(current.mode)) { var link = FS.readlink(current_path) current_path = PATH_FS.resolve(PATH.dirname(current_path), link) var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count + 1, }) current = lookup.node if (count++ > 40) { throw new FS.ErrnoError(32) } } } } return { path: current_path, node: current } }, getPath: node => { var path while (true) { if (FS.isRoot(node)) { var mount = node.mount.mountpoint if (!path) return mount return mount[mount.length - 1] !== '/' ? mount + '/' + path : mount + path } path = path ? node.name + '/' + path : node.name node = node.parent } }, hashName: (parentid, name) => { var hash = 0 for (var i = 0; i < name.length; i++) { hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0 } return ((parentid + hash) >>> 0) % FS.nameTable.length }, hashAddNode: node => { var hash = FS.hashName(node.parent.id, node.name) node.name_next = FS.nameTable[hash] FS.nameTable[hash] = node }, hashRemoveNode: node => { var hash = FS.hashName(node.parent.id, node.name) if (FS.nameTable[hash] === node) { FS.nameTable[hash] = node.name_next } else { var current = FS.nameTable[hash] while (current) { if (current.name_next === node) { current.name_next = node.name_next break } current = current.name_next } } }, lookupNode: (parent, name) => { var errCode = FS.mayLookup(parent) if (errCode) { throw new FS.ErrnoError(errCode, parent) } var hash = FS.hashName(parent.id, name) for (var node = FS.nameTable[hash]; node; node = node.name_next) { var nodeName = node.name if (node.parent.id === parent.id && nodeName === name) { return node } } return FS.lookup(parent, name) }, createNode: (parent, name, mode, rdev) => { var node = new FS.FSNode(parent, name, mode, rdev) FS.hashAddNode(node) return node }, destroyNode: node => { FS.hashRemoveNode(node) }, isRoot: node => { return node === node.parent }, isMountpoint: node => { return !!node.mounted }, isFile: mode => { return (mode & 61440) === 32768 }, isDir: mode => { return (mode & 61440) === 16384 }, isLink: mode => { return (mode & 61440) === 40960 }, isChrdev: mode => { return (mode & 61440) === 8192 }, isBlkdev: mode => { return (mode & 61440) === 24576 }, isFIFO: mode => { return (mode & 61440) === 4096 }, isSocket: mode => { return (mode & 49152) === 49152 }, flagModes: { r: 0, 'r+': 2, w: 577, 'w+': 578, a: 1089, 'a+': 1090 }, modeStringToFlags: str => { var flags = FS.flagModes[str] if (typeof flags == 'undefined') { throw new Error('Unknown file open mode: ' + str) } return flags }, flagsToPermissionString: flag => { var perms = ['r', 'w', 'rw'][flag & 3] if (flag & 512) { perms += 'w' } return perms }, nodePermissions: (node, perms) => { if (FS.ignorePermissions) { return 0 } if (perms.includes('r') && !(node.mode & 292)) { return 2 } else if (perms.includes('w') && !(node.mode & 146)) { return 2 } else if (perms.includes('x') && !(node.mode & 73)) { return 2 } return 0 }, mayLookup: dir => { var errCode = FS.nodePermissions(dir, 'x') if (errCode) return errCode if (!dir.node_ops.lookup) return 2 return 0 }, mayCreate: (dir, name) => { try { var node = FS.lookupNode(dir, name) return 20 } catch (e) {} return FS.nodePermissions(dir, 'wx') }, mayDelete: (dir, name, isdir) => { var node try { node = FS.lookupNode(dir, name) } catch (e) { return e.errno } var errCode = FS.nodePermissions(dir, 'wx') if (errCode) { return errCode } if (isdir) { if (!FS.isDir(node.mode)) { return 54 } if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return 10 } } else { if (FS.isDir(node.mode)) { return 31 } } return 0 }, mayOpen: (node, flags) => { if (!node) { return 44 } if (FS.isLink(node.mode)) { return 32 } else if (FS.isDir(node.mode)) { if (FS.flagsToPermissionString(flags) !== 'r' || flags & 512) { return 31 } } return FS.nodePermissions(node, FS.flagsToPermissionString(flags)) }, MAX_OPEN_FDS: 4096, nextfd: (fd_start = 0, fd_end = FS.MAX_OPEN_FDS) => { for (var fd = fd_start; fd <= fd_end; fd++) { if (!FS.streams[fd]) { return fd } } throw new FS.ErrnoError(33) }, getStream: fd => FS.streams[fd], createStream: (stream, fd_start, fd_end) => { if (!FS.FSStream) { FS.FSStream = function() { this.shared = {} } FS.FSStream.prototype = {} Object.defineProperties(FS.FSStream.prototype, { object: { get: function() { return this.node }, set: function(val) { this.node = val }, }, isRead: { get: function() { return (this.flags & 2097155) !== 1 }, }, isWrite: { get: function() { return (this.flags & 2097155) !== 0 }, }, isAppend: { get: function() { return this.flags & 1024 }, }, flags: { get: function() { return this.shared.flags }, set: function(val) { this.shared.flags = val }, }, position: { get: function() { return this.shared.position }, set: function(val) { this.shared.position = val }, }, }) } stream = Object.assign(new FS.FSStream(), stream) var fd = FS.nextfd(fd_start, fd_end) stream.fd = fd FS.streams[fd] = stream return stream }, closeStream: fd => { FS.streams[fd] = null }, chrdev_stream_ops: { open: stream => { var device = FS.getDevice(stream.node.rdev) stream.stream_ops = device.stream_ops if (stream.stream_ops.open) { stream.stream_ops.open(stream) } }, llseek: () => { throw new FS.ErrnoError(70) }, }, major: dev => dev >> 8, minor: dev => dev & 255, makedev: (ma, mi) => (ma << 8) | mi, registerDevice: (dev, ops) => { FS.devices[dev] = { stream_ops: ops } }, getDevice: dev => FS.devices[dev], getMounts: mount => { var mounts = [] var check = [mount] while (check.length) { var m = check.pop() mounts.push(m) check.push.apply(check, m.mounts) } return mounts }, syncfs: (populate, callback) => { if (typeof populate == 'function') { callback = populate populate = false } FS.syncFSRequests++ if (FS.syncFSRequests > 1) { err( 'warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work' ) } var mounts = FS.getMounts(FS.root.mount) var completed = 0 function doCallback(errCode) { FS.syncFSRequests-- return callback(errCode) } function done(errCode) { if (errCode) { if (!done.errored) { done.errored = true return doCallback(errCode) } return } if (++completed >= mounts.length) { doCallback(null) } } mounts.forEach(mount => { if (!mount.type.syncfs) { return done(null) } mount.type.syncfs(mount, populate, done) }) }, mount: (type, opts, mountpoint) => { var root = mountpoint === '/' var pseudo = !mountpoint var node if (root && FS.root) { throw new FS.ErrnoError(10) } else if (!root && !pseudo) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) mountpoint = lookup.path node = lookup.node if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } if (!FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } } var mount = { type: type, opts: opts, mountpoint: mountpoint, mounts: [], } var mountRoot = type.mount(mount) mountRoot.mount = mount mount.root = mountRoot if (root) { FS.root = mountRoot } else if (node) { node.mounted = mount if (node.mount) { node.mount.mounts.push(mount) } } return mountRoot }, unmount: mountpoint => { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) if (!FS.isMountpoint(lookup.node)) { throw new FS.ErrnoError(28) } var node = lookup.node var mount = node.mounted var mounts = FS.getMounts(mount) Object.keys(FS.nameTable).forEach(hash => { var current = FS.nameTable[hash] while (current) { var next = current.name_next if (mounts.includes(current.mount)) { FS.destroyNode(current) } current = next } }) node.mounted = null var idx = node.mount.mounts.indexOf(mount) node.mount.mounts.splice(idx, 1) }, lookup: (parent, name) => { return parent.node_ops.lookup(parent, name) }, mknod: (path, mode, dev) => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) if (!name || name === '.' || name === '..') { throw new FS.ErrnoError(28) } var errCode = FS.mayCreate(parent, name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.mknod) { throw new FS.ErrnoError(63) } return parent.node_ops.mknod(parent, name, mode, dev) }, create: (path, mode) => { mode = mode !== undefined ? mode : 438 mode &= 4095 mode |= 32768 return FS.mknod(path, mode, 0) }, mkdir: (path, mode) => { mode = mode !== undefined ? mode : 511 mode &= 511 | 512 mode |= 16384 return FS.mknod(path, mode, 0) }, mkdirTree: (path, mode) => { var dirs = path.split('/') var d = '' for (var i = 0; i < dirs.length; ++i) { if (!dirs[i]) continue d += '/' + dirs[i] try { FS.mkdir(d, mode) } catch (e) { if (e.errno != 20) throw e } } }, mkdev: (path, mode, dev) => { if (typeof dev == 'undefined') { dev = mode mode = 438 } mode |= 8192 return FS.mknod(path, mode, dev) }, symlink: (oldpath, newpath) => { if (!PATH_FS.resolve(oldpath)) { throw new FS.ErrnoError(44) } var lookup = FS.lookupPath(newpath, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var newname = PATH.basename(newpath) var errCode = FS.mayCreate(parent, newname) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.symlink) { throw new FS.ErrnoError(63) } return parent.node_ops.symlink(parent, newname, oldpath) }, rename: (old_path, new_path) => { var old_dirname = PATH.dirname(old_path) var new_dirname = PATH.dirname(new_path) var old_name = PATH.basename(old_path) var new_name = PATH.basename(new_path) var lookup, old_dir, new_dir lookup = FS.lookupPath(old_path, { parent: true }) old_dir = lookup.node lookup = FS.lookupPath(new_path, { parent: true }) new_dir = lookup.node if (!old_dir || !new_dir) throw new FS.ErrnoError(44) if (old_dir.mount !== new_dir.mount) { throw new FS.ErrnoError(75) } var old_node = FS.lookupNode(old_dir, old_name) var relative = PATH_FS.relative(old_path, new_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(28) } relative = PATH_FS.relative(new_path, old_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(55) } var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (old_node === new_node) { return } var isdir = FS.isDir(old_node.mode) var errCode = FS.mayDelete(old_dir, old_name, isdir) if (errCode) { throw new FS.ErrnoError(errCode) } errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!old_dir.node_ops.rename) { throw new FS.ErrnoError(63) } if ( FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node)) ) { throw new FS.ErrnoError(10) } if (new_dir !== old_dir) { errCode = FS.nodePermissions(old_dir, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } } FS.hashRemoveNode(old_node) try { old_dir.node_ops.rename(old_node, new_dir, new_name) } catch (e) { throw e } finally { FS.hashAddNode(old_node) } }, rmdir: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, true) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.rmdir) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.rmdir(parent, name) FS.destroyNode(node) }, readdir: path => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node if (!node.node_ops.readdir) { throw new FS.ErrnoError(54) } return node.node_ops.readdir(node) }, unlink: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, false) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.unlink) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.unlink(parent, name) FS.destroyNode(node) }, readlink: path => { var lookup = FS.lookupPath(path) var link = lookup.node if (!link) { throw new FS.ErrnoError(44) } if (!link.node_ops.readlink) { throw new FS.ErrnoError(28) } return PATH_FS.resolve( FS.getPath(link.parent), link.node_ops.readlink(link) ) }, stat: (path, dontFollow) => { var lookup = FS.lookupPath(path, { follow: !dontFollow }) var node = lookup.node if (!node) { throw new FS.ErrnoError(44) } if (!node.node_ops.getattr) { throw new FS.ErrnoError(63) } return node.node_ops.getattr(node) }, lstat: path => { return FS.stat(path, true) }, chmod: (path, mode, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { mode: (mode & 4095) | (node.mode & ~4095), timestamp: Date.now(), }) }, lchmod: (path, mode) => { FS.chmod(path, mode, true) }, fchmod: (fd, mode) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chmod(stream.node, mode) }, chown: (path, uid, gid, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { timestamp: Date.now() }) }, lchown: (path, uid, gid) => { FS.chown(path, uid, gid, true) }, fchown: (fd, uid, gid) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chown(stream.node, uid, gid) }, truncate: (path, len) => { if (len < 0) { throw new FS.ErrnoError(28) } var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: true }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } if (FS.isDir(node.mode)) { throw new FS.ErrnoError(31) } if (!FS.isFile(node.mode)) { throw new FS.ErrnoError(28) } var errCode = FS.nodePermissions(node, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } node.node_ops.setattr(node, { size: len, timestamp: Date.now() }) }, ftruncate: (fd, len) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(28) } FS.truncate(stream.node, len) }, utime: (path, atime, mtime) => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node node.node_ops.setattr(node, { timestamp: Math.max(atime, mtime) }) }, open: (path, flags, mode) => { if (path === '') { throw new FS.ErrnoError(44) } flags = typeof flags == 'string' ? FS.modeStringToFlags(flags) : flags mode = typeof mode == 'undefined' ? 438 : mode if (flags & 64) { mode = (mode & 4095) | 32768 } else { mode = 0 } var node if (typeof path == 'object') { node = path } else { path = PATH.normalize(path) try { var lookup = FS.lookupPath(path, { follow: !(flags & 131072) }) node = lookup.node } catch (e) {} } var created = false if (flags & 64) { if (node) { if (flags & 128) { throw new FS.ErrnoError(20) } } else { node = FS.mknod(path, mode, 0) created = true } } if (!node) { throw new FS.ErrnoError(44) } if (FS.isChrdev(node.mode)) { flags &= ~512 } if (flags & 65536 && !FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } if (!created) { var errCode = FS.mayOpen(node, flags) if (errCode) { throw new FS.ErrnoError(errCode) } } if (flags & 512 && !created) { FS.truncate(node, 0) } flags &= ~(128 | 512 | 131072) var stream = FS.createStream({ node: node, path: FS.getPath(node), flags: flags, seekable: true, position: 0, stream_ops: node.stream_ops, ungotten: [], error: false, }) if (stream.stream_ops.open) { stream.stream_ops.open(stream) } if (Module['logReadFiles'] && !(flags & 1)) { if (!FS.readFiles) FS.readFiles = {} if (!(path in FS.readFiles)) { FS.readFiles[path] = 1 } } return stream }, close: stream => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (stream.getdents) stream.getdents = null try { if (stream.stream_ops.close) { stream.stream_ops.close(stream) } } catch (e) { throw e } finally { FS.closeStream(stream.fd) } stream.fd = null }, isClosed: stream => { return stream.fd === null }, llseek: (stream, offset, whence) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (!stream.seekable || !stream.stream_ops.llseek) { throw new FS.ErrnoError(70) } if (whence != 0 && whence != 1 && whence != 2) { throw new FS.ErrnoError(28) } stream.position = stream.stream_ops.llseek(stream, offset, whence) stream.ungotten = [] return stream.position }, read: (stream, buffer, offset, length, position) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.read) { throw new FS.ErrnoError(28) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesRead = stream.stream_ops.read( stream, buffer, offset, length, position ) if (!seeking) stream.position += bytesRead return bytesRead }, write: (stream, buffer, offset, length, position, canOwn) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.write) { throw new FS.ErrnoError(28) } if (stream.seekable && stream.flags & 1024) { FS.llseek(stream, 0, 2) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesWritten = stream.stream_ops.write( stream, buffer, offset, length, position, canOwn ) if (!seeking) stream.position += bytesWritten return bytesWritten }, allocate: (stream, offset, length) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (offset < 0 || length <= 0) { throw new FS.ErrnoError(28) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(43) } if (!stream.stream_ops.allocate) { throw new FS.ErrnoError(138) } stream.stream_ops.allocate(stream, offset, length) }, mmap: (stream, length, position, prot, flags) => { if ( (prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2 ) { throw new FS.ErrnoError(2) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(2) } if (!stream.stream_ops.mmap) { throw new FS.ErrnoError(43) } return stream.stream_ops.mmap(stream, length, position, prot, flags) }, msync: (stream, buffer, offset, length, mmapFlags) => { if (!stream.stream_ops.msync) { return 0 } return stream.stream_ops.msync( stream, buffer, offset, length, mmapFlags ) }, munmap: stream => 0, ioctl: (stream, cmd, arg) => { if (!stream.stream_ops.ioctl) { throw new FS.ErrnoError(59) } return stream.stream_ops.ioctl(stream, cmd, arg) }, readFile: (path, opts = {}) => { opts.flags = opts.flags || 0 opts.encoding = opts.encoding || 'binary' if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { throw new Error('Invalid encoding type "' + opts.encoding + '"') } var ret var stream = FS.open(path, opts.flags) var stat = FS.stat(path) var length = stat.size var buf = new Uint8Array(length) FS.read(stream, buf, 0, length, 0) if (opts.encoding === 'utf8') { ret = UTF8ArrayToString(buf, 0) } else if (opts.encoding === 'binary') { ret = buf } FS.close(stream) return ret }, writeFile: (path, data, opts = {}) => { opts.flags = opts.flags || 577 var stream = FS.open(path, opts.flags, opts.mode) if (typeof data == 'string') { var buf = new Uint8Array(lengthBytesUTF8(data) + 1) var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length) FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn) } else if (ArrayBuffer.isView(data)) { FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn) } else { throw new Error('Unsupported data type') } FS.close(stream) }, cwd: () => FS.currentPath, chdir: path => { var lookup = FS.lookupPath(path, { follow: true }) if (lookup.node === null) { throw new FS.ErrnoError(44) } if (!FS.isDir(lookup.node.mode)) { throw new FS.ErrnoError(54) } var errCode = FS.nodePermissions(lookup.node, 'x') if (errCode) { throw new FS.ErrnoError(errCode) } FS.currentPath = lookup.path }, createDefaultDirectories: () => { FS.mkdir('/tmp') FS.mkdir('/home') FS.mkdir('/home/web_user') }, createDefaultDevices: () => { FS.mkdir('/dev') FS.registerDevice(FS.makedev(1, 3), { read: () => 0, write: (stream, buffer, offset, length, pos) => length, }) FS.mkdev('/dev/null', FS.makedev(1, 3)) TTY.register(FS.makedev(5, 0), TTY.default_tty_ops) TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops) FS.mkdev('/dev/tty', FS.makedev(5, 0)) FS.mkdev('/dev/tty1', FS.makedev(6, 0)) var random_device = getRandomDevice() FS.createDevice('/dev', 'random', random_device) FS.createDevice('/dev', 'urandom', random_device) FS.mkdir('/dev/shm') FS.mkdir('/dev/shm/tmp') }, createSpecialDirectories: () => { FS.mkdir('/proc') var proc_self = FS.mkdir('/proc/self') FS.mkdir('/proc/self/fd') FS.mount( { mount: () => { var node = FS.createNode(proc_self, 'fd', 16384 | 511, 73) node.node_ops = { lookup: (parent, name) => { var fd = +name var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) var ret = { parent: null, mount: { mountpoint: 'fake' }, node_ops: { readlink: () => stream.path }, } ret.parent = ret return ret }, } return node }, }, {}, '/proc/self/fd' ) }, createStandardStreams: () => { if (Module['stdin']) { FS.createDevice('/dev', 'stdin', Module['stdin']) } else { FS.symlink('/dev/tty', '/dev/stdin') } if (Module['stdout']) { FS.createDevice('/dev', 'stdout', null, Module['stdout']) } else { FS.symlink('/dev/tty', '/dev/stdout') } if (Module['stderr']) { FS.createDevice('/dev', 'stderr', null, Module['stderr']) } else { FS.symlink('/dev/tty1', '/dev/stderr') } var stdin = FS.open('/dev/stdin', 0) var stdout = FS.open('/dev/stdout', 1) var stderr = FS.open('/dev/stderr', 1) }, ensureErrnoError: () => { if (FS.ErrnoError) return FS.ErrnoError = function ErrnoError(errno, node) { this.node = node this.setErrno = function(errno) { this.errno = errno } this.setErrno(errno) this.message = 'FS error' } FS.ErrnoError.prototype = new Error() FS.ErrnoError.prototype.constructor = FS.ErrnoError ;[44].forEach(code => { FS.genericErrors[code] = new FS.ErrnoError(code) FS.genericErrors[code].stack = '' }) }, staticInit: () => { FS.ensureErrnoError() FS.nameTable = new Array(4096) FS.mount(MEMFS, {}, '/') FS.createDefaultDirectories() FS.createDefaultDevices() FS.createSpecialDirectories() FS.filesystems = { MEMFS: MEMFS, NODEFS: NODEFS } }, init: (input, output, error) => { FS.init.initialized = true FS.ensureErrnoError() Module['stdin'] = input || Module['stdin'] Module['stdout'] = output || Module['stdout'] Module['stderr'] = error || Module['stderr'] FS.createStandardStreams() }, quit: () => { FS.init.initialized = false for (var i = 0; i < FS.streams.length; i++) { var stream = FS.streams[i] if (!stream) { continue } FS.close(stream) } }, getMode: (canRead, canWrite) => { var mode = 0 if (canRead) mode |= 292 | 73 if (canWrite) mode |= 146 return mode }, findObject: (path, dontResolveLastLink) => { var ret = FS.analyzePath(path, dontResolveLastLink) if (!ret.exists) { return null } return ret.object }, analyzePath: (path, dontResolveLastLink) => { try { var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) path = lookup.path } catch (e) {} var ret = { isRoot: false, exists: false, error: 0, name: null, path: null, object: null, parentExists: false, parentPath: null, parentObject: null, } try { var lookup = FS.lookupPath(path, { parent: true }) ret.parentExists = true ret.parentPath = lookup.path ret.parentObject = lookup.node ret.name = PATH.basename(path) lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) ret.exists = true ret.path = lookup.path ret.object = lookup.node ret.name = lookup.node.name ret.isRoot = lookup.path === '/' } catch (e) { ret.error = e.errno } return ret }, createPath: (parent, path, canRead, canWrite) => { parent = typeof parent == 'string' ? parent : FS.getPath(parent) var parts = path.split('/').reverse() while (parts.length) { var part = parts.pop() if (!part) continue var current = PATH.join2(parent, part) try { FS.mkdir(current) } catch (e) {} parent = current } return current }, createFile: (parent, name, properties, canRead, canWrite) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(canRead, canWrite) return FS.create(path, mode) }, createDataFile: (parent, name, data, canRead, canWrite, canOwn) => { var path = name if (parent) { parent = typeof parent == 'string' ? parent : FS.getPath(parent) path = name ? PATH.join2(parent, name) : parent } var mode = FS.getMode(canRead, canWrite) var node = FS.create(path, mode) if (data) { if (typeof data == 'string') { var arr = new Array(data.length) for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i) data = arr } FS.chmod(node, mode | 146) var stream = FS.open(node, 577) FS.write(stream, data, 0, data.length, 0, canOwn) FS.close(stream) FS.chmod(node, mode) } return node }, createDevice: (parent, name, input, output) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(!!input, !!output) if (!FS.createDevice.major) FS.createDevice.major = 64 var dev = FS.makedev(FS.createDevice.major++, 0) FS.registerDevice(dev, { open: stream => { stream.seekable = false }, close: stream => { if (output && output.buffer && output.buffer.length) { output(10) } }, read: (stream, buffer, offset, length, pos) => { var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = input() } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: (stream, buffer, offset, length, pos) => { for (var i = 0; i < length; i++) { try { output(buffer[offset + i]) } catch (e) { throw new FS.ErrnoError(29) } } if (length) { stream.node.timestamp = Date.now() } return i }, }) return FS.mkdev(path, mode, dev) }, forceLoadFile: obj => { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true if (typeof XMLHttpRequest != 'undefined') { throw new Error( 'Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.' ) } else if (read_) { try { obj.contents = intArrayFromString(read_(obj.url), true) obj.usedBytes = obj.contents.length } catch (e) { throw new FS.ErrnoError(29) } } else { throw new Error('Cannot load without read() or XMLHttpRequest.') } }, createLazyFile: (parent, name, url, canRead, canWrite) => { function LazyUint8Array() { this.lengthKnown = false this.chunks = [] } LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) { if (idx > this.length - 1 || idx < 0) { return undefined } var chunkOffset = idx % this.chunkSize var chunkNum = (idx / this.chunkSize) | 0 return this.getter(chunkNum)[chunkOffset] } LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter( getter ) { this.getter = getter } LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { var xhr = new XMLHttpRequest() xhr.open('HEAD', url, false) xhr.send(null) if (!((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) throw new Error("Couldn't load " + url + '. Status: ' + xhr.status) var datalength = Number(xhr.getResponseHeader('Content-length')) var header var hasByteServing = (header = xhr.getResponseHeader('Accept-Ranges')) && header === 'bytes' var usesGzip = (header = xhr.getResponseHeader('Content-Encoding')) && header === 'gzip' var chunkSize = 1024 * 1024 if (!hasByteServing) chunkSize = datalength var doXHR = (from, to) => { if (from > to) throw new Error( 'invalid range (' + from + ', ' + to + ') or no bytes requested!' ) if (to > datalength - 1) throw new Error( 'only ' + datalength + ' bytes available! programmer error!' ) var xhr = new XMLHttpRequest() xhr.open('GET', url, false) if (datalength !== chunkSize) xhr.setRequestHeader('Range', 'bytes=' + from + '-' + to) xhr.responseType = 'arraybuffer' if (xhr.overrideMimeType) { xhr.overrideMimeType('text/plain; charset=x-user-defined') } xhr.send(null) if ( !((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ) throw new Error( "Couldn't load " + url + '. Status: ' + xhr.status ) if (xhr.response !== undefined) { return new Uint8Array(xhr.response || []) } return intArrayFromString(xhr.responseText || '', true) } var lazyArray = this lazyArray.setDataGetter(chunkNum => { var start = chunkNum * chunkSize var end = (chunkNum + 1) * chunkSize - 1 end = Math.min(end, datalength - 1) if (typeof lazyArray.chunks[chunkNum] == 'undefined') { lazyArray.chunks[chunkNum] = doXHR(start, end) } if (typeof lazyArray.chunks[chunkNum] == 'undefined') throw new Error('doXHR failed!') return lazyArray.chunks[chunkNum] }) if (usesGzip || !datalength) { chunkSize = datalength = 1 datalength = this.getter(0).length chunkSize = datalength out( 'LazyFiles on gzip forces download of the whole file when length is accessed' ) } this._length = datalength this._chunkSize = chunkSize this.lengthKnown = true } if (typeof XMLHttpRequest != 'undefined') { if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc' var lazyArray = new LazyUint8Array() Object.defineProperties(lazyArray, { length: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._length }, }, chunkSize: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._chunkSize }, }, }) var properties = { isDevice: false, contents: lazyArray } } else { var properties = { isDevice: false, url: url } } var node = FS.createFile(parent, name, properties, canRead, canWrite) if (properties.contents) { node.contents = properties.contents } else if (properties.url) { node.contents = null node.url = properties.url } Object.defineProperties(node, { usedBytes: { get: function() { return this.contents.length }, }, }) var stream_ops = {} var keys = Object.keys(node.stream_ops) keys.forEach(key => { var fn = node.stream_ops[key] stream_ops[key] = function forceLoadLazyFile() { FS.forceLoadFile(node) return fn.apply(null, arguments) } }) function writeChunks(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= contents.length) return 0 var size = Math.min(contents.length - position, length) if (contents.slice) { for (var i = 0; i < size; i++) { buffer[offset + i] = contents[position + i] } } else { for (var i = 0; i < size; i++) { buffer[offset + i] = contents.get(position + i) } } return size } stream_ops.read = (stream, buffer, offset, length, position) => { FS.forceLoadFile(node) return writeChunks(stream, buffer, offset, length, position) } stream_ops.mmap = (stream, length, position, prot, flags) => { FS.forceLoadFile(node) var ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } writeChunks(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } } node.stream_ops = stream_ops return node }, createPreloadedFile: ( parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish ) => { var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent var dep = getUniqueRunDependency('cp ' + fullname) function processData(byteArray) { function finish(byteArray) { if (preFinish) preFinish() if (!dontCreateFile) { FS.createDataFile( parent, name, byteArray, canRead, canWrite, canOwn ) } if (onload) onload() removeRunDependency(dep) } if ( Browser.handledByPreloadPlugin(byteArray, fullname, finish, () => { if (onerror) onerror() removeRunDependency(dep) }) ) { return } finish(byteArray) } addRunDependency(dep) if (typeof url == 'string') { asyncLoad(url, byteArray => processData(byteArray), onerror) } else { processData(url) } }, indexedDB: () => { return ( window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB ) }, DB_NAME: () => { return 'EM_FS_' + window.location.pathname }, DB_VERSION: 20, DB_STORE_NAME: 'FILE_DATA', saveFilesToDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = () => { out('creating db') var db = openRequest.result db.createObjectStore(FS.DB_STORE_NAME) } openRequest.onsuccess = () => { var db = openRequest.result var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite') var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var putRequest = files.put( FS.analyzePath(path).object.contents, path ) putRequest.onsuccess = () => { ok++ if (ok + fail == total) finish() } putRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, loadFilesFromDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = onerror openRequest.onsuccess = () => { var db = openRequest.result try { var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly') } catch (e) { onerror(e) return } var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var getRequest = files.get(path) getRequest.onsuccess = () => { if (FS.analyzePath(path).exists) { FS.unlink(path) } FS.createDataFile( PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true ) ok++ if (ok + fail == total) finish() } getRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, } var SYSCALLS = { DEFAULT_POLLMASK: 5, calculateAt: function(dirfd, path, allowEmpty) { if (PATH.isAbs(path)) { return path } var dir if (dirfd === -100) { dir = FS.cwd() } else { var dirstream = SYSCALLS.getStreamFromFD(dirfd) dir = dirstream.path } if (path.length == 0) { if (!allowEmpty) { throw new FS.ErrnoError(44) } return dir } return PATH.join2(dir, path) }, doStat: function(func, path, buf) { try { var stat = func(path) } catch (e) { if ( e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node)) ) { return -54 } throw e } HEAP32[buf >> 2] = stat.dev HEAP32[(buf + 8) >> 2] = stat.ino HEAP32[(buf + 12) >> 2] = stat.mode HEAPU32[(buf + 16) >> 2] = stat.nlink HEAP32[(buf + 20) >> 2] = stat.uid HEAP32[(buf + 24) >> 2] = stat.gid HEAP32[(buf + 28) >> 2] = stat.rdev ;(tempI64 = [ stat.size >>> 0, ((tempDouble = stat.size), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 40) >> 2] = tempI64[0]), (HEAP32[(buf + 44) >> 2] = tempI64[1]) HEAP32[(buf + 48) >> 2] = 4096 HEAP32[(buf + 52) >> 2] = stat.blocks var atime = stat.atime.getTime() var mtime = stat.mtime.getTime() var ctime = stat.ctime.getTime() ;(tempI64 = [ Math.floor(atime / 1e3) >>> 0, ((tempDouble = Math.floor(atime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 56) >> 2] = tempI64[0]), (HEAP32[(buf + 60) >> 2] = tempI64[1]) HEAPU32[(buf + 64) >> 2] = (atime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(mtime / 1e3) >>> 0, ((tempDouble = Math.floor(mtime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 72) >> 2] = tempI64[0]), (HEAP32[(buf + 76) >> 2] = tempI64[1]) HEAPU32[(buf + 80) >> 2] = (mtime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(ctime / 1e3) >>> 0, ((tempDouble = Math.floor(ctime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 88) >> 2] = tempI64[0]), (HEAP32[(buf + 92) >> 2] = tempI64[1]) HEAPU32[(buf + 96) >> 2] = (ctime % 1e3) * 1e3 ;(tempI64 = [ stat.ino >>> 0, ((tempDouble = stat.ino), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 104) >> 2] = tempI64[0]), (HEAP32[(buf + 108) >> 2] = tempI64[1]) return 0 }, doMsync: function(addr, stream, len, flags, offset) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } if (flags & 2) { return 0 } var buffer = HEAPU8.slice(addr, addr + len) FS.msync(stream, buffer, offset, len, flags) }, varargs: undefined, get: function() { SYSCALLS.varargs += 4 var ret = HEAP32[(SYSCALLS.varargs - 4) >> 2] return ret }, getStr: function(ptr) { var ret = UTF8ToString(ptr) return ret }, getStreamFromFD: function(fd) { var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) return stream }, } function ___syscall_fcntl64(fd, cmd, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (cmd) { case 0: { var arg = SYSCALLS.get() if (arg < 0) { return -28 } var newStream newStream = FS.createStream(stream, arg) return newStream.fd } case 1: case 2: return 0 case 3: return stream.flags case 4: { var arg = SYSCALLS.get() stream.flags |= arg return 0 } case 5: { var arg = SYSCALLS.get() var offset = 0 HEAP16[(arg + offset) >> 1] = 2 return 0 } case 6: case 7: return 0 case 16: case 8: return -28 case 9: setErrNo(28) return -1 default: { return -28 } } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_getcwd(buf, size) { try { if (size === 0) return -28 var cwd = FS.cwd() var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1 if (size < cwdLengthInBytes) return -68 stringToUTF8(cwd, buf, size) return cwdLengthInBytes } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_ioctl(fd, op, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (op) { case 21509: case 21505: { if (!stream.tty) return -59 return 0 } case 21510: case 21511: case 21512: case 21506: case 21507: case 21508: { if (!stream.tty) return -59 return 0 } case 21519: { if (!stream.tty) return -59 var argp = SYSCALLS.get() HEAP32[argp >> 2] = 0 return 0 } case 21520: { if (!stream.tty) return -59 return -28 } case 21531: { var argp = SYSCALLS.get() return FS.ioctl(stream, op, argp) } case 21523: { if (!stream.tty) return -59 return 0 } case 21524: { if (!stream.tty) return -59 return 0 } default: return -28 } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_openat(dirfd, path, flags, varargs) { SYSCALLS.varargs = varargs try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) var mode = varargs ? SYSCALLS.get() : 0 return FS.open(path, flags, mode).fd } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_readlinkat(dirfd, path, buf, bufsize) { try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) if (bufsize <= 0) return -28 var ret = FS.readlink(path) var len = Math.min(bufsize, lengthBytesUTF8(ret)) var endChar = HEAP8[buf + len] stringToUTF8(ret, buf, bufsize + 1) HEAP8[buf + len] = endChar return len } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_stat64(path, buf) { try { path = SYSCALLS.getStr(path) return SYSCALLS.doStat(FS.stat, path, buf) } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function _abort() { abort('') } function _emscripten_memcpy_big(dest, src, num) { HEAPU8.copyWithin(dest, src, src + num) } function getHeapMax() { return 2147483648 } function emscripten_realloc_buffer(size) { try { wasmMemory.grow((size - buffer.byteLength + 65535) >>> 16) updateGlobalBufferAndViews(wasmMemory.buffer) return 1 } catch (e) {} } function _emscripten_resize_heap(requestedSize) { var oldSize = HEAPU8.length requestedSize = requestedSize >>> 0 var maxHeapSize = getHeapMax() if (requestedSize > maxHeapSize) { return false } let alignUp = (x, multiple) => x + ((multiple - (x % multiple)) % multiple) for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown) overGrownHeapSize = Math.min( overGrownHeapSize, requestedSize + 100663296 ) var newSize = Math.min( maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536) ) var replacement = emscripten_realloc_buffer(newSize) if (replacement) { return true } } return false } var ENV = {} function getExecutableName() { return thisProgram || './this.program' } function getEnvStrings() { if (!getEnvStrings.strings) { var lang = ( (typeof navigator == 'object' && navigator.languages && navigator.languages[0]) || 'C' ).replace('-', '_') + '.UTF-8' var env = { USER: 'web_user', LOGNAME: 'web_user', PATH: '/', PWD: '/', HOME: '/home/web_user', LANG: lang, _: getExecutableName(), } for (var x in ENV) { if (ENV[x] === undefined) delete env[x] else env[x] = ENV[x] } var strings = [] for (var x in env) { strings.push(x + '=' + env[x]) } getEnvStrings.strings = strings } return getEnvStrings.strings } function writeAsciiToMemory(str, buffer, dontAddNull) { for (var i = 0; i < str.length; ++i) { HEAP8[buffer++ >> 0] = str.charCodeAt(i) } if (!dontAddNull) HEAP8[buffer >> 0] = 0 } function _environ_get(__environ, environ_buf) { var bufSize = 0 getEnvStrings().forEach(function(string, i) { var ptr = environ_buf + bufSize HEAPU32[(__environ + i * 4) >> 2] = ptr writeAsciiToMemory(string, ptr) bufSize += string.length + 1 }) return 0 } function _environ_sizes_get(penviron_count, penviron_buf_size) { var strings = getEnvStrings() HEAPU32[penviron_count >> 2] = strings.length var bufSize = 0 strings.forEach(function(string) { bufSize += string.length + 1 }) HEAPU32[penviron_buf_size >> 2] = bufSize return 0 } function _proc_exit(code) { EXITSTATUS = code if (!keepRuntimeAlive()) { if (Module['onExit']) Module['onExit'](code) ABORT = true } quit_(code, new ExitStatus(code)) } function exitJS(status, implicit) { EXITSTATUS = status _proc_exit(status) } var _exit = exitJS function _fd_close(fd) { try { var stream = SYSCALLS.getStreamFromFD(fd) FS.close(stream) return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doReadv(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.read(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr if (curr < len) break } return ret } function _fd_read(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doReadv(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function convertI32PairToI53Checked(lo, hi) { return (hi + 2097152) >>> 0 < 4194305 - !!lo ? (lo >>> 0) + hi * 4294967296 : NaN } function _fd_seek(fd, offset_low, offset_high, whence, newOffset) { try { var offset = convertI32PairToI53Checked(offset_low, offset_high) if (isNaN(offset)) return 61 var stream = SYSCALLS.getStreamFromFD(fd) FS.llseek(stream, offset, whence) ;(tempI64 = [ stream.position >>> 0, ((tempDouble = stream.position), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[newOffset >> 2] = tempI64[0]), (HEAP32[(newOffset + 4) >> 2] = tempI64[1]) if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doWritev(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.write(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr } return ret } function _fd_write(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doWritev(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function __isLeapYear(year) { return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) } function __arraySum(array, index) { var sum = 0 for (var i = 0; i <= index; sum += array[i++]) {} return sum } var __MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] var __MONTH_DAYS_REGULAR = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] function __addDays(date, days) { var newDate = new Date(date.getTime()) while (days > 0) { var leap = __isLeapYear(newDate.getFullYear()) var currentMonth = newDate.getMonth() var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth] if (days > daysInCurrentMonth - newDate.getDate()) { days -= daysInCurrentMonth - newDate.getDate() + 1 newDate.setDate(1) if (currentMonth < 11) { newDate.setMonth(currentMonth + 1) } else { newDate.setMonth(0) newDate.setFullYear(newDate.getFullYear() + 1) } } else { newDate.setDate(newDate.getDate() + days) return newDate } } return newDate } function writeArrayToMemory(array, buffer) { HEAP8.set(array, buffer) } function _strftime(s, maxsize, format, tm) { var tm_zone = HEAP32[(tm + 40) >> 2] var date = { tm_sec: HEAP32[tm >> 2], tm_min: HEAP32[(tm + 4) >> 2], tm_hour: HEAP32[(tm + 8) >> 2], tm_mday: HEAP32[(tm + 12) >> 2], tm_mon: HEAP32[(tm + 16) >> 2], tm_year: HEAP32[(tm + 20) >> 2], tm_wday: HEAP32[(tm + 24) >> 2], tm_yday: HEAP32[(tm + 28) >> 2], tm_isdst: HEAP32[(tm + 32) >> 2], tm_gmtoff: HEAP32[(tm + 36) >> 2], tm_zone: tm_zone ? UTF8ToString(tm_zone) : '', } var pattern = UTF8ToString(format) var EXPANSION_RULES_1 = { '%c': '%a %b %d %H:%M:%S %Y', '%D': '%m/%d/%y', '%F': '%Y-%m-%d', '%h': '%b', '%r': '%I:%M:%S %p', '%R': '%H:%M', '%T': '%H:%M:%S', '%x': '%m/%d/%y', '%X': '%H:%M:%S', '%Ec': '%c', '%EC': '%C', '%Ex': '%m/%d/%y', '%EX': '%H:%M:%S', '%Ey': '%y', '%EY': '%Y', '%Od': '%d', '%Oe': '%e', '%OH': '%H', '%OI': '%I', '%Om': '%m', '%OM': '%M', '%OS': '%S', '%Ou': '%u', '%OU': '%U', '%OV': '%V', '%Ow': '%w', '%OW': '%W', '%Oy': '%y', } for (var rule in EXPANSION_RULES_1) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_1[rule] ) } var WEEKDAYS = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', ] var MONTHS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ] function leadingSomething(value, digits, character) { var str = typeof value == 'number' ? value.toString() : value || '' while (str.length < digits) { str = character[0] + str } return str } function leadingNulls(value, digits) { return leadingSomething(value, digits, '0') } function compareByDay(date1, date2) { function sgn(value) { return value < 0 ? -1 : value > 0 ? 1 : 0 } var compare if ((compare = sgn(date1.getFullYear() - date2.getFullYear())) === 0) { if ((compare = sgn(date1.getMonth() - date2.getMonth())) === 0) { compare = sgn(date1.getDate() - date2.getDate()) } } return compare } function getFirstWeekStartDate(janFourth) { switch (janFourth.getDay()) { case 0: return new Date(janFourth.getFullYear() - 1, 11, 29) case 1: return janFourth case 2: return new Date(janFourth.getFullYear(), 0, 3) case 3: return new Date(janFourth.getFullYear(), 0, 2) case 4: return new Date(janFourth.getFullYear(), 0, 1) case 5: return new Date(janFourth.getFullYear() - 1, 11, 31) case 6: return new Date(janFourth.getFullYear() - 1, 11, 30) } } function getWeekBasedYear(date) { var thisDate = __addDays( new Date(date.tm_year + 1900, 0, 1), date.tm_yday ) var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4) var janFourthNextYear = new Date(thisDate.getFullYear() + 1, 0, 4) var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear) var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear) if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { return thisDate.getFullYear() + 1 } return thisDate.getFullYear() } return thisDate.getFullYear() - 1 } var EXPANSION_RULES_2 = { '%a': function(date) { return WEEKDAYS[date.tm_wday].substring(0, 3) }, '%A': function(date) { return WEEKDAYS[date.tm_wday] }, '%b': function(date) { return MONTHS[date.tm_mon].substring(0, 3) }, '%B': function(date) { return MONTHS[date.tm_mon] }, '%C': function(date) { var year = date.tm_year + 1900 return leadingNulls((year / 100) | 0, 2) }, '%d': function(date) { return leadingNulls(date.tm_mday, 2) }, '%e': function(date) { return leadingSomething(date.tm_mday, 2, ' ') }, '%g': function(date) { return getWeekBasedYear(date) .toString() .substring(2) }, '%G': function(date) { return getWeekBasedYear(date) }, '%H': function(date) { return leadingNulls(date.tm_hour, 2) }, '%I': function(date) { var twelveHour = date.tm_hour if (twelveHour == 0) twelveHour = 12 else if (twelveHour > 12) twelveHour -= 12 return leadingNulls(twelveHour, 2) }, '%j': function(date) { return leadingNulls( date.tm_mday + __arraySum( __isLeapYear(date.tm_year + 1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon - 1 ), 3 ) }, '%m': function(date) { return leadingNulls(date.tm_mon + 1, 2) }, '%M': function(date) { return leadingNulls(date.tm_min, 2) }, '%n': function() { return '\n' }, '%p': function(date) { if (date.tm_hour >= 0 && date.tm_hour < 12) { return 'AM' } return 'PM' }, '%S': function(date) { return leadingNulls(date.tm_sec, 2) }, '%t': function() { return '\t' }, '%u': function(date) { return date.tm_wday || 7 }, '%U': function(date) { var days = date.tm_yday + 7 - date.tm_wday return leadingNulls(Math.floor(days / 7), 2) }, '%V': function(date) { var val = Math.floor( (date.tm_yday + 7 - ((date.tm_wday + 6) % 7)) / 7 ) if ((date.tm_wday + 371 - date.tm_yday - 2) % 7 <= 2) { val++ } if (!val) { val = 52 var dec31 = (date.tm_wday + 7 - date.tm_yday - 1) % 7 if ( dec31 == 4 || (dec31 == 5 && __isLeapYear((date.tm_year % 400) - 1)) ) { val++ } } else if (val == 53) { var jan1 = (date.tm_wday + 371 - date.tm_yday) % 7 if (jan1 != 4 && (jan1 != 3 || !__isLeapYear(date.tm_year))) val = 1 } return leadingNulls(val, 2) }, '%w': function(date) { return date.tm_wday }, '%W': function(date) { var days = date.tm_yday + 7 - ((date.tm_wday + 6) % 7) return leadingNulls(Math.floor(days / 7), 2) }, '%y': function(date) { return (date.tm_year + 1900).toString().substring(2) }, '%Y': function(date) { return date.tm_year + 1900 }, '%z': function(date) { var off = date.tm_gmtoff var ahead = off >= 0 off = Math.abs(off) / 60 off = (off / 60) * 100 + (off % 60) return (ahead ? '+' : '-') + String('0000' + off).slice(-4) }, '%Z': function(date) { return date.tm_zone }, '%%': function() { return '%' }, } pattern = pattern.replace(/%%/g, '\0\0') for (var rule in EXPANSION_RULES_2) { if (pattern.includes(rule)) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date) ) } } pattern = pattern.replace(/\0\0/g, '%') var bytes = intArrayFromString(pattern, false) if (bytes.length > maxsize) { return 0 } writeArrayToMemory(bytes, s) return bytes.length - 1 } function _strftime_l(s, maxsize, format, tm, loc) { return _strftime(s, maxsize, format, tm) } function handleException(e) { if (e instanceof ExitStatus || e == 'unwind') { return EXITSTATUS } quit_(1, e) } function allocateUTF8OnStack(str) { var size = lengthBytesUTF8(str) + 1 var ret = stackAlloc(size) stringToUTF8Array(str, HEAP8, ret, size) return ret } function getCFunc(ident) { var func = Module['_' + ident] return func } function ccall(ident, returnType, argTypes, args, opts) { var toC = { string: str => { var ret = 0 if (str !== null && str !== undefined && str !== 0) { var len = (str.length << 2) + 1 ret = stackAlloc(len) stringToUTF8(str, ret, len) } return ret }, array: arr => { var ret = stackAlloc(arr.length) writeArrayToMemory(arr, ret) return ret }, } function convertReturnValue(ret) { if (returnType === 'string') { return UTF8ToString(ret) } if (returnType === 'boolean') return Boolean(ret) return ret } var func = getCFunc(ident) var cArgs = [] var stack = 0 if (args) { for (var i = 0; i < args.length; i++) { var converter = toC[argTypes[i]] if (converter) { if (stack === 0) stack = stackSave() cArgs[i] = converter(args[i]) } else { cArgs[i] = args[i] } } } var ret = func.apply(null, cArgs) function onDone(ret) { if (stack !== 0) stackRestore(stack) return convertReturnValue(ret) } ret = onDone(ret) return ret } function cwrap(ident, returnType, argTypes, opts) { argTypes = argTypes || [] var numericArgs = argTypes.every( type => type === 'number' || type === 'boolean' ) var numericRet = returnType !== 'string' if (numericRet && numericArgs && !opts) { return getCFunc(ident) } return function() { return ccall(ident, returnType, argTypes, arguments, opts) } } function AsciiToString(ptr) { var str = '' while (1) { var ch = HEAPU8[ptr++ >> 0] if (!ch) return str str += String.fromCharCode(ch) } } var FSNode = function(parent, name, mode, rdev) { if (!parent) { parent = this } this.parent = parent this.mount = parent.mount this.mounted = null this.id = FS.nextInode++ this.name = name this.mode = mode this.node_ops = {} this.stream_ops = {} this.rdev = rdev } var readMode = 292 | 73 var writeMode = 146 Object.defineProperties(FSNode.prototype, { read: { get: function() { return (this.mode & readMode) === readMode }, set: function(val) { val ? (this.mode |= readMode) : (this.mode &= ~readMode) }, }, write: { get: function() { return (this.mode & writeMode) === writeMode }, set: function(val) { val ? (this.mode |= writeMode) : (this.mode &= ~writeMode) }, }, isFolder: { get: function() { return FS.isDir(this.mode) }, }, isDevice: { get: function() { return FS.isChrdev(this.mode) }, }, }) FS.FSNode = FSNode FS.staticInit() Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_unlink'] = FS.unlink Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice if (ENVIRONMENT_IS_NODE) { NODEFS.staticInit() } ERRNO_CODES = { EPERM: 63, ENOENT: 44, ESRCH: 71, EINTR: 27, EIO: 29, ENXIO: 60, E2BIG: 1, ENOEXEC: 45, EBADF: 8, ECHILD: 12, EAGAIN: 6, EWOULDBLOCK: 6, ENOMEM: 48, EACCES: 2, EFAULT: 21, ENOTBLK: 105, EBUSY: 10, EEXIST: 20, EXDEV: 75, ENODEV: 43, ENOTDIR: 54, EISDIR: 31, EINVAL: 28, ENFILE: 41, EMFILE: 33, ENOTTY: 59, ETXTBSY: 74, EFBIG: 22, ENOSPC: 51, ESPIPE: 70, EROFS: 69, EMLINK: 34, EPIPE: 64, EDOM: 18, ERANGE: 68, ENOMSG: 49, EIDRM: 24, ECHRNG: 106, EL2NSYNC: 156, EL3HLT: 107, EL3RST: 108, ELNRNG: 109, EUNATCH: 110, ENOCSI: 111, EL2HLT: 112, EDEADLK: 16, ENOLCK: 46, EBADE: 113, EBADR: 114, EXFULL: 115, ENOANO: 104, EBADRQC: 103, EBADSLT: 102, EDEADLOCK: 16, EBFONT: 101, ENOSTR: 100, ENODATA: 116, ETIME: 117, ENOSR: 118, ENONET: 119, ENOPKG: 120, EREMOTE: 121, ENOLINK: 47, EADV: 122, ESRMNT: 123, ECOMM: 124, EPROTO: 65, EMULTIHOP: 36, EDOTDOT: 125, EBADMSG: 9, ENOTUNIQ: 126, EBADFD: 127, EREMCHG: 128, ELIBACC: 129, ELIBBAD: 130, ELIBSCN: 131, ELIBMAX: 132, ELIBEXEC: 133, ENOSYS: 52, ENOTEMPTY: 55, ENAMETOOLONG: 37, ELOOP: 32, EOPNOTSUPP: 138, EPFNOSUPPORT: 139, ECONNRESET: 15, ENOBUFS: 42, EAFNOSUPPORT: 5, EPROTOTYPE: 67, ENOTSOCK: 57, ENOPROTOOPT: 50, ESHUTDOWN: 140, ECONNREFUSED: 14, EADDRINUSE: 3, ECONNABORTED: 13, ENETUNREACH: 40, ENETDOWN: 38, ETIMEDOUT: 73, EHOSTDOWN: 142, EHOSTUNREACH: 23, EINPROGRESS: 26, EALREADY: 7, EDESTADDRREQ: 17, EMSGSIZE: 35, EPROTONOSUPPORT: 66, ESOCKTNOSUPPORT: 137, EADDRNOTAVAIL: 4, ENETRESET: 39, EISCONN: 30, ENOTCONN: 53, ETOOMANYREFS: 141, EUSERS: 136, EDQUOT: 19, ESTALE: 72, ENOTSUP: 138, ENOMEDIUM: 148, EILSEQ: 25, EOVERFLOW: 61, ECANCELED: 11, ENOTRECOVERABLE: 56, EOWNERDEAD: 62, ESTRPIPE: 135, } var asmLibraryArg = { b: ___cxa_throw, d: ___syscall_fcntl64, r: ___syscall_getcwd, i: ___syscall_ioctl, j: ___syscall_openat, n: ___syscall_readlinkat, o: ___syscall_stat64, c: _abort, f: _emscripten_memcpy_big, m: _emscripten_resize_heap, p: _environ_get, q: _environ_sizes_get, a: _exit, e: _fd_close, h: _fd_read, k: _fd_seek, g: _fd_write, l: _strftime_l, } var asm = createWasm() var ___wasm_call_ctors = (Module['___wasm_call_ctors'] = function() { return (___wasm_call_ctors = Module['___wasm_call_ctors'] = Module['asm']['t']).apply(null, arguments) }) var _main = (Module['_main'] = function() { return (_main = Module['_main'] = Module['asm']['u']).apply( null, arguments ) }) var ___errno_location = (Module['___errno_location'] = function() { return (___errno_location = Module['___errno_location'] = Module['asm']['v']).apply(null, arguments) }) var _itk_wasm_input_array_alloc = (Module[ '_itk_wasm_input_array_alloc' ] = function() { return (_itk_wasm_input_array_alloc = Module[ '_itk_wasm_input_array_alloc' ] = Module['asm']['w']).apply(null, arguments) }) var _itk_wasm_input_json_alloc = (Module[ '_itk_wasm_input_json_alloc' ] = function() { return (_itk_wasm_input_json_alloc = Module[ '_itk_wasm_input_json_alloc' ] = Module['asm']['x']).apply(null, arguments) }) var _itk_wasm_output_json_address = (Module[ '_itk_wasm_output_json_address' ] = function() { return (_itk_wasm_output_json_address = Module[ '_itk_wasm_output_json_address' ] = Module['asm']['y']).apply(null, arguments) }) var _itk_wasm_output_json_size = (Module[ '_itk_wasm_output_json_size' ] = function() { return (_itk_wasm_output_json_size = Module[ '_itk_wasm_output_json_size' ] = Module['asm']['z']).apply(null, arguments) }) var _itk_wasm_output_array_address = (Module[ '_itk_wasm_output_array_address' ] = function() { return (_itk_wasm_output_array_address = Module[ '_itk_wasm_output_array_address' ] = Module['asm']['A']).apply(null, arguments) }) var _itk_wasm_output_array_size = (Module[ '_itk_wasm_output_array_size' ] = function() { return (_itk_wasm_output_array_size = Module[ '_itk_wasm_output_array_size' ] = Module['asm']['B']).apply(null, arguments) }) var _itk_wasm_free_all = (Module['_itk_wasm_free_all'] = function() { return (_itk_wasm_free_all = Module['_itk_wasm_free_all'] = Module['asm']['C']).apply(null, arguments) }) var stackSave = (Module['stackSave'] = function() { return (stackSave = Module['stackSave'] = Module['asm']['E']).apply( null, arguments ) }) var stackRestore = (Module['stackRestore'] = function() { return (stackRestore = Module['stackRestore'] = Module['asm']['F']).apply( null, arguments ) }) var stackAlloc = (Module['stackAlloc'] = function() { return (stackAlloc = Module['stackAlloc'] = Module['asm']['G']).apply( null, arguments ) }) var ___cxa_is_pointer_type = (Module[ '___cxa_is_pointer_type' ] = function() { return (___cxa_is_pointer_type = Module['___cxa_is_pointer_type'] = Module['asm']['H']).apply(null, arguments) }) Module['addRunDependency'] = addRunDependency Module['removeRunDependency'] = removeRunDependency Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice Module['FS_unlink'] = FS.unlink Module['callMain'] = callMain Module['ccall'] = ccall Module['cwrap'] = cwrap Module['AsciiToString'] = AsciiToString Module['writeArrayToMemory'] = writeArrayToMemory Module['writeAsciiToMemory'] = writeAsciiToMemory var calledRun dependenciesFulfilled = function runCaller() { if (!calledRun) run() if (!calledRun) dependenciesFulfilled = runCaller } function callMain(args) { var entryFunction = Module['_main'] args = args || [] args.unshift(thisProgram) var argc = args.length var argv = stackAlloc((argc + 1) * 4) var argv_ptr = argv >> 2 args.forEach(arg => { HEAP32[argv_ptr++] = allocateUTF8OnStack(arg) }) HEAP32[argv_ptr] = 0 try { var ret = entryFunction(argc, argv) exitJS(ret, true) return ret } catch (e) { return handleException(e) } } function run(args) { args = args || arguments_ if (runDependencies > 0) { return } preRun() if (runDependencies > 0) { return } function doRun() { if (calledRun) return calledRun = true Module['calledRun'] = true if (ABORT) return initRuntime() preMain() readyPromiseResolve(Module) if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized']() if (shouldRunNow) callMain(args) postRun() } if (Module['setStatus']) { Module['setStatus']('Running...') setTimeout(function() { setTimeout(function() { Module['setStatus']('') }, 1) doRun() }, 1) } else { doRun() } } if (Module['preInit']) { if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']] while (Module['preInit'].length > 0) { Module['preInit'].pop()() } } var shouldRunNow = false if (Module['noInitialRun']) shouldRunNow = false run() Module.mountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) if (FS.isDir(containingDir) || containingDir === '/') { return } var currentDir = '/' var splitContainingDir = containingDir.split(path.sep) for (var ii = 1; ii < splitContainingDir.length; ii++) { currentDir += splitContainingDir[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } FS.mount(NODEFS, { root: containingDir }, currentDir) return currentDir + path.basename(filePath) } Module.unmountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) FS.unmount(containingDir) } Module.fs_mkdirs = function(dirs) { var currentDir = '/' var splitDirs = dirs.split('/') for (var ii = 1; ii < splitDirs.length; ++ii) { currentDir += splitDirs[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } } Module.fs_readFile = function(path, opts) { return FS.readFile(path, opts) } Module.fs_writeFile = function(path, data, opts) { return FS.writeFile(path, data, opts) } Module.fs_unlink = function(path) { return FS.unlink(path) } Module.fs_open = function(path, flags, mode) { return FS.open(path, flags, mode) } Module.fs_stat = function(path) { return FS.stat(path) } Module.fs_read = function(stream, buffer, offset, length, position) { return FS.read(stream, buffer, offset, length, position) } Module.fs_close = function(stream) { return FS.close(stream) } return Compare.ready } })() export default Compare ================================================ FILE: src/IO/Compare/emscripten-build/Compare.umd.js ================================================ var Compare = (() => { var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename return function(Compare) { Compare = Compare || {} var Module = typeof Compare != 'undefined' ? Compare : {} var readyPromiseResolve, readyPromiseReject Module['ready'] = new Promise(function(resolve, reject) { readyPromiseResolve = resolve readyPromiseReject = reject }) var mStdout = null var mStderr = null Module['resetModuleStdout'] = function() { mStdout = '' } Module['resetModuleStderr'] = function() { mStderr = '' } Module['print'] = function(text) { console.log(text) mStdout += text + '\n' } Module['printErr'] = function(text) { console.error(text) mStderr += text + '\n' } Module['getModuleStdout'] = function() { return mStdout } Module['getModuleStderr'] = function() { return mStderr } var moduleOverrides = Object.assign({}, Module) var arguments_ = [] var thisProgram = './this.program' var quit_ = (status, toThrow) => { throw toThrow } var ENVIRONMENT_IS_WEB = typeof window == 'object' var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function' var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string' var scriptDirectory = '' function locateFile(path) { if (Module['locateFile']) { return Module['locateFile'](path, scriptDirectory) } return scriptDirectory + path } var read_, readAsync, readBinary, setWindowTitle function logExceptionOnExit(e) { if (e instanceof ExitStatus) return let toLog = e err('exiting due to exception: ' + toLog) } if (ENVIRONMENT_IS_NODE) { var fs = require('fs') var nodePath = require('path') if (ENVIRONMENT_IS_WORKER) { scriptDirectory = nodePath.dirname(scriptDirectory) + '/' } else { scriptDirectory = __dirname + '/' } read_ = (filename, binary) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) return fs.readFileSync(filename, binary ? undefined : 'utf8') } readBinary = filename => { var ret = read_(filename, true) if (!ret.buffer) { ret = new Uint8Array(ret) } return ret } readAsync = (filename, onload, onerror) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) fs.readFile(filename, function(err, data) { if (err) onerror(err) else onload(data.buffer) }) } if (process['argv'].length > 1) { thisProgram = process['argv'][1].replace(/\\/g, '/') } arguments_ = process['argv'].slice(2) process['on']('uncaughtException', function(ex) { if (!(ex instanceof ExitStatus)) { throw ex } }) process['on']('unhandledRejection', function(reason) { throw reason }) quit_ = (status, toThrow) => { if (keepRuntimeAlive()) { process['exitCode'] = status throw toThrow } logExceptionOnExit(toThrow) process['exit'](status) } Module['inspect'] = function() { return '[Emscripten Module object]' } } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = self.location.href } else if (typeof document != 'undefined' && document.currentScript) { scriptDirectory = document.currentScript.src } if (_scriptDir) { scriptDirectory = _scriptDir } if (scriptDirectory.indexOf('blob:') !== 0) { scriptDirectory = scriptDirectory.substr( 0, scriptDirectory.replace(/[?#].*/, '').lastIndexOf('/') + 1 ) } else { scriptDirectory = '' } { read_ = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.send(null) return xhr.responseText } if (ENVIRONMENT_IS_WORKER) { readBinary = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.responseType = 'arraybuffer' xhr.send(null) return new Uint8Array(xhr.response) } } readAsync = (url, onload, onerror) => { var xhr = new XMLHttpRequest() xhr.open('GET', url, true) xhr.responseType = 'arraybuffer' xhr.onload = () => { if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { onload(xhr.response) return } onerror() } xhr.onerror = onerror xhr.send(null) } } setWindowTitle = title => (document.title = title) } else { } var out = Module['print'] || console.log.bind(console) var err = Module['printErr'] || console.warn.bind(console) Object.assign(Module, moduleOverrides) moduleOverrides = null if (Module['arguments']) arguments_ = Module['arguments'] if (Module['thisProgram']) thisProgram = Module['thisProgram'] if (Module['quit']) quit_ = Module['quit'] var wasmBinary if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'] var noExitRuntime = Module['noExitRuntime'] || true if (typeof WebAssembly != 'object') { abort('no native wasm support detected') } var wasmMemory var ABORT = false var EXITSTATUS function assert(condition, text) { if (!condition) { abort(text) } } var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { var endIdx = idx + maxBytesToRead var endPtr = idx while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)) } var str = '' while (idx < endPtr) { var u0 = heapOrArray[idx++] if (!(u0 & 128)) { str += String.fromCharCode(u0) continue } var u1 = heapOrArray[idx++] & 63 if ((u0 & 224) == 192) { str += String.fromCharCode(((u0 & 31) << 6) | u1) continue } var u2 = heapOrArray[idx++] & 63 if ((u0 & 240) == 224) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2 } else { u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63) } if (u0 < 65536) { str += String.fromCharCode(u0) } else { var ch = u0 - 65536 str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)) } } return str } function UTF8ToString(ptr, maxBytesToRead) { return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '' } function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { if (!(maxBytesToWrite > 0)) return 0 var startIdx = outIdx var endIdx = outIdx + maxBytesToWrite - 1 for (var i = 0; i < str.length; ++i) { var u = str.charCodeAt(i) if (u >= 55296 && u <= 57343) { var u1 = str.charCodeAt(++i) u = (65536 + ((u & 1023) << 10)) | (u1 & 1023) } if (u <= 127) { if (outIdx >= endIdx) break heap[outIdx++] = u } else if (u <= 2047) { if (outIdx + 1 >= endIdx) break heap[outIdx++] = 192 | (u >> 6) heap[outIdx++] = 128 | (u & 63) } else if (u <= 65535) { if (outIdx + 2 >= endIdx) break heap[outIdx++] = 224 | (u >> 12) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } else { if (outIdx + 3 >= endIdx) break heap[outIdx++] = 240 | (u >> 18) heap[outIdx++] = 128 | ((u >> 12) & 63) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } } heap[outIdx] = 0 return outIdx - startIdx } function stringToUTF8(str, outPtr, maxBytesToWrite) { return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite) } function lengthBytesUTF8(str) { var len = 0 for (var i = 0; i < str.length; ++i) { var c = str.charCodeAt(i) if (c <= 127) { len++ } else if (c <= 2047) { len += 2 } else if (c >= 55296 && c <= 57343) { len += 4 ++i } else { len += 3 } } return len } var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64 function updateGlobalBufferAndViews(buf) { buffer = buf Module['HEAP8'] = HEAP8 = new Int8Array(buf) Module['HEAP16'] = HEAP16 = new Int16Array(buf) Module['HEAP32'] = HEAP32 = new Int32Array(buf) Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf) Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf) Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf) Module['HEAPF32'] = HEAPF32 = new Float32Array(buf) Module['HEAPF64'] = HEAPF64 = new Float64Array(buf) } var INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 16777216 var wasmTable var __ATPRERUN__ = [] var __ATINIT__ = [] var __ATMAIN__ = [] var __ATPOSTRUN__ = [] var runtimeInitialized = false function keepRuntimeAlive() { return noExitRuntime } function preRun() { if (Module['preRun']) { if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']] while (Module['preRun'].length) { addOnPreRun(Module['preRun'].shift()) } } callRuntimeCallbacks(__ATPRERUN__) } function initRuntime() { runtimeInitialized = true if (!Module['noFSInit'] && !FS.init.initialized) FS.init() FS.ignorePermissions = false TTY.init() callRuntimeCallbacks(__ATINIT__) } function preMain() { callRuntimeCallbacks(__ATMAIN__) } function postRun() { if (Module['postRun']) { if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']] while (Module['postRun'].length) { addOnPostRun(Module['postRun'].shift()) } } callRuntimeCallbacks(__ATPOSTRUN__) } function addOnPreRun(cb) { __ATPRERUN__.unshift(cb) } function addOnInit(cb) { __ATINIT__.unshift(cb) } function addOnPostRun(cb) { __ATPOSTRUN__.unshift(cb) } var runDependencies = 0 var runDependencyWatcher = null var dependenciesFulfilled = null function getUniqueRunDependency(id) { return id } function addRunDependency(id) { runDependencies++ if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } } function removeRunDependency(id) { runDependencies-- if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } if (runDependencies == 0) { if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher) runDependencyWatcher = null } if (dependenciesFulfilled) { var callback = dependenciesFulfilled dependenciesFulfilled = null callback() } } } function abort(what) { if (Module['onAbort']) { Module['onAbort'](what) } what = 'Aborted(' + what + ')' err(what) ABORT = true EXITSTATUS = 1 what += '. Build with -sASSERTIONS for more info.' var e = new WebAssembly.RuntimeError(what) readyPromiseReject(e) throw e } var dataURIPrefix = 'data:application/octet-stream;base64,' function isDataURI(filename) { return filename.startsWith(dataURIPrefix) } function isFileURI(filename) { return filename.startsWith('file://') } var wasmBinaryFile wasmBinaryFile = 'Compare.umd.wasm' if (!isDataURI(wasmBinaryFile)) { wasmBinaryFile = locateFile(wasmBinaryFile) } function getBinary(file) { try { if (file == wasmBinaryFile && wasmBinary) { return new Uint8Array(wasmBinary) } if (readBinary) { return readBinary(file) } throw 'both async and sync fetching of the wasm failed' } catch (err) { abort(err) } } function getBinaryPromise() { if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { if (typeof fetch == 'function' && !isFileURI(wasmBinaryFile)) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }) .then(function(response) { if (!response['ok']) { throw "failed to load wasm binary file at '" + wasmBinaryFile + "'" } return response['arrayBuffer']() }) .catch(function() { return getBinary(wasmBinaryFile) }) } else { if (readAsync) { return new Promise(function(resolve, reject) { readAsync( wasmBinaryFile, function(response) { resolve(new Uint8Array(response)) }, reject ) }) } } } return Promise.resolve().then(function() { return getBinary(wasmBinaryFile) }) } function createWasm() { var info = { a: asmLibraryArg } function receiveInstance(instance, module) { var exports = instance.exports Module['asm'] = exports wasmMemory = Module['asm']['s'] updateGlobalBufferAndViews(wasmMemory.buffer) wasmTable = Module['asm']['D'] addOnInit(Module['asm']['t']) removeRunDependency('wasm-instantiate') } addRunDependency('wasm-instantiate') function receiveInstantiationResult(result) { receiveInstance(result['instance']) } function instantiateArrayBuffer(receiver) { return getBinaryPromise() .then(function(binary) { return WebAssembly.instantiate(binary, info) }) .then(function(instance) { return instance }) .then(receiver, function(reason) { err('failed to asynchronously prepare wasm: ' + reason) abort(reason) }) } function instantiateAsync() { if ( !wasmBinary && typeof WebAssembly.instantiateStreaming == 'function' && !isDataURI(wasmBinaryFile) && !isFileURI(wasmBinaryFile) && !ENVIRONMENT_IS_NODE && typeof fetch == 'function' ) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then( function(response) { var result = WebAssembly.instantiateStreaming(response, info) return result.then(receiveInstantiationResult, function(reason) { err('wasm streaming compile failed: ' + reason) err('falling back to ArrayBuffer instantiation') return instantiateArrayBuffer(receiveInstantiationResult) }) } ) } else { return instantiateArrayBuffer(receiveInstantiationResult) } } if (Module['instantiateWasm']) { try { var exports = Module['instantiateWasm'](info, receiveInstance) return exports } catch (e) { err('Module.instantiateWasm callback failed with error: ' + e) readyPromiseReject(e) } } instantiateAsync().catch(readyPromiseReject) return {} } var tempDouble var tempI64 function ExitStatus(status) { this.name = 'ExitStatus' this.message = 'Program terminated with exit(' + status + ')' this.status = status } function callRuntimeCallbacks(callbacks) { while (callbacks.length > 0) { callbacks.shift()(Module) } } function ExceptionInfo(excPtr) { this.excPtr = excPtr this.ptr = excPtr - 24 this.set_type = function(type) { HEAPU32[(this.ptr + 4) >> 2] = type } this.get_type = function() { return HEAPU32[(this.ptr + 4) >> 2] } this.set_destructor = function(destructor) { HEAPU32[(this.ptr + 8) >> 2] = destructor } this.get_destructor = function() { return HEAPU32[(this.ptr + 8) >> 2] } this.set_refcount = function(refcount) { HEAP32[this.ptr >> 2] = refcount } this.set_caught = function(caught) { caught = caught ? 1 : 0 HEAP8[(this.ptr + 12) >> 0] = caught } this.get_caught = function() { return HEAP8[(this.ptr + 12) >> 0] != 0 } this.set_rethrown = function(rethrown) { rethrown = rethrown ? 1 : 0 HEAP8[(this.ptr + 13) >> 0] = rethrown } this.get_rethrown = function() { return HEAP8[(this.ptr + 13) >> 0] != 0 } this.init = function(type, destructor) { this.set_adjusted_ptr(0) this.set_type(type) this.set_destructor(destructor) this.set_refcount(0) this.set_caught(false) this.set_rethrown(false) } this.add_ref = function() { var value = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = value + 1 } this.release_ref = function() { var prev = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = prev - 1 return prev === 1 } this.set_adjusted_ptr = function(adjustedPtr) { HEAPU32[(this.ptr + 16) >> 2] = adjustedPtr } this.get_adjusted_ptr = function() { return HEAPU32[(this.ptr + 16) >> 2] } this.get_exception_ptr = function() { var isPointer = ___cxa_is_pointer_type(this.get_type()) if (isPointer) { return HEAPU32[this.excPtr >> 2] } var adjusted = this.get_adjusted_ptr() if (adjusted !== 0) return adjusted return this.excPtr } } var exceptionLast = 0 var uncaughtExceptionCount = 0 function ___cxa_throw(ptr, type, destructor) { var info = new ExceptionInfo(ptr) info.init(type, destructor) exceptionLast = ptr uncaughtExceptionCount++ throw ptr } function setErrNo(value) { HEAP32[___errno_location() >> 2] = value return value } var PATH = { isAbs: path => path.charAt(0) === '/', splitPath: filename => { var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/ return splitPathRe.exec(filename).slice(1) }, normalizeArray: (parts, allowAboveRoot) => { var up = 0 for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i] if (last === '.') { parts.splice(i, 1) } else if (last === '..') { parts.splice(i, 1) up++ } else if (up) { parts.splice(i, 1) up-- } } if (allowAboveRoot) { for (; up; up--) { parts.unshift('..') } } return parts }, normalize: path => { var isAbsolute = PATH.isAbs(path), trailingSlash = path.substr(-1) === '/' path = PATH.normalizeArray( path.split('/').filter(p => !!p), !isAbsolute ).join('/') if (!path && !isAbsolute) { path = '.' } if (path && trailingSlash) { path += '/' } return (isAbsolute ? '/' : '') + path }, dirname: path => { var result = PATH.splitPath(path), root = result[0], dir = result[1] if (!root && !dir) { return '.' } if (dir) { dir = dir.substr(0, dir.length - 1) } return root + dir }, basename: path => { if (path === '/') return '/' path = PATH.normalize(path) path = path.replace(/\/$/, '') var lastSlash = path.lastIndexOf('/') if (lastSlash === -1) return path return path.substr(lastSlash + 1) }, join: function() { var paths = Array.prototype.slice.call(arguments) return PATH.normalize(paths.join('/')) }, join2: (l, r) => { return PATH.normalize(l + '/' + r) }, } function getRandomDevice() { if ( typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function' ) { var randomBuffer = new Uint8Array(1) return () => { crypto.getRandomValues(randomBuffer) return randomBuffer[0] } } else if (ENVIRONMENT_IS_NODE) { try { var crypto_module = require('crypto') return () => crypto_module['randomBytes'](1)[0] } catch (e) {} } return () => abort('randomDevice') } var PATH_FS = { resolve: function() { var resolvedPath = '', resolvedAbsolute = false for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = i >= 0 ? arguments[i] : FS.cwd() if (typeof path != 'string') { throw new TypeError('Arguments to path.resolve must be strings') } else if (!path) { return '' } resolvedPath = path + '/' + resolvedPath resolvedAbsolute = PATH.isAbs(path) } resolvedPath = PATH.normalizeArray( resolvedPath.split('/').filter(p => !!p), !resolvedAbsolute ).join('/') return (resolvedAbsolute ? '/' : '') + resolvedPath || '.' }, relative: (from, to) => { from = PATH_FS.resolve(from).substr(1) to = PATH_FS.resolve(to).substr(1) function trim(arr) { var start = 0 for (; start < arr.length; start++) { if (arr[start] !== '') break } var end = arr.length - 1 for (; end >= 0; end--) { if (arr[end] !== '') break } if (start > end) return [] return arr.slice(start, end - start + 1) } var fromParts = trim(from.split('/')) var toParts = trim(to.split('/')) var length = Math.min(fromParts.length, toParts.length) var samePartsLength = length for (var i = 0; i < length; i++) { if (fromParts[i] !== toParts[i]) { samePartsLength = i break } } var outputParts = [] for (var i = samePartsLength; i < fromParts.length; i++) { outputParts.push('..') } outputParts = outputParts.concat(toParts.slice(samePartsLength)) return outputParts.join('/') }, } function intArrayFromString(stringy, dontAddNull, length) { var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1 var u8array = new Array(len) var numBytesWritten = stringToUTF8Array( stringy, u8array, 0, u8array.length ) if (dontAddNull) u8array.length = numBytesWritten return u8array } var TTY = { ttys: [], init: function() {}, shutdown: function() {}, register: function(dev, ops) { TTY.ttys[dev] = { input: [], output: [], ops: ops } FS.registerDevice(dev, TTY.stream_ops) }, stream_ops: { open: function(stream) { var tty = TTY.ttys[stream.node.rdev] if (!tty) { throw new FS.ErrnoError(43) } stream.tty = tty stream.seekable = false }, close: function(stream) { stream.tty.ops.fsync(stream.tty) }, fsync: function(stream) { stream.tty.ops.fsync(stream.tty) }, read: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.get_char) { throw new FS.ErrnoError(60) } var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = stream.tty.ops.get_char(stream.tty) } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.put_char) { throw new FS.ErrnoError(60) } try { for (var i = 0; i < length; i++) { stream.tty.ops.put_char(stream.tty, buffer[offset + i]) } } catch (e) { throw new FS.ErrnoError(29) } if (length) { stream.node.timestamp = Date.now() } return i }, }, default_tty_ops: { get_char: function(tty) { if (!tty.input.length) { var result = null if (ENVIRONMENT_IS_NODE) { var BUFSIZE = 256 var buf = Buffer.alloc(BUFSIZE) var bytesRead = 0 try { bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE, -1) } catch (e) { if (e.toString().includes('EOF')) bytesRead = 0 else throw e } if (bytesRead > 0) { result = buf.slice(0, bytesRead).toString('utf-8') } else { result = null } } else if ( typeof window != 'undefined' && typeof window.prompt == 'function' ) { result = window.prompt('Input: ') if (result !== null) { result += '\n' } } else if (typeof readline == 'function') { result = readline() if (result !== null) { result += '\n' } } if (!result) { return null } tty.input = intArrayFromString(result, true) } return tty.input.shift() }, put_char: function(tty, val) { if (val === null || val === 10) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, default_tty1_ops: { put_char: function(tty, val) { if (val === null || val === 10) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, } function mmapAlloc(size) { abort() } var MEMFS = { ops_table: null, mount: function(mount) { return MEMFS.createNode(null, '/', 16384 | 511, 0) }, createNode: function(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { throw new FS.ErrnoError(63) } if (!MEMFS.ops_table) { MEMFS.ops_table = { dir: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, lookup: MEMFS.node_ops.lookup, mknod: MEMFS.node_ops.mknod, rename: MEMFS.node_ops.rename, unlink: MEMFS.node_ops.unlink, rmdir: MEMFS.node_ops.rmdir, readdir: MEMFS.node_ops.readdir, symlink: MEMFS.node_ops.symlink, }, stream: { llseek: MEMFS.stream_ops.llseek }, }, file: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: { llseek: MEMFS.stream_ops.llseek, read: MEMFS.stream_ops.read, write: MEMFS.stream_ops.write, allocate: MEMFS.stream_ops.allocate, mmap: MEMFS.stream_ops.mmap, msync: MEMFS.stream_ops.msync, }, }, link: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, readlink: MEMFS.node_ops.readlink, }, stream: {}, }, chrdev: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: FS.chrdev_stream_ops, }, } } var node = FS.createNode(parent, name, mode, dev) if (FS.isDir(node.mode)) { node.node_ops = MEMFS.ops_table.dir.node node.stream_ops = MEMFS.ops_table.dir.stream node.contents = {} } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node node.stream_ops = MEMFS.ops_table.file.stream node.usedBytes = 0 node.contents = null } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node node.stream_ops = MEMFS.ops_table.link.stream } else if (FS.isChrdev(node.mode)) { node.node_ops = MEMFS.ops_table.chrdev.node node.stream_ops = MEMFS.ops_table.chrdev.stream } node.timestamp = Date.now() if (parent) { parent.contents[name] = node parent.timestamp = node.timestamp } return node }, getFileDataAsTypedArray: function(node) { if (!node.contents) return new Uint8Array(0) if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes) return new Uint8Array(node.contents) }, expandFileStorage: function(node, newCapacity) { var prevCapacity = node.contents ? node.contents.length : 0 if (prevCapacity >= newCapacity) return var CAPACITY_DOUBLING_MAX = 1024 * 1024 newCapacity = Math.max( newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2 : 1.125)) >>> 0 ) if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256) var oldContents = node.contents node.contents = new Uint8Array(newCapacity) if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0) }, resizeFileStorage: function(node, newSize) { if (node.usedBytes == newSize) return if (newSize == 0) { node.contents = null node.usedBytes = 0 } else { var oldContents = node.contents node.contents = new Uint8Array(newSize) if (oldContents) { node.contents.set( oldContents.subarray(0, Math.min(newSize, node.usedBytes)) ) } node.usedBytes = newSize } }, node_ops: { getattr: function(node) { var attr = {} attr.dev = FS.isChrdev(node.mode) ? node.id : 1 attr.ino = node.id attr.mode = node.mode attr.nlink = 1 attr.uid = 0 attr.gid = 0 attr.rdev = node.rdev if (FS.isDir(node.mode)) { attr.size = 4096 } else if (FS.isFile(node.mode)) { attr.size = node.usedBytes } else if (FS.isLink(node.mode)) { attr.size = node.link.length } else { attr.size = 0 } attr.atime = new Date(node.timestamp) attr.mtime = new Date(node.timestamp) attr.ctime = new Date(node.timestamp) attr.blksize = 4096 attr.blocks = Math.ceil(attr.size / attr.blksize) return attr }, setattr: function(node, attr) { if (attr.mode !== undefined) { node.mode = attr.mode } if (attr.timestamp !== undefined) { node.timestamp = attr.timestamp } if (attr.size !== undefined) { MEMFS.resizeFileStorage(node, attr.size) } }, lookup: function(parent, name) { throw FS.genericErrors[44] }, mknod: function(parent, name, mode, dev) { return MEMFS.createNode(parent, name, mode, dev) }, rename: function(old_node, new_dir, new_name) { if (FS.isDir(old_node.mode)) { var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (new_node) { for (var i in new_node.contents) { throw new FS.ErrnoError(55) } } } delete old_node.parent.contents[old_node.name] old_node.parent.timestamp = Date.now() old_node.name = new_name new_dir.contents[new_name] = old_node new_dir.timestamp = old_node.parent.timestamp old_node.parent = new_dir }, unlink: function(parent, name) { delete parent.contents[name] parent.timestamp = Date.now() }, rmdir: function(parent, name) { var node = FS.lookupNode(parent, name) for (var i in node.contents) { throw new FS.ErrnoError(55) } delete parent.contents[name] parent.timestamp = Date.now() }, readdir: function(node) { var entries = ['.', '..'] for (var key in node.contents) { if (!node.contents.hasOwnProperty(key)) { continue } entries.push(key) } return entries }, symlink: function(parent, newname, oldpath) { var node = MEMFS.createNode(parent, newname, 511 | 40960, 0) node.link = oldpath return node }, readlink: function(node) { if (!FS.isLink(node.mode)) { throw new FS.ErrnoError(28) } return node.link }, }, stream_ops: { read: function(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= stream.node.usedBytes) return 0 var size = Math.min(stream.node.usedBytes - position, length) if (size > 8 && contents.subarray) { buffer.set(contents.subarray(position, position + size), offset) } else { for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i] } return size }, write: function(stream, buffer, offset, length, position, canOwn) { if (buffer.buffer === HEAP8.buffer) { canOwn = false } if (!length) return 0 var node = stream.node node.timestamp = Date.now() if (buffer.subarray && (!node.contents || node.contents.subarray)) { if (canOwn) { node.contents = buffer.subarray(offset, offset + length) node.usedBytes = length return length } else if (node.usedBytes === 0 && position === 0) { node.contents = buffer.slice(offset, offset + length) node.usedBytes = length return length } else if (position + length <= node.usedBytes) { node.contents.set( buffer.subarray(offset, offset + length), position ) return length } } MEMFS.expandFileStorage(node, position + length) if (node.contents.subarray && buffer.subarray) { node.contents.set( buffer.subarray(offset, offset + length), position ) } else { for (var i = 0; i < length; i++) { node.contents[position + i] = buffer[offset + i] } } node.usedBytes = Math.max(node.usedBytes, position + length) return length }, llseek: function(stream, offset, whence) { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { position += stream.node.usedBytes } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, allocate: function(stream, offset, length) { MEMFS.expandFileStorage(stream.node, offset + length) stream.node.usedBytes = Math.max( stream.node.usedBytes, offset + length ) }, mmap: function(stream, length, position, prot, flags) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr var allocated var contents = stream.node.contents if (!(flags & 2) && contents.buffer === buffer) { allocated = false ptr = contents.byteOffset } else { if (position > 0 || position + length < contents.length) { if (contents.subarray) { contents = contents.subarray(position, position + length) } else { contents = Array.prototype.slice.call( contents, position, position + length ) } } allocated = true ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } HEAP8.set(contents, ptr) } return { ptr: ptr, allocated: allocated } }, msync: function(stream, buffer, offset, length, mmapFlags) { MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } function asyncLoad(url, onload, onerror, noRunDep) { var dep = !noRunDep ? getUniqueRunDependency('al ' + url) : '' readAsync( url, arrayBuffer => { assert( arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).' ) onload(new Uint8Array(arrayBuffer)) if (dep) removeRunDependency(dep) }, event => { if (onerror) { onerror() } else { throw 'Loading data file "' + url + '" failed.' } } ) if (dep) addRunDependency(dep) } var ERRNO_CODES = {} var NODEFS = { isWindows: false, staticInit: () => { NODEFS.isWindows = !!process.platform.match(/^win/) var flags = process['binding']('constants') if (flags['fs']) { flags = flags['fs'] } NODEFS.flagsForNodeMap = { 1024: flags['O_APPEND'], 64: flags['O_CREAT'], 128: flags['O_EXCL'], 256: flags['O_NOCTTY'], 0: flags['O_RDONLY'], 2: flags['O_RDWR'], 4096: flags['O_SYNC'], 512: flags['O_TRUNC'], 1: flags['O_WRONLY'], 131072: flags['O_NOFOLLOW'], } }, convertNodeCode: e => { var code = e.code return ERRNO_CODES[code] }, mount: mount => { return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0) }, createNode: (parent, name, mode, dev) => { if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { throw new FS.ErrnoError(28) } var node = FS.createNode(parent, name, mode) node.node_ops = NODEFS.node_ops node.stream_ops = NODEFS.stream_ops return node }, getMode: path => { var stat try { stat = fs.lstatSync(path) if (NODEFS.isWindows) { stat.mode = stat.mode | ((stat.mode & 292) >> 2) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return stat.mode }, realPath: node => { var parts = [] while (node.parent !== node) { parts.push(node.name) node = node.parent } parts.push(node.mount.opts.root) parts.reverse() return PATH.join.apply(null, parts) }, flagsForNode: flags => { flags &= ~2097152 flags &= ~2048 flags &= ~32768 flags &= ~524288 flags &= ~65536 var newFlags = 0 for (var k in NODEFS.flagsForNodeMap) { if (flags & k) { newFlags |= NODEFS.flagsForNodeMap[k] flags ^= k } } if (flags) { throw new FS.ErrnoError(28) } return newFlags }, node_ops: { getattr: node => { var path = NODEFS.realPath(node) var stat try { stat = fs.lstatSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } if (NODEFS.isWindows && !stat.blksize) { stat.blksize = 4096 } if (NODEFS.isWindows && !stat.blocks) { stat.blocks = ((stat.size + stat.blksize - 1) / stat.blksize) | 0 } return { dev: stat.dev, ino: stat.ino, mode: stat.mode, nlink: stat.nlink, uid: stat.uid, gid: stat.gid, rdev: stat.rdev, size: stat.size, atime: stat.atime, mtime: stat.mtime, ctime: stat.ctime, blksize: stat.blksize, blocks: stat.blocks, } }, setattr: (node, attr) => { var path = NODEFS.realPath(node) try { if (attr.mode !== undefined) { fs.chmodSync(path, attr.mode) node.mode = attr.mode } if (attr.timestamp !== undefined) { var date = new Date(attr.timestamp) fs.utimesSync(path, date, date) } if (attr.size !== undefined) { fs.truncateSync(path, attr.size) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, lookup: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) var mode = NODEFS.getMode(path) return NODEFS.createNode(parent, name, mode) }, mknod: (parent, name, mode, dev) => { var node = NODEFS.createNode(parent, name, mode, dev) var path = NODEFS.realPath(node) try { if (FS.isDir(node.mode)) { fs.mkdirSync(path, node.mode) } else { fs.writeFileSync(path, '', { mode: node.mode }) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return node }, rename: (oldNode, newDir, newName) => { var oldPath = NODEFS.realPath(oldNode) var newPath = PATH.join2(NODEFS.realPath(newDir), newName) try { fs.renameSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } oldNode.name = newName }, unlink: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.unlinkSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, rmdir: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.rmdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readdir: node => { var path = NODEFS.realPath(node) try { return fs.readdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, symlink: (parent, newName, oldPath) => { var newPath = PATH.join2(NODEFS.realPath(parent), newName) try { fs.symlinkSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readlink: node => { var path = NODEFS.realPath(node) try { path = fs.readlinkSync(path) path = nodePath.relative( nodePath.resolve(node.mount.opts.root), path ) return path } catch (e) { if (!e.code) throw e if (e.code === 'UNKNOWN') throw new FS.ErrnoError(28) throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, }, stream_ops: { open: stream => { var path = NODEFS.realPath(stream.node) try { if (FS.isFile(stream.node.mode)) { stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags)) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, close: stream => { try { if (FS.isFile(stream.node.mode) && stream.nfd) { fs.closeSync(stream.nfd) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, read: (stream, buffer, offset, length, position) => { if (length === 0) return 0 try { return fs.readSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, write: (stream, buffer, offset, length, position) => { try { return fs.writeSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, llseek: (stream, offset, whence) => { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { try { var stat = fs.fstatSync(stream.nfd) position += stat.size } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, mmap: (stream, length, position, prot, flags) => { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr = mmapAlloc(length) NODEFS.stream_ops.read(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } }, msync: (stream, buffer, offset, length, mmapFlags) => { NODEFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } var FS = { root: null, mounts: [], devices: {}, streams: [], nextInode: 1, nameTable: null, currentPath: '/', initialized: false, ignorePermissions: true, ErrnoError: null, genericErrors: {}, filesystems: null, syncFSRequests: 0, lookupPath: (path, opts = {}) => { path = PATH_FS.resolve(path) if (!path) return { path: '', node: null } var defaults = { follow_mount: true, recurse_count: 0 } opts = Object.assign(defaults, opts) if (opts.recurse_count > 8) { throw new FS.ErrnoError(32) } var parts = path.split('/').filter(p => !!p) var current = FS.root var current_path = '/' for (var i = 0; i < parts.length; i++) { var islast = i === parts.length - 1 if (islast && opts.parent) { break } current = FS.lookupNode(current, parts[i]) current_path = PATH.join2(current_path, parts[i]) if (FS.isMountpoint(current)) { if (!islast || (islast && opts.follow_mount)) { current = current.mounted.root } } if (!islast || opts.follow) { var count = 0 while (FS.isLink(current.mode)) { var link = FS.readlink(current_path) current_path = PATH_FS.resolve(PATH.dirname(current_path), link) var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count + 1, }) current = lookup.node if (count++ > 40) { throw new FS.ErrnoError(32) } } } } return { path: current_path, node: current } }, getPath: node => { var path while (true) { if (FS.isRoot(node)) { var mount = node.mount.mountpoint if (!path) return mount return mount[mount.length - 1] !== '/' ? mount + '/' + path : mount + path } path = path ? node.name + '/' + path : node.name node = node.parent } }, hashName: (parentid, name) => { var hash = 0 for (var i = 0; i < name.length; i++) { hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0 } return ((parentid + hash) >>> 0) % FS.nameTable.length }, hashAddNode: node => { var hash = FS.hashName(node.parent.id, node.name) node.name_next = FS.nameTable[hash] FS.nameTable[hash] = node }, hashRemoveNode: node => { var hash = FS.hashName(node.parent.id, node.name) if (FS.nameTable[hash] === node) { FS.nameTable[hash] = node.name_next } else { var current = FS.nameTable[hash] while (current) { if (current.name_next === node) { current.name_next = node.name_next break } current = current.name_next } } }, lookupNode: (parent, name) => { var errCode = FS.mayLookup(parent) if (errCode) { throw new FS.ErrnoError(errCode, parent) } var hash = FS.hashName(parent.id, name) for (var node = FS.nameTable[hash]; node; node = node.name_next) { var nodeName = node.name if (node.parent.id === parent.id && nodeName === name) { return node } } return FS.lookup(parent, name) }, createNode: (parent, name, mode, rdev) => { var node = new FS.FSNode(parent, name, mode, rdev) FS.hashAddNode(node) return node }, destroyNode: node => { FS.hashRemoveNode(node) }, isRoot: node => { return node === node.parent }, isMountpoint: node => { return !!node.mounted }, isFile: mode => { return (mode & 61440) === 32768 }, isDir: mode => { return (mode & 61440) === 16384 }, isLink: mode => { return (mode & 61440) === 40960 }, isChrdev: mode => { return (mode & 61440) === 8192 }, isBlkdev: mode => { return (mode & 61440) === 24576 }, isFIFO: mode => { return (mode & 61440) === 4096 }, isSocket: mode => { return (mode & 49152) === 49152 }, flagModes: { r: 0, 'r+': 2, w: 577, 'w+': 578, a: 1089, 'a+': 1090 }, modeStringToFlags: str => { var flags = FS.flagModes[str] if (typeof flags == 'undefined') { throw new Error('Unknown file open mode: ' + str) } return flags }, flagsToPermissionString: flag => { var perms = ['r', 'w', 'rw'][flag & 3] if (flag & 512) { perms += 'w' } return perms }, nodePermissions: (node, perms) => { if (FS.ignorePermissions) { return 0 } if (perms.includes('r') && !(node.mode & 292)) { return 2 } else if (perms.includes('w') && !(node.mode & 146)) { return 2 } else if (perms.includes('x') && !(node.mode & 73)) { return 2 } return 0 }, mayLookup: dir => { var errCode = FS.nodePermissions(dir, 'x') if (errCode) return errCode if (!dir.node_ops.lookup) return 2 return 0 }, mayCreate: (dir, name) => { try { var node = FS.lookupNode(dir, name) return 20 } catch (e) {} return FS.nodePermissions(dir, 'wx') }, mayDelete: (dir, name, isdir) => { var node try { node = FS.lookupNode(dir, name) } catch (e) { return e.errno } var errCode = FS.nodePermissions(dir, 'wx') if (errCode) { return errCode } if (isdir) { if (!FS.isDir(node.mode)) { return 54 } if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return 10 } } else { if (FS.isDir(node.mode)) { return 31 } } return 0 }, mayOpen: (node, flags) => { if (!node) { return 44 } if (FS.isLink(node.mode)) { return 32 } else if (FS.isDir(node.mode)) { if (FS.flagsToPermissionString(flags) !== 'r' || flags & 512) { return 31 } } return FS.nodePermissions(node, FS.flagsToPermissionString(flags)) }, MAX_OPEN_FDS: 4096, nextfd: (fd_start = 0, fd_end = FS.MAX_OPEN_FDS) => { for (var fd = fd_start; fd <= fd_end; fd++) { if (!FS.streams[fd]) { return fd } } throw new FS.ErrnoError(33) }, getStream: fd => FS.streams[fd], createStream: (stream, fd_start, fd_end) => { if (!FS.FSStream) { FS.FSStream = function() { this.shared = {} } FS.FSStream.prototype = {} Object.defineProperties(FS.FSStream.prototype, { object: { get: function() { return this.node }, set: function(val) { this.node = val }, }, isRead: { get: function() { return (this.flags & 2097155) !== 1 }, }, isWrite: { get: function() { return (this.flags & 2097155) !== 0 }, }, isAppend: { get: function() { return this.flags & 1024 }, }, flags: { get: function() { return this.shared.flags }, set: function(val) { this.shared.flags = val }, }, position: { get: function() { return this.shared.position }, set: function(val) { this.shared.position = val }, }, }) } stream = Object.assign(new FS.FSStream(), stream) var fd = FS.nextfd(fd_start, fd_end) stream.fd = fd FS.streams[fd] = stream return stream }, closeStream: fd => { FS.streams[fd] = null }, chrdev_stream_ops: { open: stream => { var device = FS.getDevice(stream.node.rdev) stream.stream_ops = device.stream_ops if (stream.stream_ops.open) { stream.stream_ops.open(stream) } }, llseek: () => { throw new FS.ErrnoError(70) }, }, major: dev => dev >> 8, minor: dev => dev & 255, makedev: (ma, mi) => (ma << 8) | mi, registerDevice: (dev, ops) => { FS.devices[dev] = { stream_ops: ops } }, getDevice: dev => FS.devices[dev], getMounts: mount => { var mounts = [] var check = [mount] while (check.length) { var m = check.pop() mounts.push(m) check.push.apply(check, m.mounts) } return mounts }, syncfs: (populate, callback) => { if (typeof populate == 'function') { callback = populate populate = false } FS.syncFSRequests++ if (FS.syncFSRequests > 1) { err( 'warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work' ) } var mounts = FS.getMounts(FS.root.mount) var completed = 0 function doCallback(errCode) { FS.syncFSRequests-- return callback(errCode) } function done(errCode) { if (errCode) { if (!done.errored) { done.errored = true return doCallback(errCode) } return } if (++completed >= mounts.length) { doCallback(null) } } mounts.forEach(mount => { if (!mount.type.syncfs) { return done(null) } mount.type.syncfs(mount, populate, done) }) }, mount: (type, opts, mountpoint) => { var root = mountpoint === '/' var pseudo = !mountpoint var node if (root && FS.root) { throw new FS.ErrnoError(10) } else if (!root && !pseudo) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) mountpoint = lookup.path node = lookup.node if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } if (!FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } } var mount = { type: type, opts: opts, mountpoint: mountpoint, mounts: [], } var mountRoot = type.mount(mount) mountRoot.mount = mount mount.root = mountRoot if (root) { FS.root = mountRoot } else if (node) { node.mounted = mount if (node.mount) { node.mount.mounts.push(mount) } } return mountRoot }, unmount: mountpoint => { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) if (!FS.isMountpoint(lookup.node)) { throw new FS.ErrnoError(28) } var node = lookup.node var mount = node.mounted var mounts = FS.getMounts(mount) Object.keys(FS.nameTable).forEach(hash => { var current = FS.nameTable[hash] while (current) { var next = current.name_next if (mounts.includes(current.mount)) { FS.destroyNode(current) } current = next } }) node.mounted = null var idx = node.mount.mounts.indexOf(mount) node.mount.mounts.splice(idx, 1) }, lookup: (parent, name) => { return parent.node_ops.lookup(parent, name) }, mknod: (path, mode, dev) => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) if (!name || name === '.' || name === '..') { throw new FS.ErrnoError(28) } var errCode = FS.mayCreate(parent, name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.mknod) { throw new FS.ErrnoError(63) } return parent.node_ops.mknod(parent, name, mode, dev) }, create: (path, mode) => { mode = mode !== undefined ? mode : 438 mode &= 4095 mode |= 32768 return FS.mknod(path, mode, 0) }, mkdir: (path, mode) => { mode = mode !== undefined ? mode : 511 mode &= 511 | 512 mode |= 16384 return FS.mknod(path, mode, 0) }, mkdirTree: (path, mode) => { var dirs = path.split('/') var d = '' for (var i = 0; i < dirs.length; ++i) { if (!dirs[i]) continue d += '/' + dirs[i] try { FS.mkdir(d, mode) } catch (e) { if (e.errno != 20) throw e } } }, mkdev: (path, mode, dev) => { if (typeof dev == 'undefined') { dev = mode mode = 438 } mode |= 8192 return FS.mknod(path, mode, dev) }, symlink: (oldpath, newpath) => { if (!PATH_FS.resolve(oldpath)) { throw new FS.ErrnoError(44) } var lookup = FS.lookupPath(newpath, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var newname = PATH.basename(newpath) var errCode = FS.mayCreate(parent, newname) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.symlink) { throw new FS.ErrnoError(63) } return parent.node_ops.symlink(parent, newname, oldpath) }, rename: (old_path, new_path) => { var old_dirname = PATH.dirname(old_path) var new_dirname = PATH.dirname(new_path) var old_name = PATH.basename(old_path) var new_name = PATH.basename(new_path) var lookup, old_dir, new_dir lookup = FS.lookupPath(old_path, { parent: true }) old_dir = lookup.node lookup = FS.lookupPath(new_path, { parent: true }) new_dir = lookup.node if (!old_dir || !new_dir) throw new FS.ErrnoError(44) if (old_dir.mount !== new_dir.mount) { throw new FS.ErrnoError(75) } var old_node = FS.lookupNode(old_dir, old_name) var relative = PATH_FS.relative(old_path, new_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(28) } relative = PATH_FS.relative(new_path, old_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(55) } var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (old_node === new_node) { return } var isdir = FS.isDir(old_node.mode) var errCode = FS.mayDelete(old_dir, old_name, isdir) if (errCode) { throw new FS.ErrnoError(errCode) } errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!old_dir.node_ops.rename) { throw new FS.ErrnoError(63) } if ( FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node)) ) { throw new FS.ErrnoError(10) } if (new_dir !== old_dir) { errCode = FS.nodePermissions(old_dir, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } } FS.hashRemoveNode(old_node) try { old_dir.node_ops.rename(old_node, new_dir, new_name) } catch (e) { throw e } finally { FS.hashAddNode(old_node) } }, rmdir: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, true) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.rmdir) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.rmdir(parent, name) FS.destroyNode(node) }, readdir: path => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node if (!node.node_ops.readdir) { throw new FS.ErrnoError(54) } return node.node_ops.readdir(node) }, unlink: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, false) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.unlink) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.unlink(parent, name) FS.destroyNode(node) }, readlink: path => { var lookup = FS.lookupPath(path) var link = lookup.node if (!link) { throw new FS.ErrnoError(44) } if (!link.node_ops.readlink) { throw new FS.ErrnoError(28) } return PATH_FS.resolve( FS.getPath(link.parent), link.node_ops.readlink(link) ) }, stat: (path, dontFollow) => { var lookup = FS.lookupPath(path, { follow: !dontFollow }) var node = lookup.node if (!node) { throw new FS.ErrnoError(44) } if (!node.node_ops.getattr) { throw new FS.ErrnoError(63) } return node.node_ops.getattr(node) }, lstat: path => { return FS.stat(path, true) }, chmod: (path, mode, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { mode: (mode & 4095) | (node.mode & ~4095), timestamp: Date.now(), }) }, lchmod: (path, mode) => { FS.chmod(path, mode, true) }, fchmod: (fd, mode) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chmod(stream.node, mode) }, chown: (path, uid, gid, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { timestamp: Date.now() }) }, lchown: (path, uid, gid) => { FS.chown(path, uid, gid, true) }, fchown: (fd, uid, gid) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chown(stream.node, uid, gid) }, truncate: (path, len) => { if (len < 0) { throw new FS.ErrnoError(28) } var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: true }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } if (FS.isDir(node.mode)) { throw new FS.ErrnoError(31) } if (!FS.isFile(node.mode)) { throw new FS.ErrnoError(28) } var errCode = FS.nodePermissions(node, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } node.node_ops.setattr(node, { size: len, timestamp: Date.now() }) }, ftruncate: (fd, len) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(28) } FS.truncate(stream.node, len) }, utime: (path, atime, mtime) => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node node.node_ops.setattr(node, { timestamp: Math.max(atime, mtime) }) }, open: (path, flags, mode) => { if (path === '') { throw new FS.ErrnoError(44) } flags = typeof flags == 'string' ? FS.modeStringToFlags(flags) : flags mode = typeof mode == 'undefined' ? 438 : mode if (flags & 64) { mode = (mode & 4095) | 32768 } else { mode = 0 } var node if (typeof path == 'object') { node = path } else { path = PATH.normalize(path) try { var lookup = FS.lookupPath(path, { follow: !(flags & 131072) }) node = lookup.node } catch (e) {} } var created = false if (flags & 64) { if (node) { if (flags & 128) { throw new FS.ErrnoError(20) } } else { node = FS.mknod(path, mode, 0) created = true } } if (!node) { throw new FS.ErrnoError(44) } if (FS.isChrdev(node.mode)) { flags &= ~512 } if (flags & 65536 && !FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } if (!created) { var errCode = FS.mayOpen(node, flags) if (errCode) { throw new FS.ErrnoError(errCode) } } if (flags & 512 && !created) { FS.truncate(node, 0) } flags &= ~(128 | 512 | 131072) var stream = FS.createStream({ node: node, path: FS.getPath(node), flags: flags, seekable: true, position: 0, stream_ops: node.stream_ops, ungotten: [], error: false, }) if (stream.stream_ops.open) { stream.stream_ops.open(stream) } if (Module['logReadFiles'] && !(flags & 1)) { if (!FS.readFiles) FS.readFiles = {} if (!(path in FS.readFiles)) { FS.readFiles[path] = 1 } } return stream }, close: stream => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (stream.getdents) stream.getdents = null try { if (stream.stream_ops.close) { stream.stream_ops.close(stream) } } catch (e) { throw e } finally { FS.closeStream(stream.fd) } stream.fd = null }, isClosed: stream => { return stream.fd === null }, llseek: (stream, offset, whence) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (!stream.seekable || !stream.stream_ops.llseek) { throw new FS.ErrnoError(70) } if (whence != 0 && whence != 1 && whence != 2) { throw new FS.ErrnoError(28) } stream.position = stream.stream_ops.llseek(stream, offset, whence) stream.ungotten = [] return stream.position }, read: (stream, buffer, offset, length, position) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.read) { throw new FS.ErrnoError(28) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesRead = stream.stream_ops.read( stream, buffer, offset, length, position ) if (!seeking) stream.position += bytesRead return bytesRead }, write: (stream, buffer, offset, length, position, canOwn) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.write) { throw new FS.ErrnoError(28) } if (stream.seekable && stream.flags & 1024) { FS.llseek(stream, 0, 2) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesWritten = stream.stream_ops.write( stream, buffer, offset, length, position, canOwn ) if (!seeking) stream.position += bytesWritten return bytesWritten }, allocate: (stream, offset, length) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (offset < 0 || length <= 0) { throw new FS.ErrnoError(28) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(43) } if (!stream.stream_ops.allocate) { throw new FS.ErrnoError(138) } stream.stream_ops.allocate(stream, offset, length) }, mmap: (stream, length, position, prot, flags) => { if ( (prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2 ) { throw new FS.ErrnoError(2) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(2) } if (!stream.stream_ops.mmap) { throw new FS.ErrnoError(43) } return stream.stream_ops.mmap(stream, length, position, prot, flags) }, msync: (stream, buffer, offset, length, mmapFlags) => { if (!stream.stream_ops.msync) { return 0 } return stream.stream_ops.msync( stream, buffer, offset, length, mmapFlags ) }, munmap: stream => 0, ioctl: (stream, cmd, arg) => { if (!stream.stream_ops.ioctl) { throw new FS.ErrnoError(59) } return stream.stream_ops.ioctl(stream, cmd, arg) }, readFile: (path, opts = {}) => { opts.flags = opts.flags || 0 opts.encoding = opts.encoding || 'binary' if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { throw new Error('Invalid encoding type "' + opts.encoding + '"') } var ret var stream = FS.open(path, opts.flags) var stat = FS.stat(path) var length = stat.size var buf = new Uint8Array(length) FS.read(stream, buf, 0, length, 0) if (opts.encoding === 'utf8') { ret = UTF8ArrayToString(buf, 0) } else if (opts.encoding === 'binary') { ret = buf } FS.close(stream) return ret }, writeFile: (path, data, opts = {}) => { opts.flags = opts.flags || 577 var stream = FS.open(path, opts.flags, opts.mode) if (typeof data == 'string') { var buf = new Uint8Array(lengthBytesUTF8(data) + 1) var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length) FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn) } else if (ArrayBuffer.isView(data)) { FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn) } else { throw new Error('Unsupported data type') } FS.close(stream) }, cwd: () => FS.currentPath, chdir: path => { var lookup = FS.lookupPath(path, { follow: true }) if (lookup.node === null) { throw new FS.ErrnoError(44) } if (!FS.isDir(lookup.node.mode)) { throw new FS.ErrnoError(54) } var errCode = FS.nodePermissions(lookup.node, 'x') if (errCode) { throw new FS.ErrnoError(errCode) } FS.currentPath = lookup.path }, createDefaultDirectories: () => { FS.mkdir('/tmp') FS.mkdir('/home') FS.mkdir('/home/web_user') }, createDefaultDevices: () => { FS.mkdir('/dev') FS.registerDevice(FS.makedev(1, 3), { read: () => 0, write: (stream, buffer, offset, length, pos) => length, }) FS.mkdev('/dev/null', FS.makedev(1, 3)) TTY.register(FS.makedev(5, 0), TTY.default_tty_ops) TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops) FS.mkdev('/dev/tty', FS.makedev(5, 0)) FS.mkdev('/dev/tty1', FS.makedev(6, 0)) var random_device = getRandomDevice() FS.createDevice('/dev', 'random', random_device) FS.createDevice('/dev', 'urandom', random_device) FS.mkdir('/dev/shm') FS.mkdir('/dev/shm/tmp') }, createSpecialDirectories: () => { FS.mkdir('/proc') var proc_self = FS.mkdir('/proc/self') FS.mkdir('/proc/self/fd') FS.mount( { mount: () => { var node = FS.createNode(proc_self, 'fd', 16384 | 511, 73) node.node_ops = { lookup: (parent, name) => { var fd = +name var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) var ret = { parent: null, mount: { mountpoint: 'fake' }, node_ops: { readlink: () => stream.path }, } ret.parent = ret return ret }, } return node }, }, {}, '/proc/self/fd' ) }, createStandardStreams: () => { if (Module['stdin']) { FS.createDevice('/dev', 'stdin', Module['stdin']) } else { FS.symlink('/dev/tty', '/dev/stdin') } if (Module['stdout']) { FS.createDevice('/dev', 'stdout', null, Module['stdout']) } else { FS.symlink('/dev/tty', '/dev/stdout') } if (Module['stderr']) { FS.createDevice('/dev', 'stderr', null, Module['stderr']) } else { FS.symlink('/dev/tty1', '/dev/stderr') } var stdin = FS.open('/dev/stdin', 0) var stdout = FS.open('/dev/stdout', 1) var stderr = FS.open('/dev/stderr', 1) }, ensureErrnoError: () => { if (FS.ErrnoError) return FS.ErrnoError = function ErrnoError(errno, node) { this.node = node this.setErrno = function(errno) { this.errno = errno } this.setErrno(errno) this.message = 'FS error' } FS.ErrnoError.prototype = new Error() FS.ErrnoError.prototype.constructor = FS.ErrnoError ;[44].forEach(code => { FS.genericErrors[code] = new FS.ErrnoError(code) FS.genericErrors[code].stack = '' }) }, staticInit: () => { FS.ensureErrnoError() FS.nameTable = new Array(4096) FS.mount(MEMFS, {}, '/') FS.createDefaultDirectories() FS.createDefaultDevices() FS.createSpecialDirectories() FS.filesystems = { MEMFS: MEMFS, NODEFS: NODEFS } }, init: (input, output, error) => { FS.init.initialized = true FS.ensureErrnoError() Module['stdin'] = input || Module['stdin'] Module['stdout'] = output || Module['stdout'] Module['stderr'] = error || Module['stderr'] FS.createStandardStreams() }, quit: () => { FS.init.initialized = false for (var i = 0; i < FS.streams.length; i++) { var stream = FS.streams[i] if (!stream) { continue } FS.close(stream) } }, getMode: (canRead, canWrite) => { var mode = 0 if (canRead) mode |= 292 | 73 if (canWrite) mode |= 146 return mode }, findObject: (path, dontResolveLastLink) => { var ret = FS.analyzePath(path, dontResolveLastLink) if (!ret.exists) { return null } return ret.object }, analyzePath: (path, dontResolveLastLink) => { try { var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) path = lookup.path } catch (e) {} var ret = { isRoot: false, exists: false, error: 0, name: null, path: null, object: null, parentExists: false, parentPath: null, parentObject: null, } try { var lookup = FS.lookupPath(path, { parent: true }) ret.parentExists = true ret.parentPath = lookup.path ret.parentObject = lookup.node ret.name = PATH.basename(path) lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) ret.exists = true ret.path = lookup.path ret.object = lookup.node ret.name = lookup.node.name ret.isRoot = lookup.path === '/' } catch (e) { ret.error = e.errno } return ret }, createPath: (parent, path, canRead, canWrite) => { parent = typeof parent == 'string' ? parent : FS.getPath(parent) var parts = path.split('/').reverse() while (parts.length) { var part = parts.pop() if (!part) continue var current = PATH.join2(parent, part) try { FS.mkdir(current) } catch (e) {} parent = current } return current }, createFile: (parent, name, properties, canRead, canWrite) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(canRead, canWrite) return FS.create(path, mode) }, createDataFile: (parent, name, data, canRead, canWrite, canOwn) => { var path = name if (parent) { parent = typeof parent == 'string' ? parent : FS.getPath(parent) path = name ? PATH.join2(parent, name) : parent } var mode = FS.getMode(canRead, canWrite) var node = FS.create(path, mode) if (data) { if (typeof data == 'string') { var arr = new Array(data.length) for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i) data = arr } FS.chmod(node, mode | 146) var stream = FS.open(node, 577) FS.write(stream, data, 0, data.length, 0, canOwn) FS.close(stream) FS.chmod(node, mode) } return node }, createDevice: (parent, name, input, output) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(!!input, !!output) if (!FS.createDevice.major) FS.createDevice.major = 64 var dev = FS.makedev(FS.createDevice.major++, 0) FS.registerDevice(dev, { open: stream => { stream.seekable = false }, close: stream => { if (output && output.buffer && output.buffer.length) { output(10) } }, read: (stream, buffer, offset, length, pos) => { var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = input() } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: (stream, buffer, offset, length, pos) => { for (var i = 0; i < length; i++) { try { output(buffer[offset + i]) } catch (e) { throw new FS.ErrnoError(29) } } if (length) { stream.node.timestamp = Date.now() } return i }, }) return FS.mkdev(path, mode, dev) }, forceLoadFile: obj => { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true if (typeof XMLHttpRequest != 'undefined') { throw new Error( 'Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.' ) } else if (read_) { try { obj.contents = intArrayFromString(read_(obj.url), true) obj.usedBytes = obj.contents.length } catch (e) { throw new FS.ErrnoError(29) } } else { throw new Error('Cannot load without read() or XMLHttpRequest.') } }, createLazyFile: (parent, name, url, canRead, canWrite) => { function LazyUint8Array() { this.lengthKnown = false this.chunks = [] } LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) { if (idx > this.length - 1 || idx < 0) { return undefined } var chunkOffset = idx % this.chunkSize var chunkNum = (idx / this.chunkSize) | 0 return this.getter(chunkNum)[chunkOffset] } LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter( getter ) { this.getter = getter } LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { var xhr = new XMLHttpRequest() xhr.open('HEAD', url, false) xhr.send(null) if (!((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) throw new Error("Couldn't load " + url + '. Status: ' + xhr.status) var datalength = Number(xhr.getResponseHeader('Content-length')) var header var hasByteServing = (header = xhr.getResponseHeader('Accept-Ranges')) && header === 'bytes' var usesGzip = (header = xhr.getResponseHeader('Content-Encoding')) && header === 'gzip' var chunkSize = 1024 * 1024 if (!hasByteServing) chunkSize = datalength var doXHR = (from, to) => { if (from > to) throw new Error( 'invalid range (' + from + ', ' + to + ') or no bytes requested!' ) if (to > datalength - 1) throw new Error( 'only ' + datalength + ' bytes available! programmer error!' ) var xhr = new XMLHttpRequest() xhr.open('GET', url, false) if (datalength !== chunkSize) xhr.setRequestHeader('Range', 'bytes=' + from + '-' + to) xhr.responseType = 'arraybuffer' if (xhr.overrideMimeType) { xhr.overrideMimeType('text/plain; charset=x-user-defined') } xhr.send(null) if ( !((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ) throw new Error( "Couldn't load " + url + '. Status: ' + xhr.status ) if (xhr.response !== undefined) { return new Uint8Array(xhr.response || []) } return intArrayFromString(xhr.responseText || '', true) } var lazyArray = this lazyArray.setDataGetter(chunkNum => { var start = chunkNum * chunkSize var end = (chunkNum + 1) * chunkSize - 1 end = Math.min(end, datalength - 1) if (typeof lazyArray.chunks[chunkNum] == 'undefined') { lazyArray.chunks[chunkNum] = doXHR(start, end) } if (typeof lazyArray.chunks[chunkNum] == 'undefined') throw new Error('doXHR failed!') return lazyArray.chunks[chunkNum] }) if (usesGzip || !datalength) { chunkSize = datalength = 1 datalength = this.getter(0).length chunkSize = datalength out( 'LazyFiles on gzip forces download of the whole file when length is accessed' ) } this._length = datalength this._chunkSize = chunkSize this.lengthKnown = true } if (typeof XMLHttpRequest != 'undefined') { if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc' var lazyArray = new LazyUint8Array() Object.defineProperties(lazyArray, { length: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._length }, }, chunkSize: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._chunkSize }, }, }) var properties = { isDevice: false, contents: lazyArray } } else { var properties = { isDevice: false, url: url } } var node = FS.createFile(parent, name, properties, canRead, canWrite) if (properties.contents) { node.contents = properties.contents } else if (properties.url) { node.contents = null node.url = properties.url } Object.defineProperties(node, { usedBytes: { get: function() { return this.contents.length }, }, }) var stream_ops = {} var keys = Object.keys(node.stream_ops) keys.forEach(key => { var fn = node.stream_ops[key] stream_ops[key] = function forceLoadLazyFile() { FS.forceLoadFile(node) return fn.apply(null, arguments) } }) function writeChunks(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= contents.length) return 0 var size = Math.min(contents.length - position, length) if (contents.slice) { for (var i = 0; i < size; i++) { buffer[offset + i] = contents[position + i] } } else { for (var i = 0; i < size; i++) { buffer[offset + i] = contents.get(position + i) } } return size } stream_ops.read = (stream, buffer, offset, length, position) => { FS.forceLoadFile(node) return writeChunks(stream, buffer, offset, length, position) } stream_ops.mmap = (stream, length, position, prot, flags) => { FS.forceLoadFile(node) var ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } writeChunks(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } } node.stream_ops = stream_ops return node }, createPreloadedFile: ( parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish ) => { var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent var dep = getUniqueRunDependency('cp ' + fullname) function processData(byteArray) { function finish(byteArray) { if (preFinish) preFinish() if (!dontCreateFile) { FS.createDataFile( parent, name, byteArray, canRead, canWrite, canOwn ) } if (onload) onload() removeRunDependency(dep) } if ( Browser.handledByPreloadPlugin(byteArray, fullname, finish, () => { if (onerror) onerror() removeRunDependency(dep) }) ) { return } finish(byteArray) } addRunDependency(dep) if (typeof url == 'string') { asyncLoad(url, byteArray => processData(byteArray), onerror) } else { processData(url) } }, indexedDB: () => { return ( window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB ) }, DB_NAME: () => { return 'EM_FS_' + window.location.pathname }, DB_VERSION: 20, DB_STORE_NAME: 'FILE_DATA', saveFilesToDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = () => { out('creating db') var db = openRequest.result db.createObjectStore(FS.DB_STORE_NAME) } openRequest.onsuccess = () => { var db = openRequest.result var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite') var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var putRequest = files.put( FS.analyzePath(path).object.contents, path ) putRequest.onsuccess = () => { ok++ if (ok + fail == total) finish() } putRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, loadFilesFromDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = onerror openRequest.onsuccess = () => { var db = openRequest.result try { var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly') } catch (e) { onerror(e) return } var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var getRequest = files.get(path) getRequest.onsuccess = () => { if (FS.analyzePath(path).exists) { FS.unlink(path) } FS.createDataFile( PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true ) ok++ if (ok + fail == total) finish() } getRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, } var SYSCALLS = { DEFAULT_POLLMASK: 5, calculateAt: function(dirfd, path, allowEmpty) { if (PATH.isAbs(path)) { return path } var dir if (dirfd === -100) { dir = FS.cwd() } else { var dirstream = SYSCALLS.getStreamFromFD(dirfd) dir = dirstream.path } if (path.length == 0) { if (!allowEmpty) { throw new FS.ErrnoError(44) } return dir } return PATH.join2(dir, path) }, doStat: function(func, path, buf) { try { var stat = func(path) } catch (e) { if ( e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node)) ) { return -54 } throw e } HEAP32[buf >> 2] = stat.dev HEAP32[(buf + 8) >> 2] = stat.ino HEAP32[(buf + 12) >> 2] = stat.mode HEAPU32[(buf + 16) >> 2] = stat.nlink HEAP32[(buf + 20) >> 2] = stat.uid HEAP32[(buf + 24) >> 2] = stat.gid HEAP32[(buf + 28) >> 2] = stat.rdev ;(tempI64 = [ stat.size >>> 0, ((tempDouble = stat.size), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 40) >> 2] = tempI64[0]), (HEAP32[(buf + 44) >> 2] = tempI64[1]) HEAP32[(buf + 48) >> 2] = 4096 HEAP32[(buf + 52) >> 2] = stat.blocks var atime = stat.atime.getTime() var mtime = stat.mtime.getTime() var ctime = stat.ctime.getTime() ;(tempI64 = [ Math.floor(atime / 1e3) >>> 0, ((tempDouble = Math.floor(atime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 56) >> 2] = tempI64[0]), (HEAP32[(buf + 60) >> 2] = tempI64[1]) HEAPU32[(buf + 64) >> 2] = (atime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(mtime / 1e3) >>> 0, ((tempDouble = Math.floor(mtime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 72) >> 2] = tempI64[0]), (HEAP32[(buf + 76) >> 2] = tempI64[1]) HEAPU32[(buf + 80) >> 2] = (mtime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(ctime / 1e3) >>> 0, ((tempDouble = Math.floor(ctime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 88) >> 2] = tempI64[0]), (HEAP32[(buf + 92) >> 2] = tempI64[1]) HEAPU32[(buf + 96) >> 2] = (ctime % 1e3) * 1e3 ;(tempI64 = [ stat.ino >>> 0, ((tempDouble = stat.ino), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 104) >> 2] = tempI64[0]), (HEAP32[(buf + 108) >> 2] = tempI64[1]) return 0 }, doMsync: function(addr, stream, len, flags, offset) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } if (flags & 2) { return 0 } var buffer = HEAPU8.slice(addr, addr + len) FS.msync(stream, buffer, offset, len, flags) }, varargs: undefined, get: function() { SYSCALLS.varargs += 4 var ret = HEAP32[(SYSCALLS.varargs - 4) >> 2] return ret }, getStr: function(ptr) { var ret = UTF8ToString(ptr) return ret }, getStreamFromFD: function(fd) { var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) return stream }, } function ___syscall_fcntl64(fd, cmd, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (cmd) { case 0: { var arg = SYSCALLS.get() if (arg < 0) { return -28 } var newStream newStream = FS.createStream(stream, arg) return newStream.fd } case 1: case 2: return 0 case 3: return stream.flags case 4: { var arg = SYSCALLS.get() stream.flags |= arg return 0 } case 5: { var arg = SYSCALLS.get() var offset = 0 HEAP16[(arg + offset) >> 1] = 2 return 0 } case 6: case 7: return 0 case 16: case 8: return -28 case 9: setErrNo(28) return -1 default: { return -28 } } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_getcwd(buf, size) { try { if (size === 0) return -28 var cwd = FS.cwd() var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1 if (size < cwdLengthInBytes) return -68 stringToUTF8(cwd, buf, size) return cwdLengthInBytes } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_ioctl(fd, op, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (op) { case 21509: case 21505: { if (!stream.tty) return -59 return 0 } case 21510: case 21511: case 21512: case 21506: case 21507: case 21508: { if (!stream.tty) return -59 return 0 } case 21519: { if (!stream.tty) return -59 var argp = SYSCALLS.get() HEAP32[argp >> 2] = 0 return 0 } case 21520: { if (!stream.tty) return -59 return -28 } case 21531: { var argp = SYSCALLS.get() return FS.ioctl(stream, op, argp) } case 21523: { if (!stream.tty) return -59 return 0 } case 21524: { if (!stream.tty) return -59 return 0 } default: return -28 } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_openat(dirfd, path, flags, varargs) { SYSCALLS.varargs = varargs try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) var mode = varargs ? SYSCALLS.get() : 0 return FS.open(path, flags, mode).fd } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_readlinkat(dirfd, path, buf, bufsize) { try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) if (bufsize <= 0) return -28 var ret = FS.readlink(path) var len = Math.min(bufsize, lengthBytesUTF8(ret)) var endChar = HEAP8[buf + len] stringToUTF8(ret, buf, bufsize + 1) HEAP8[buf + len] = endChar return len } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_stat64(path, buf) { try { path = SYSCALLS.getStr(path) return SYSCALLS.doStat(FS.stat, path, buf) } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function _abort() { abort('') } function _emscripten_memcpy_big(dest, src, num) { HEAPU8.copyWithin(dest, src, src + num) } function getHeapMax() { return 2147483648 } function emscripten_realloc_buffer(size) { try { wasmMemory.grow((size - buffer.byteLength + 65535) >>> 16) updateGlobalBufferAndViews(wasmMemory.buffer) return 1 } catch (e) {} } function _emscripten_resize_heap(requestedSize) { var oldSize = HEAPU8.length requestedSize = requestedSize >>> 0 var maxHeapSize = getHeapMax() if (requestedSize > maxHeapSize) { return false } let alignUp = (x, multiple) => x + ((multiple - (x % multiple)) % multiple) for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown) overGrownHeapSize = Math.min( overGrownHeapSize, requestedSize + 100663296 ) var newSize = Math.min( maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536) ) var replacement = emscripten_realloc_buffer(newSize) if (replacement) { return true } } return false } var ENV = {} function getExecutableName() { return thisProgram || './this.program' } function getEnvStrings() { if (!getEnvStrings.strings) { var lang = ( (typeof navigator == 'object' && navigator.languages && navigator.languages[0]) || 'C' ).replace('-', '_') + '.UTF-8' var env = { USER: 'web_user', LOGNAME: 'web_user', PATH: '/', PWD: '/', HOME: '/home/web_user', LANG: lang, _: getExecutableName(), } for (var x in ENV) { if (ENV[x] === undefined) delete env[x] else env[x] = ENV[x] } var strings = [] for (var x in env) { strings.push(x + '=' + env[x]) } getEnvStrings.strings = strings } return getEnvStrings.strings } function writeAsciiToMemory(str, buffer, dontAddNull) { for (var i = 0; i < str.length; ++i) { HEAP8[buffer++ >> 0] = str.charCodeAt(i) } if (!dontAddNull) HEAP8[buffer >> 0] = 0 } function _environ_get(__environ, environ_buf) { var bufSize = 0 getEnvStrings().forEach(function(string, i) { var ptr = environ_buf + bufSize HEAPU32[(__environ + i * 4) >> 2] = ptr writeAsciiToMemory(string, ptr) bufSize += string.length + 1 }) return 0 } function _environ_sizes_get(penviron_count, penviron_buf_size) { var strings = getEnvStrings() HEAPU32[penviron_count >> 2] = strings.length var bufSize = 0 strings.forEach(function(string) { bufSize += string.length + 1 }) HEAPU32[penviron_buf_size >> 2] = bufSize return 0 } function _proc_exit(code) { EXITSTATUS = code if (!keepRuntimeAlive()) { if (Module['onExit']) Module['onExit'](code) ABORT = true } quit_(code, new ExitStatus(code)) } function exitJS(status, implicit) { EXITSTATUS = status _proc_exit(status) } var _exit = exitJS function _fd_close(fd) { try { var stream = SYSCALLS.getStreamFromFD(fd) FS.close(stream) return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doReadv(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.read(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr if (curr < len) break } return ret } function _fd_read(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doReadv(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function convertI32PairToI53Checked(lo, hi) { return (hi + 2097152) >>> 0 < 4194305 - !!lo ? (lo >>> 0) + hi * 4294967296 : NaN } function _fd_seek(fd, offset_low, offset_high, whence, newOffset) { try { var offset = convertI32PairToI53Checked(offset_low, offset_high) if (isNaN(offset)) return 61 var stream = SYSCALLS.getStreamFromFD(fd) FS.llseek(stream, offset, whence) ;(tempI64 = [ stream.position >>> 0, ((tempDouble = stream.position), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[newOffset >> 2] = tempI64[0]), (HEAP32[(newOffset + 4) >> 2] = tempI64[1]) if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doWritev(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.write(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr } return ret } function _fd_write(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doWritev(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function __isLeapYear(year) { return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) } function __arraySum(array, index) { var sum = 0 for (var i = 0; i <= index; sum += array[i++]) {} return sum } var __MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] var __MONTH_DAYS_REGULAR = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] function __addDays(date, days) { var newDate = new Date(date.getTime()) while (days > 0) { var leap = __isLeapYear(newDate.getFullYear()) var currentMonth = newDate.getMonth() var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth] if (days > daysInCurrentMonth - newDate.getDate()) { days -= daysInCurrentMonth - newDate.getDate() + 1 newDate.setDate(1) if (currentMonth < 11) { newDate.setMonth(currentMonth + 1) } else { newDate.setMonth(0) newDate.setFullYear(newDate.getFullYear() + 1) } } else { newDate.setDate(newDate.getDate() + days) return newDate } } return newDate } function writeArrayToMemory(array, buffer) { HEAP8.set(array, buffer) } function _strftime(s, maxsize, format, tm) { var tm_zone = HEAP32[(tm + 40) >> 2] var date = { tm_sec: HEAP32[tm >> 2], tm_min: HEAP32[(tm + 4) >> 2], tm_hour: HEAP32[(tm + 8) >> 2], tm_mday: HEAP32[(tm + 12) >> 2], tm_mon: HEAP32[(tm + 16) >> 2], tm_year: HEAP32[(tm + 20) >> 2], tm_wday: HEAP32[(tm + 24) >> 2], tm_yday: HEAP32[(tm + 28) >> 2], tm_isdst: HEAP32[(tm + 32) >> 2], tm_gmtoff: HEAP32[(tm + 36) >> 2], tm_zone: tm_zone ? UTF8ToString(tm_zone) : '', } var pattern = UTF8ToString(format) var EXPANSION_RULES_1 = { '%c': '%a %b %d %H:%M:%S %Y', '%D': '%m/%d/%y', '%F': '%Y-%m-%d', '%h': '%b', '%r': '%I:%M:%S %p', '%R': '%H:%M', '%T': '%H:%M:%S', '%x': '%m/%d/%y', '%X': '%H:%M:%S', '%Ec': '%c', '%EC': '%C', '%Ex': '%m/%d/%y', '%EX': '%H:%M:%S', '%Ey': '%y', '%EY': '%Y', '%Od': '%d', '%Oe': '%e', '%OH': '%H', '%OI': '%I', '%Om': '%m', '%OM': '%M', '%OS': '%S', '%Ou': '%u', '%OU': '%U', '%OV': '%V', '%Ow': '%w', '%OW': '%W', '%Oy': '%y', } for (var rule in EXPANSION_RULES_1) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_1[rule] ) } var WEEKDAYS = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', ] var MONTHS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ] function leadingSomething(value, digits, character) { var str = typeof value == 'number' ? value.toString() : value || '' while (str.length < digits) { str = character[0] + str } return str } function leadingNulls(value, digits) { return leadingSomething(value, digits, '0') } function compareByDay(date1, date2) { function sgn(value) { return value < 0 ? -1 : value > 0 ? 1 : 0 } var compare if ((compare = sgn(date1.getFullYear() - date2.getFullYear())) === 0) { if ((compare = sgn(date1.getMonth() - date2.getMonth())) === 0) { compare = sgn(date1.getDate() - date2.getDate()) } } return compare } function getFirstWeekStartDate(janFourth) { switch (janFourth.getDay()) { case 0: return new Date(janFourth.getFullYear() - 1, 11, 29) case 1: return janFourth case 2: return new Date(janFourth.getFullYear(), 0, 3) case 3: return new Date(janFourth.getFullYear(), 0, 2) case 4: return new Date(janFourth.getFullYear(), 0, 1) case 5: return new Date(janFourth.getFullYear() - 1, 11, 31) case 6: return new Date(janFourth.getFullYear() - 1, 11, 30) } } function getWeekBasedYear(date) { var thisDate = __addDays( new Date(date.tm_year + 1900, 0, 1), date.tm_yday ) var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4) var janFourthNextYear = new Date(thisDate.getFullYear() + 1, 0, 4) var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear) var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear) if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { return thisDate.getFullYear() + 1 } return thisDate.getFullYear() } return thisDate.getFullYear() - 1 } var EXPANSION_RULES_2 = { '%a': function(date) { return WEEKDAYS[date.tm_wday].substring(0, 3) }, '%A': function(date) { return WEEKDAYS[date.tm_wday] }, '%b': function(date) { return MONTHS[date.tm_mon].substring(0, 3) }, '%B': function(date) { return MONTHS[date.tm_mon] }, '%C': function(date) { var year = date.tm_year + 1900 return leadingNulls((year / 100) | 0, 2) }, '%d': function(date) { return leadingNulls(date.tm_mday, 2) }, '%e': function(date) { return leadingSomething(date.tm_mday, 2, ' ') }, '%g': function(date) { return getWeekBasedYear(date) .toString() .substring(2) }, '%G': function(date) { return getWeekBasedYear(date) }, '%H': function(date) { return leadingNulls(date.tm_hour, 2) }, '%I': function(date) { var twelveHour = date.tm_hour if (twelveHour == 0) twelveHour = 12 else if (twelveHour > 12) twelveHour -= 12 return leadingNulls(twelveHour, 2) }, '%j': function(date) { return leadingNulls( date.tm_mday + __arraySum( __isLeapYear(date.tm_year + 1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon - 1 ), 3 ) }, '%m': function(date) { return leadingNulls(date.tm_mon + 1, 2) }, '%M': function(date) { return leadingNulls(date.tm_min, 2) }, '%n': function() { return '\n' }, '%p': function(date) { if (date.tm_hour >= 0 && date.tm_hour < 12) { return 'AM' } return 'PM' }, '%S': function(date) { return leadingNulls(date.tm_sec, 2) }, '%t': function() { return '\t' }, '%u': function(date) { return date.tm_wday || 7 }, '%U': function(date) { var days = date.tm_yday + 7 - date.tm_wday return leadingNulls(Math.floor(days / 7), 2) }, '%V': function(date) { var val = Math.floor( (date.tm_yday + 7 - ((date.tm_wday + 6) % 7)) / 7 ) if ((date.tm_wday + 371 - date.tm_yday - 2) % 7 <= 2) { val++ } if (!val) { val = 52 var dec31 = (date.tm_wday + 7 - date.tm_yday - 1) % 7 if ( dec31 == 4 || (dec31 == 5 && __isLeapYear((date.tm_year % 400) - 1)) ) { val++ } } else if (val == 53) { var jan1 = (date.tm_wday + 371 - date.tm_yday) % 7 if (jan1 != 4 && (jan1 != 3 || !__isLeapYear(date.tm_year))) val = 1 } return leadingNulls(val, 2) }, '%w': function(date) { return date.tm_wday }, '%W': function(date) { var days = date.tm_yday + 7 - ((date.tm_wday + 6) % 7) return leadingNulls(Math.floor(days / 7), 2) }, '%y': function(date) { return (date.tm_year + 1900).toString().substring(2) }, '%Y': function(date) { return date.tm_year + 1900 }, '%z': function(date) { var off = date.tm_gmtoff var ahead = off >= 0 off = Math.abs(off) / 60 off = (off / 60) * 100 + (off % 60) return (ahead ? '+' : '-') + String('0000' + off).slice(-4) }, '%Z': function(date) { return date.tm_zone }, '%%': function() { return '%' }, } pattern = pattern.replace(/%%/g, '\0\0') for (var rule in EXPANSION_RULES_2) { if (pattern.includes(rule)) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date) ) } } pattern = pattern.replace(/\0\0/g, '%') var bytes = intArrayFromString(pattern, false) if (bytes.length > maxsize) { return 0 } writeArrayToMemory(bytes, s) return bytes.length - 1 } function _strftime_l(s, maxsize, format, tm, loc) { return _strftime(s, maxsize, format, tm) } function handleException(e) { if (e instanceof ExitStatus || e == 'unwind') { return EXITSTATUS } quit_(1, e) } function allocateUTF8OnStack(str) { var size = lengthBytesUTF8(str) + 1 var ret = stackAlloc(size) stringToUTF8Array(str, HEAP8, ret, size) return ret } function getCFunc(ident) { var func = Module['_' + ident] return func } function ccall(ident, returnType, argTypes, args, opts) { var toC = { string: str => { var ret = 0 if (str !== null && str !== undefined && str !== 0) { var len = (str.length << 2) + 1 ret = stackAlloc(len) stringToUTF8(str, ret, len) } return ret }, array: arr => { var ret = stackAlloc(arr.length) writeArrayToMemory(arr, ret) return ret }, } function convertReturnValue(ret) { if (returnType === 'string') { return UTF8ToString(ret) } if (returnType === 'boolean') return Boolean(ret) return ret } var func = getCFunc(ident) var cArgs = [] var stack = 0 if (args) { for (var i = 0; i < args.length; i++) { var converter = toC[argTypes[i]] if (converter) { if (stack === 0) stack = stackSave() cArgs[i] = converter(args[i]) } else { cArgs[i] = args[i] } } } var ret = func.apply(null, cArgs) function onDone(ret) { if (stack !== 0) stackRestore(stack) return convertReturnValue(ret) } ret = onDone(ret) return ret } function cwrap(ident, returnType, argTypes, opts) { argTypes = argTypes || [] var numericArgs = argTypes.every( type => type === 'number' || type === 'boolean' ) var numericRet = returnType !== 'string' if (numericRet && numericArgs && !opts) { return getCFunc(ident) } return function() { return ccall(ident, returnType, argTypes, arguments, opts) } } function AsciiToString(ptr) { var str = '' while (1) { var ch = HEAPU8[ptr++ >> 0] if (!ch) return str str += String.fromCharCode(ch) } } var FSNode = function(parent, name, mode, rdev) { if (!parent) { parent = this } this.parent = parent this.mount = parent.mount this.mounted = null this.id = FS.nextInode++ this.name = name this.mode = mode this.node_ops = {} this.stream_ops = {} this.rdev = rdev } var readMode = 292 | 73 var writeMode = 146 Object.defineProperties(FSNode.prototype, { read: { get: function() { return (this.mode & readMode) === readMode }, set: function(val) { val ? (this.mode |= readMode) : (this.mode &= ~readMode) }, }, write: { get: function() { return (this.mode & writeMode) === writeMode }, set: function(val) { val ? (this.mode |= writeMode) : (this.mode &= ~writeMode) }, }, isFolder: { get: function() { return FS.isDir(this.mode) }, }, isDevice: { get: function() { return FS.isChrdev(this.mode) }, }, }) FS.FSNode = FSNode FS.staticInit() Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_unlink'] = FS.unlink Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice if (ENVIRONMENT_IS_NODE) { NODEFS.staticInit() } ERRNO_CODES = { EPERM: 63, ENOENT: 44, ESRCH: 71, EINTR: 27, EIO: 29, ENXIO: 60, E2BIG: 1, ENOEXEC: 45, EBADF: 8, ECHILD: 12, EAGAIN: 6, EWOULDBLOCK: 6, ENOMEM: 48, EACCES: 2, EFAULT: 21, ENOTBLK: 105, EBUSY: 10, EEXIST: 20, EXDEV: 75, ENODEV: 43, ENOTDIR: 54, EISDIR: 31, EINVAL: 28, ENFILE: 41, EMFILE: 33, ENOTTY: 59, ETXTBSY: 74, EFBIG: 22, ENOSPC: 51, ESPIPE: 70, EROFS: 69, EMLINK: 34, EPIPE: 64, EDOM: 18, ERANGE: 68, ENOMSG: 49, EIDRM: 24, ECHRNG: 106, EL2NSYNC: 156, EL3HLT: 107, EL3RST: 108, ELNRNG: 109, EUNATCH: 110, ENOCSI: 111, EL2HLT: 112, EDEADLK: 16, ENOLCK: 46, EBADE: 113, EBADR: 114, EXFULL: 115, ENOANO: 104, EBADRQC: 103, EBADSLT: 102, EDEADLOCK: 16, EBFONT: 101, ENOSTR: 100, ENODATA: 116, ETIME: 117, ENOSR: 118, ENONET: 119, ENOPKG: 120, EREMOTE: 121, ENOLINK: 47, EADV: 122, ESRMNT: 123, ECOMM: 124, EPROTO: 65, EMULTIHOP: 36, EDOTDOT: 125, EBADMSG: 9, ENOTUNIQ: 126, EBADFD: 127, EREMCHG: 128, ELIBACC: 129, ELIBBAD: 130, ELIBSCN: 131, ELIBMAX: 132, ELIBEXEC: 133, ENOSYS: 52, ENOTEMPTY: 55, ENAMETOOLONG: 37, ELOOP: 32, EOPNOTSUPP: 138, EPFNOSUPPORT: 139, ECONNRESET: 15, ENOBUFS: 42, EAFNOSUPPORT: 5, EPROTOTYPE: 67, ENOTSOCK: 57, ENOPROTOOPT: 50, ESHUTDOWN: 140, ECONNREFUSED: 14, EADDRINUSE: 3, ECONNABORTED: 13, ENETUNREACH: 40, ENETDOWN: 38, ETIMEDOUT: 73, EHOSTDOWN: 142, EHOSTUNREACH: 23, EINPROGRESS: 26, EALREADY: 7, EDESTADDRREQ: 17, EMSGSIZE: 35, EPROTONOSUPPORT: 66, ESOCKTNOSUPPORT: 137, EADDRNOTAVAIL: 4, ENETRESET: 39, EISCONN: 30, ENOTCONN: 53, ETOOMANYREFS: 141, EUSERS: 136, EDQUOT: 19, ESTALE: 72, ENOTSUP: 138, ENOMEDIUM: 148, EILSEQ: 25, EOVERFLOW: 61, ECANCELED: 11, ENOTRECOVERABLE: 56, EOWNERDEAD: 62, ESTRPIPE: 135, } var asmLibraryArg = { b: ___cxa_throw, d: ___syscall_fcntl64, r: ___syscall_getcwd, i: ___syscall_ioctl, j: ___syscall_openat, n: ___syscall_readlinkat, o: ___syscall_stat64, c: _abort, f: _emscripten_memcpy_big, m: _emscripten_resize_heap, p: _environ_get, q: _environ_sizes_get, a: _exit, e: _fd_close, h: _fd_read, k: _fd_seek, g: _fd_write, l: _strftime_l, } var asm = createWasm() var ___wasm_call_ctors = (Module['___wasm_call_ctors'] = function() { return (___wasm_call_ctors = Module['___wasm_call_ctors'] = Module['asm']['t']).apply(null, arguments) }) var _main = (Module['_main'] = function() { return (_main = Module['_main'] = Module['asm']['u']).apply( null, arguments ) }) var ___errno_location = (Module['___errno_location'] = function() { return (___errno_location = Module['___errno_location'] = Module['asm']['v']).apply(null, arguments) }) var _itk_wasm_input_array_alloc = (Module[ '_itk_wasm_input_array_alloc' ] = function() { return (_itk_wasm_input_array_alloc = Module[ '_itk_wasm_input_array_alloc' ] = Module['asm']['w']).apply(null, arguments) }) var _itk_wasm_input_json_alloc = (Module[ '_itk_wasm_input_json_alloc' ] = function() { return (_itk_wasm_input_json_alloc = Module[ '_itk_wasm_input_json_alloc' ] = Module['asm']['x']).apply(null, arguments) }) var _itk_wasm_output_json_address = (Module[ '_itk_wasm_output_json_address' ] = function() { return (_itk_wasm_output_json_address = Module[ '_itk_wasm_output_json_address' ] = Module['asm']['y']).apply(null, arguments) }) var _itk_wasm_output_json_size = (Module[ '_itk_wasm_output_json_size' ] = function() { return (_itk_wasm_output_json_size = Module[ '_itk_wasm_output_json_size' ] = Module['asm']['z']).apply(null, arguments) }) var _itk_wasm_output_array_address = (Module[ '_itk_wasm_output_array_address' ] = function() { return (_itk_wasm_output_array_address = Module[ '_itk_wasm_output_array_address' ] = Module['asm']['A']).apply(null, arguments) }) var _itk_wasm_output_array_size = (Module[ '_itk_wasm_output_array_size' ] = function() { return (_itk_wasm_output_array_size = Module[ '_itk_wasm_output_array_size' ] = Module['asm']['B']).apply(null, arguments) }) var _itk_wasm_free_all = (Module['_itk_wasm_free_all'] = function() { return (_itk_wasm_free_all = Module['_itk_wasm_free_all'] = Module['asm']['C']).apply(null, arguments) }) var stackSave = (Module['stackSave'] = function() { return (stackSave = Module['stackSave'] = Module['asm']['E']).apply( null, arguments ) }) var stackRestore = (Module['stackRestore'] = function() { return (stackRestore = Module['stackRestore'] = Module['asm']['F']).apply( null, arguments ) }) var stackAlloc = (Module['stackAlloc'] = function() { return (stackAlloc = Module['stackAlloc'] = Module['asm']['G']).apply( null, arguments ) }) var ___cxa_is_pointer_type = (Module[ '___cxa_is_pointer_type' ] = function() { return (___cxa_is_pointer_type = Module['___cxa_is_pointer_type'] = Module['asm']['H']).apply(null, arguments) }) Module['addRunDependency'] = addRunDependency Module['removeRunDependency'] = removeRunDependency Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice Module['FS_unlink'] = FS.unlink Module['callMain'] = callMain Module['ccall'] = ccall Module['cwrap'] = cwrap Module['AsciiToString'] = AsciiToString Module['writeArrayToMemory'] = writeArrayToMemory Module['writeAsciiToMemory'] = writeAsciiToMemory var calledRun dependenciesFulfilled = function runCaller() { if (!calledRun) run() if (!calledRun) dependenciesFulfilled = runCaller } function callMain(args) { var entryFunction = Module['_main'] args = args || [] args.unshift(thisProgram) var argc = args.length var argv = stackAlloc((argc + 1) * 4) var argv_ptr = argv >> 2 args.forEach(arg => { HEAP32[argv_ptr++] = allocateUTF8OnStack(arg) }) HEAP32[argv_ptr] = 0 try { var ret = entryFunction(argc, argv) exitJS(ret, true) return ret } catch (e) { return handleException(e) } } function run(args) { args = args || arguments_ if (runDependencies > 0) { return } preRun() if (runDependencies > 0) { return } function doRun() { if (calledRun) return calledRun = true Module['calledRun'] = true if (ABORT) return initRuntime() preMain() readyPromiseResolve(Module) if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized']() if (shouldRunNow) callMain(args) postRun() } if (Module['setStatus']) { Module['setStatus']('Running...') setTimeout(function() { setTimeout(function() { Module['setStatus']('') }, 1) doRun() }, 1) } else { doRun() } } if (Module['preInit']) { if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']] while (Module['preInit'].length > 0) { Module['preInit'].pop()() } } var shouldRunNow = false if (Module['noInitialRun']) shouldRunNow = false run() Module.mountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) if (FS.isDir(containingDir) || containingDir === '/') { return } var currentDir = '/' var splitContainingDir = containingDir.split(path.sep) for (var ii = 1; ii < splitContainingDir.length; ii++) { currentDir += splitContainingDir[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } FS.mount(NODEFS, { root: containingDir }, currentDir) return currentDir + path.basename(filePath) } Module.unmountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) FS.unmount(containingDir) } Module.fs_mkdirs = function(dirs) { var currentDir = '/' var splitDirs = dirs.split('/') for (var ii = 1; ii < splitDirs.length; ++ii) { currentDir += splitDirs[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } } Module.fs_readFile = function(path, opts) { return FS.readFile(path, opts) } Module.fs_writeFile = function(path, data, opts) { return FS.writeFile(path, data, opts) } Module.fs_unlink = function(path) { return FS.unlink(path) } Module.fs_open = function(path, flags, mode) { return FS.open(path, flags, mode) } Module.fs_stat = function(path) { return FS.stat(path) } Module.fs_read = function(stream, buffer, offset, length, position) { return FS.read(stream, buffer, offset, length, position) } Module.fs_close = function(stream) { return FS.close(stream) } return Compare.ready } })() if (typeof exports === 'object' && typeof module === 'object') module.exports = Compare else if (typeof define === 'function' && define['amd']) define([], function() { return Compare }) else if (typeof exports === 'object') exports['Compare'] = Compare ================================================ FILE: src/IO/Compare/index.mjs ================================================ #!/usr/bin/env node /* eslint-env node */ import { Command } from 'commander/esm.mjs' import path from 'path' const program = new Command() import { readLocalFile, writeLocalFile, runPipelineNode, InterfaceTypes, } from 'itk-wasm' program .description('Create checkerboard from 2 images') .arguments(' ') .parse(process.argv) if (program.args.length < 3) { console.error('Please pass in 2 input file paths and output file path.') process.exit(1) } const pipelinePath = path.resolve('./emscripten-build/Compare') const inputFile1 = program.args[0] const inputFile2 = program.args[1] const outputFile = program.args[2] const boxes = program.args[3] try { const inputImage1 = await readLocalFile(inputFile1) const inputImage2 = await readLocalFile(inputFile2) const inputs = [ { type: InterfaceTypes.Image, data: inputImage1, }, { type: InterfaceTypes.Image, data: inputImage2, }, ] const desiredOutputs = [{ type: InterfaceTypes.Image }] const args = [ '0', '1', '0', '--boxes', boxes, // '--max-total-splits', // '10', // '--split', // '5', '--memory-io', ] const { outputs } = await runPipelineNode( pipelinePath, args, desiredOutputs, inputs ) const outputImage = outputs[0].data await writeLocalFile(outputImage, outputFile) } catch (error) { console.error('Error during processing:\n') console.error(error) } ================================================ FILE: src/IO/Compare/package.json ================================================ { "name": "checkerboard-itk-wasm", "version": "1.0.0", "description": "", "main": "index.js", "type": "module", "scripts": { "build": "itk-wasm -i itkwasm/emscripten:latest build", "build:debug": "itk-wasm -i itkwasm/emscripten:latest-debug build -- -DCMAKE_BUILD_TYPE=Debug", "test": "node index.mjs ../Downsample/cthead1.png ../Downsample/cthead1-bin.png output.png 5,8" }, "author": "", "license": "Apache-2.0", "dependencies": { "itk-image-io": "^1.0.0-b.84", "itk-wasm": "^1.0.0-b.84" } } ================================================ FILE: src/IO/ConglomerateMultiscaleSpatialImage.js ================================================ import MultiscaleSpatialImage from './MultiscaleSpatialImage' export class ConglomerateMultiscaleSpatialImage extends MultiscaleSpatialImage { constructor(images) { const { scaleInfo, imageType } = images.slice(1).reduce( (fused, { scaleInfo, imageType }) => ({ imageType: { ...imageType, components: fused.imageType.components + imageType.components, }, scaleInfo: fused.scaleInfo.map((scale, scaleIdx) => ({ ...scale, ranges: scale.ranges ? [...scale.ranges, ...scaleInfo[scaleIdx].ranges] : scale.ranges, // ranges may be undefined })), }), images[0] ) super(scaleInfo, imageType) this.images = images } async getImage(scale, worldBounds = []) { this.worldBoundsForBuildImage = worldBounds return super.getImage(scale, worldBounds) } async buildImage(scale /*bounds*/) { // Run sequentially rather than Promise.all to avoid hang during chunk fetching const builtImages = [] for (const image of this.images) { builtImages.push( await image.getImage(scale, this.worldBoundsForBuildImage) ) } return builtImages } } ================================================ FILE: src/IO/Downsample/.gitignore ================================================ /emscripten-build/* !emscripten-build/Downsample* resample.png ================================================ FILE: src/IO/Downsample/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) project(Downsample) set(CMAKE_CXX_STANDARD 17) set(io_components) if (NOT EMSCRIPTEN AND NOT WASI) set(io_components ITKIOPNG ITKIOMeta ITKIONRRD ) endif() find_package(ITK REQUIRED COMPONENTS ${io_components} WebAssemblyInterface ITKImageGrid ITKImageFunction GenericLabelInterpolator ) include(${ITK_USE_FILE}) add_executable(Downsample Downsample.cxx) target_link_libraries(Downsample PUBLIC ${ITK_LIBRARIES}) add_executable(DownsampleLabelImage DownsampleLabelImage.cxx) target_link_libraries(DownsampleLabelImage PUBLIC ${ITK_LIBRARIES}) enable_testing() add_test(NAME DownsampleTest COMMAND Downsample ${CMAKE_CURRENT_SOURCE_DIR}/cthead1.png ${CMAKE_CURRENT_BINARY_DIR}/cthead1.shrink.png 2,2 ) add_test(NAME DownsampleTestLabelImage COMMAND DownsampleLabelImage ${CMAKE_CURRENT_SOURCE_DIR}/cthead1-bin.png ${CMAKE_CURRENT_BINARY_DIR}/cthead1Label.shrink.png 2,2 ) ================================================ FILE: src/IO/Downsample/Downsample.cxx ================================================ /*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "itkBinShrinkImageFilter.h" #include "itkVectorImage.h" #include "itkResampleImageFilter.h" #include "itkLabelImageGaussianInterpolateImageFunction.h" #include "itkImageRegionSplitterSlowDimension.h" #include "itkExtractImageFilter.h" #include "itkRGBPixel.h" #include "itkRGBAPixel.h" #include "itkVectorImage.h" #include "itkOffset.h" #include "itkVector.h" #include "itkPoint.h" #include "itkCovariantVector.h" #include "itkSymmetricSecondRankTensor.h" #include "itkDiffusionTensor3D.h" #include #include "itkFixedArray.h" #include "itkArray.h" #include "itkMatrix.h" #include "itkVariableLengthVector.h" #include "itkVariableSizeMatrix.h" #include #include "itkPipeline.h" #include "itkInputImage.h" #include "itkOutputImage.h" #include "itkSupportInputImageTypes.h" #include "itkOutputTextStream.h" template int Downsample(itk::wasm::Pipeline &pipeline, itk::wasm::InputImage &inputImage) { using ImageType = TImage; pipeline.get_option("InputImage")->required(); using OutputImageType = itk::wasm::OutputImage; OutputImageType outputImage; pipeline.add_option("OutputImage", outputImage, "Output image")->required(); std::vector factors; pipeline.add_option("DownsampleFactors", factors, "Downsampling factors for each direction")->expected(2, 3)->delimiter(','); unsigned int maxTotalSplits = 1; pipeline.add_option("-m,--max-total-splits", maxTotalSplits, "Maximum total splits when processed in parallel"); unsigned int split = 0; pipeline.add_option("-s,--split", split, "Current processed split"); itk::wasm::OutputTextStream numberOfSplitsStream; auto numberOfSplitsStreamOption = pipeline.add_option("--number-of-splits", numberOfSplitsStream, "Number of splits"); ITK_WASM_PARSE(pipeline); using FilterType = itk::BinShrinkImageFilter; auto filter = FilterType::New(); filter->SetInput(inputImage.Get()); filter->SetShrinkFactor(0, factors[0]); filter->SetShrinkFactor(1, factors[1]); if (ImageType::ImageDimension > 2) { filter->SetShrinkFactor(2, factors[2]); } using ResampleFilterType = itk::ResampleImageFilter; auto resampleFilter = ResampleFilterType::New(); resampleFilter->SetInput(inputImage.Get()); filter->UpdateOutputInformation(); using ROIFilterType = itk::ExtractImageFilter; auto roiFilter = ROIFilterType::New(); using RegionType = typename ImageType::RegionType; const RegionType largestRegion(filter->GetOutput()->GetLargestPossibleRegion()); using SplitterType = itk::ImageRegionSplitterSlowDimension; auto splitter = SplitterType::New(); const unsigned int numberOfSplits = splitter->GetNumberOfSplits(largestRegion, maxTotalSplits); if (split >= numberOfSplits) { std::cerr << "Error: requested split: " << split << " is outside the number of splits: " << numberOfSplits << std::endl; return EXIT_FAILURE; } if (!numberOfSplitsStreamOption->empty()) { numberOfSplitsStream.Get() << numberOfSplits; } RegionType requestedRegion(largestRegion); splitter->GetSplit(split, numberOfSplits, requestedRegion); roiFilter->SetExtractionRegion(requestedRegion); roiFilter->SetInput(filter->GetOutput()); ITK_WASM_CATCH_EXCEPTION(pipeline, roiFilter->Update()); auto result = roiFilter->GetOutput(); outputImage.Set(result); return EXIT_SUCCESS; } template class PipelineFunctor { public: int operator()(itk::wasm::Pipeline &pipeline) { using ImageType = TImage; using InputImageType = itk::wasm::InputImage; InputImageType inputImage; pipeline.add_option("InputImage", inputImage, "Input image"); ITK_WASM_PRE_PARSE(pipeline); return Downsample(pipeline, inputImage); } }; int main(int argc, char *argv[]) { itk::wasm::Pipeline pipeline("Downsample", "Downsample an image", argc, argv); return itk::wasm::SupportInputImageTypes, itk::RGBAPixel, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::VariableLengthVector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::Vector, itk::CovariantVector, itk::CovariantVector, itk::CovariantVector, itk::CovariantVector>::Dimensions<2U, 3U>("InputImage", pipeline); } ================================================ FILE: src/IO/Downsample/DownsampleLabelImage.cxx ================================================ /*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "itkBinShrinkImageFilter.h" #include "itkVectorImage.h" #include "itkResampleImageFilter.h" #include "itkLabelImageGenericInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkImageRegionSplitterSlowDimension.h" #include "itkExtractImageFilter.h" #include "itkRGBPixel.h" #include "itkRGBAPixel.h" #include "itkVectorImage.h" #include "itkOffset.h" #include "itkVector.h" #include "itkPoint.h" #include "itkCovariantVector.h" #include "itkSymmetricSecondRankTensor.h" #include "itkDiffusionTensor3D.h" #include #include "itkFixedArray.h" #include "itkArray.h" #include "itkMatrix.h" #include "itkVariableLengthVector.h" #include "itkVariableSizeMatrix.h" #include #include "itkPipeline.h" #include "itkInputImage.h" #include "itkOutputImage.h" #include "itkSupportInputImageTypes.h" #include "itkOutputTextStream.h" template int DownsampleLabelImage(itk::wasm::Pipeline &pipeline, itk::wasm::InputImage &inputImage) { using ImageType = TImage; pipeline.get_option("InputImage")->required(); using OutputImageType = itk::wasm::OutputImage; OutputImageType outputImage; pipeline.add_option("OutputImage", outputImage, "Output image")->required(); std::vector factors; pipeline.add_option("DownsampleFactors", factors, "Downsampling factors for each direction")->expected(2, 3)->delimiter(','); unsigned int maxTotalSplits = 1; pipeline.add_option("-m,--max-total-splits", maxTotalSplits, "Maximum total splits when processed in parallel"); unsigned int split = 0; pipeline.add_option("-s,--split", split, "Current processed split"); itk::wasm::OutputTextStream numberOfSplitsStream; auto numberOfSplitsStreamOption = pipeline.add_option("--number-of-splits", numberOfSplitsStream, "Number of splits"); ITK_WASM_PARSE(pipeline); using FilterType = itk::BinShrinkImageFilter; auto filter = FilterType::New(); filter->SetInput(inputImage.Get()); filter->SetShrinkFactor(0, factors[0]); filter->SetShrinkFactor(1, factors[1]); if (ImageType::ImageDimension > 2) { filter->SetShrinkFactor(2, factors[2]); } using ResampleFilterType = itk::ResampleImageFilter; auto resampleFilter = ResampleFilterType::New(); resampleFilter->SetInput(inputImage.Get()); filter->UpdateOutputInformation(); using ROIFilterType = itk::ExtractImageFilter; auto roiFilter = ROIFilterType::New(); using RegionType = typename ImageType::RegionType; const RegionType largestRegion(filter->GetOutput()->GetLargestPossibleRegion()); using SplitterType = itk::ImageRegionSplitterSlowDimension; auto splitter = SplitterType::New(); const unsigned int numberOfSplits = splitter->GetNumberOfSplits(largestRegion, maxTotalSplits); if (split >= numberOfSplits) { std::cerr << "Error: requested split: " << split << " is outside the number of splits: " << numberOfSplits << std::endl; return EXIT_FAILURE; } if (!numberOfSplitsStreamOption->empty()) { numberOfSplitsStream.Get() << numberOfSplits; } RegionType requestedRegion(largestRegion); splitter->GetSplit(split, numberOfSplits, requestedRegion); roiFilter->SetExtractionRegion(requestedRegion); roiFilter->SetInput(resampleFilter->GetOutput()); const ImageType *shrunk = filter->GetOutput(); resampleFilter->SetSize(shrunk->GetLargestPossibleRegion().GetSize()); resampleFilter->SetOutputOrigin(shrunk->GetOrigin()); auto spacing = shrunk->GetSpacing(); resampleFilter->SetOutputSpacing(spacing); resampleFilter->SetOutputDirection(shrunk->GetDirection()); using CoordRepType = double; using InterpolatorType = itk::LabelImageGenericInterpolateImageFunction; auto interpolator = InterpolatorType::New(); resampleFilter->SetInterpolator(interpolator); try { roiFilter->Update(); } catch (std::exception &error) { std::cerr << "Error: " << error.what() << std::endl; return EXIT_FAILURE; } outputImage.Set(roiFilter->GetOutput()); return EXIT_SUCCESS; } template class PipelineFunctor { public: int operator()(itk::wasm::Pipeline &pipeline) { using ImageType = TImage; using InputImageType = itk::wasm::InputImage; InputImageType inputImage; pipeline.add_option("InputImage", inputImage, "Input image"); ITK_WASM_PRE_PARSE(pipeline); return DownsampleLabelImage(pipeline, inputImage); } }; int main(int argc, char *argv[]) { itk::wasm::Pipeline pipeline("DownsampleLabel", "Downsample a label image", argc, argv); return itk::wasm::SupportInputImageTypes::Dimensions<2U, 3U>("InputImage", pipeline); } ================================================ FILE: src/IO/Downsample/cypress/integration/load_data_spec.js ================================================ describe('Downsample images', () => { it('successfully downsamples an image', () => { cy.visit('http://localhost:8080/') cy.readFile('cthead1.png', null).then(headBuffer => { cy.get('input[type=file]').selectFile({ contents: headBuffer, fileName: 'cthead1.png', }) cy.get('textarea').contains('"imageType"') }) }) it('successfully downsamples a label image', () => { cy.visit('http://localhost:8080/') cy.readFile('cthead1-bin.png', null).then(headBuffer => { cy.get('input[type=checkbox]').check() cy.get('input[type=file]').selectFile({ contents: headBuffer, fileName: 'cthead1-bin.png', }) cy.get('textarea').contains('"imageType"') }) }) }) ================================================ FILE: src/IO/Downsample/cypress/plugins/index.cjs ================================================ /// // *********************************************************** // This example plugins/index.js can be used to load plugins // // You can change the location of this file or turn off loading // the plugins file with the 'pluginsFile' configuration option. // // You can read more here: // https://on.cypress.io/plugins-guide // *********************************************************** // This function is called when a project is opened or re-opened (e.g. due to // the project's config changing) /** * @type {Cypress.PluginConfig} */ // eslint-disable-next-line no-unused-vars module.exports = (on, config) => { // `on` is used to hook into various events Cypress emits // `config` is the resolved Cypress config } ================================================ FILE: src/IO/Downsample/cypress.json ================================================ {} ================================================ FILE: src/IO/Downsample/downsample.js ================================================ #!/usr/bin/env node import { Command } from 'commander/esm.mjs' import path from 'path' const program = new Command() import { readLocalFile, writeLocalFile, runPipelineNode, InterfaceTypes, } from 'itk-wasm' program .description('Downsample an image') .option( '-l, --label-image', 'Downsample as a label image as opposed to an intensity image' ) .arguments(' ') .parse(process.argv) if (program.args.length < 2) { console.error('Please pass in both the input and output file paths.') process.exit(1) } const inputFile = program.args[0] const outputFile = program.args[1] const pipelinePath = program.options.labelImage ? path.resolve('./emscripten-build/DownsampleLabelImage') : path.resolve('./emscripten-build/Downsample') console.log(pipelinePath) const factors = [2, 2, 2] try { const inputImage = await readLocalFile(inputFile) const inputs = [ { type: InterfaceTypes.Image, data: inputImage, }, ] const desiredOutputs = [ { type: InterfaceTypes.Image }, // { type: InterfaceTypes.TextStream }, ] const args = [ '0', '0', factors.join(','), // '--max-total-splits', '' + maxTotalSplits, // '--split', '' + index, // '--number-of-splits', '1', '--memory-io', ] const { stdout, stderr, outputs } = await runPipelineNode( pipelinePath, args, desiredOutputs, inputs ) const outputImage = outputs[0].data await writeLocalFile(outputImage, outputFile) } catch (error) { console.error('Error during processing:\n') console.error(error) } ================================================ FILE: src/IO/Downsample/emscripten-build/Downsample.js ================================================ var Downsample = (() => { var _scriptDir = import.meta.url return async function(Downsample) { Downsample = Downsample || {} var Module = typeof Downsample != 'undefined' ? Downsample : {} var readyPromiseResolve, readyPromiseReject Module['ready'] = new Promise(function(resolve, reject) { readyPromiseResolve = resolve readyPromiseReject = reject }) var mStdout = null var mStderr = null Module['resetModuleStdout'] = function() { mStdout = '' } Module['resetModuleStderr'] = function() { mStderr = '' } Module['print'] = function(text) { console.log(text) mStdout += text + '\n' } Module['printErr'] = function(text) { console.error(text) mStderr += text + '\n' } Module['getModuleStdout'] = function() { return mStdout } Module['getModuleStderr'] = function() { return mStderr } var moduleOverrides = Object.assign({}, Module) var arguments_ = [] var thisProgram = './this.program' var quit_ = (status, toThrow) => { throw toThrow } var ENVIRONMENT_IS_WEB = typeof window == 'object' var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function' var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string' var scriptDirectory = '' function locateFile(path) { if (Module['locateFile']) { return Module['locateFile'](path, scriptDirectory) } return scriptDirectory + path } var read_, readAsync, readBinary, setWindowTitle function logExceptionOnExit(e) { if (e instanceof ExitStatus) return let toLog = e err('exiting due to exception: ' + toLog) } if (ENVIRONMENT_IS_NODE) { const { createRequire: createRequire } = await import('module') var require = createRequire(import.meta.url) var fs = require('fs') var nodePath = require('path') if (ENVIRONMENT_IS_WORKER) { scriptDirectory = nodePath.dirname(scriptDirectory) + '/' } else { scriptDirectory = require('url').fileURLToPath( new URL('./', import.meta.url) ) } read_ = (filename, binary) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) return fs.readFileSync(filename, binary ? undefined : 'utf8') } readBinary = filename => { var ret = read_(filename, true) if (!ret.buffer) { ret = new Uint8Array(ret) } return ret } readAsync = (filename, onload, onerror) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) fs.readFile(filename, function(err, data) { if (err) onerror(err) else onload(data.buffer) }) } if (process['argv'].length > 1) { thisProgram = process['argv'][1].replace(/\\/g, '/') } arguments_ = process['argv'].slice(2) process['on']('uncaughtException', function(ex) { if (!(ex instanceof ExitStatus)) { throw ex } }) process['on']('unhandledRejection', function(reason) { throw reason }) quit_ = (status, toThrow) => { if (keepRuntimeAlive()) { process['exitCode'] = status throw toThrow } logExceptionOnExit(toThrow) process['exit'](status) } Module['inspect'] = function() { return '[Emscripten Module object]' } } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = self.location.href } else if (typeof document != 'undefined' && document.currentScript) { scriptDirectory = document.currentScript.src } if (_scriptDir) { scriptDirectory = _scriptDir } if (scriptDirectory.indexOf('blob:') !== 0) { scriptDirectory = scriptDirectory.substr( 0, scriptDirectory.replace(/[?#].*/, '').lastIndexOf('/') + 1 ) } else { scriptDirectory = '' } { read_ = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.send(null) return xhr.responseText } if (ENVIRONMENT_IS_WORKER) { readBinary = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.responseType = 'arraybuffer' xhr.send(null) return new Uint8Array(xhr.response) } } readAsync = (url, onload, onerror) => { var xhr = new XMLHttpRequest() xhr.open('GET', url, true) xhr.responseType = 'arraybuffer' xhr.onload = () => { if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { onload(xhr.response) return } onerror() } xhr.onerror = onerror xhr.send(null) } } setWindowTitle = title => (document.title = title) } else { } var out = Module['print'] || console.log.bind(console) var err = Module['printErr'] || console.warn.bind(console) Object.assign(Module, moduleOverrides) moduleOverrides = null if (Module['arguments']) arguments_ = Module['arguments'] if (Module['thisProgram']) thisProgram = Module['thisProgram'] if (Module['quit']) quit_ = Module['quit'] var wasmBinary if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'] var noExitRuntime = Module['noExitRuntime'] || true if (typeof WebAssembly != 'object') { abort('no native wasm support detected') } var wasmMemory var ABORT = false var EXITSTATUS function assert(condition, text) { if (!condition) { abort(text) } } var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { var endIdx = idx + maxBytesToRead var endPtr = idx while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)) } var str = '' while (idx < endPtr) { var u0 = heapOrArray[idx++] if (!(u0 & 128)) { str += String.fromCharCode(u0) continue } var u1 = heapOrArray[idx++] & 63 if ((u0 & 224) == 192) { str += String.fromCharCode(((u0 & 31) << 6) | u1) continue } var u2 = heapOrArray[idx++] & 63 if ((u0 & 240) == 224) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2 } else { u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63) } if (u0 < 65536) { str += String.fromCharCode(u0) } else { var ch = u0 - 65536 str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)) } } return str } function UTF8ToString(ptr, maxBytesToRead) { return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '' } function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { if (!(maxBytesToWrite > 0)) return 0 var startIdx = outIdx var endIdx = outIdx + maxBytesToWrite - 1 for (var i = 0; i < str.length; ++i) { var u = str.charCodeAt(i) if (u >= 55296 && u <= 57343) { var u1 = str.charCodeAt(++i) u = (65536 + ((u & 1023) << 10)) | (u1 & 1023) } if (u <= 127) { if (outIdx >= endIdx) break heap[outIdx++] = u } else if (u <= 2047) { if (outIdx + 1 >= endIdx) break heap[outIdx++] = 192 | (u >> 6) heap[outIdx++] = 128 | (u & 63) } else if (u <= 65535) { if (outIdx + 2 >= endIdx) break heap[outIdx++] = 224 | (u >> 12) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } else { if (outIdx + 3 >= endIdx) break heap[outIdx++] = 240 | (u >> 18) heap[outIdx++] = 128 | ((u >> 12) & 63) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } } heap[outIdx] = 0 return outIdx - startIdx } function stringToUTF8(str, outPtr, maxBytesToWrite) { return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite) } function lengthBytesUTF8(str) { var len = 0 for (var i = 0; i < str.length; ++i) { var c = str.charCodeAt(i) if (c <= 127) { len++ } else if (c <= 2047) { len += 2 } else if (c >= 55296 && c <= 57343) { len += 4 ++i } else { len += 3 } } return len } var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64 function updateGlobalBufferAndViews(buf) { buffer = buf Module['HEAP8'] = HEAP8 = new Int8Array(buf) Module['HEAP16'] = HEAP16 = new Int16Array(buf) Module['HEAP32'] = HEAP32 = new Int32Array(buf) Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf) Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf) Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf) Module['HEAPF32'] = HEAPF32 = new Float32Array(buf) Module['HEAPF64'] = HEAPF64 = new Float64Array(buf) } var INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 16777216 var wasmTable var __ATPRERUN__ = [] var __ATINIT__ = [] var __ATMAIN__ = [] var __ATPOSTRUN__ = [] var runtimeInitialized = false function keepRuntimeAlive() { return noExitRuntime } function preRun() { if (Module['preRun']) { if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']] while (Module['preRun'].length) { addOnPreRun(Module['preRun'].shift()) } } callRuntimeCallbacks(__ATPRERUN__) } function initRuntime() { runtimeInitialized = true if (!Module['noFSInit'] && !FS.init.initialized) FS.init() FS.ignorePermissions = false TTY.init() callRuntimeCallbacks(__ATINIT__) } function preMain() { callRuntimeCallbacks(__ATMAIN__) } function postRun() { if (Module['postRun']) { if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']] while (Module['postRun'].length) { addOnPostRun(Module['postRun'].shift()) } } callRuntimeCallbacks(__ATPOSTRUN__) } function addOnPreRun(cb) { __ATPRERUN__.unshift(cb) } function addOnInit(cb) { __ATINIT__.unshift(cb) } function addOnPostRun(cb) { __ATPOSTRUN__.unshift(cb) } var runDependencies = 0 var runDependencyWatcher = null var dependenciesFulfilled = null function getUniqueRunDependency(id) { return id } function addRunDependency(id) { runDependencies++ if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } } function removeRunDependency(id) { runDependencies-- if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } if (runDependencies == 0) { if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher) runDependencyWatcher = null } if (dependenciesFulfilled) { var callback = dependenciesFulfilled dependenciesFulfilled = null callback() } } } function abort(what) { if (Module['onAbort']) { Module['onAbort'](what) } what = 'Aborted(' + what + ')' err(what) ABORT = true EXITSTATUS = 1 what += '. Build with -sASSERTIONS for more info.' var e = new WebAssembly.RuntimeError(what) readyPromiseReject(e) throw e } var dataURIPrefix = 'data:application/octet-stream;base64,' function isDataURI(filename) { return filename.startsWith(dataURIPrefix) } function isFileURI(filename) { return filename.startsWith('file://') } var wasmBinaryFile if (Module['locateFile']) { wasmBinaryFile = 'Downsample.wasm' if (!isDataURI(wasmBinaryFile)) { wasmBinaryFile = locateFile(wasmBinaryFile) } } else { wasmBinaryFile = new URL('Downsample.wasm', import.meta.url).href } function getBinary(file) { try { if (file == wasmBinaryFile && wasmBinary) { return new Uint8Array(wasmBinary) } if (readBinary) { return readBinary(file) } throw 'both async and sync fetching of the wasm failed' } catch (err) { abort(err) } } function getBinaryPromise() { if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { if (typeof fetch == 'function' && !isFileURI(wasmBinaryFile)) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }) .then(function(response) { if (!response['ok']) { throw "failed to load wasm binary file at '" + wasmBinaryFile + "'" } return response['arrayBuffer']() }) .catch(function() { return getBinary(wasmBinaryFile) }) } else { if (readAsync) { return new Promise(function(resolve, reject) { readAsync( wasmBinaryFile, function(response) { resolve(new Uint8Array(response)) }, reject ) }) } } } return Promise.resolve().then(function() { return getBinary(wasmBinaryFile) }) } function createWasm() { var info = { a: asmLibraryArg } function receiveInstance(instance, module) { var exports = instance.exports Module['asm'] = exports wasmMemory = Module['asm']['s'] updateGlobalBufferAndViews(wasmMemory.buffer) wasmTable = Module['asm']['D'] addOnInit(Module['asm']['t']) removeRunDependency('wasm-instantiate') } addRunDependency('wasm-instantiate') function receiveInstantiationResult(result) { receiveInstance(result['instance']) } function instantiateArrayBuffer(receiver) { return getBinaryPromise() .then(function(binary) { return WebAssembly.instantiate(binary, info) }) .then(function(instance) { return instance }) .then(receiver, function(reason) { err('failed to asynchronously prepare wasm: ' + reason) abort(reason) }) } function instantiateAsync() { if ( !wasmBinary && typeof WebAssembly.instantiateStreaming == 'function' && !isDataURI(wasmBinaryFile) && !isFileURI(wasmBinaryFile) && !ENVIRONMENT_IS_NODE && typeof fetch == 'function' ) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then( function(response) { var result = WebAssembly.instantiateStreaming(response, info) return result.then(receiveInstantiationResult, function(reason) { err('wasm streaming compile failed: ' + reason) err('falling back to ArrayBuffer instantiation') return instantiateArrayBuffer(receiveInstantiationResult) }) } ) } else { return instantiateArrayBuffer(receiveInstantiationResult) } } if (Module['instantiateWasm']) { try { var exports = Module['instantiateWasm'](info, receiveInstance) return exports } catch (e) { err('Module.instantiateWasm callback failed with error: ' + e) readyPromiseReject(e) } } instantiateAsync().catch(readyPromiseReject) return {} } var tempDouble var tempI64 function ExitStatus(status) { this.name = 'ExitStatus' this.message = 'Program terminated with exit(' + status + ')' this.status = status } function callRuntimeCallbacks(callbacks) { while (callbacks.length > 0) { callbacks.shift()(Module) } } function ExceptionInfo(excPtr) { this.excPtr = excPtr this.ptr = excPtr - 24 this.set_type = function(type) { HEAPU32[(this.ptr + 4) >> 2] = type } this.get_type = function() { return HEAPU32[(this.ptr + 4) >> 2] } this.set_destructor = function(destructor) { HEAPU32[(this.ptr + 8) >> 2] = destructor } this.get_destructor = function() { return HEAPU32[(this.ptr + 8) >> 2] } this.set_refcount = function(refcount) { HEAP32[this.ptr >> 2] = refcount } this.set_caught = function(caught) { caught = caught ? 1 : 0 HEAP8[(this.ptr + 12) >> 0] = caught } this.get_caught = function() { return HEAP8[(this.ptr + 12) >> 0] != 0 } this.set_rethrown = function(rethrown) { rethrown = rethrown ? 1 : 0 HEAP8[(this.ptr + 13) >> 0] = rethrown } this.get_rethrown = function() { return HEAP8[(this.ptr + 13) >> 0] != 0 } this.init = function(type, destructor) { this.set_adjusted_ptr(0) this.set_type(type) this.set_destructor(destructor) this.set_refcount(0) this.set_caught(false) this.set_rethrown(false) } this.add_ref = function() { var value = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = value + 1 } this.release_ref = function() { var prev = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = prev - 1 return prev === 1 } this.set_adjusted_ptr = function(adjustedPtr) { HEAPU32[(this.ptr + 16) >> 2] = adjustedPtr } this.get_adjusted_ptr = function() { return HEAPU32[(this.ptr + 16) >> 2] } this.get_exception_ptr = function() { var isPointer = ___cxa_is_pointer_type(this.get_type()) if (isPointer) { return HEAPU32[this.excPtr >> 2] } var adjusted = this.get_adjusted_ptr() if (adjusted !== 0) return adjusted return this.excPtr } } var exceptionLast = 0 var uncaughtExceptionCount = 0 function ___cxa_throw(ptr, type, destructor) { var info = new ExceptionInfo(ptr) info.init(type, destructor) exceptionLast = ptr uncaughtExceptionCount++ throw ptr } function setErrNo(value) { HEAP32[___errno_location() >> 2] = value return value } var PATH = { isAbs: path => path.charAt(0) === '/', splitPath: filename => { var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/ return splitPathRe.exec(filename).slice(1) }, normalizeArray: (parts, allowAboveRoot) => { var up = 0 for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i] if (last === '.') { parts.splice(i, 1) } else if (last === '..') { parts.splice(i, 1) up++ } else if (up) { parts.splice(i, 1) up-- } } if (allowAboveRoot) { for (; up; up--) { parts.unshift('..') } } return parts }, normalize: path => { var isAbsolute = PATH.isAbs(path), trailingSlash = path.substr(-1) === '/' path = PATH.normalizeArray( path.split('/').filter(p => !!p), !isAbsolute ).join('/') if (!path && !isAbsolute) { path = '.' } if (path && trailingSlash) { path += '/' } return (isAbsolute ? '/' : '') + path }, dirname: path => { var result = PATH.splitPath(path), root = result[0], dir = result[1] if (!root && !dir) { return '.' } if (dir) { dir = dir.substr(0, dir.length - 1) } return root + dir }, basename: path => { if (path === '/') return '/' path = PATH.normalize(path) path = path.replace(/\/$/, '') var lastSlash = path.lastIndexOf('/') if (lastSlash === -1) return path return path.substr(lastSlash + 1) }, join: function() { var paths = Array.prototype.slice.call(arguments) return PATH.normalize(paths.join('/')) }, join2: (l, r) => { return PATH.normalize(l + '/' + r) }, } function getRandomDevice() { if ( typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function' ) { var randomBuffer = new Uint8Array(1) return () => { crypto.getRandomValues(randomBuffer) return randomBuffer[0] } } else if (ENVIRONMENT_IS_NODE) { try { var crypto_module = require('crypto') return () => crypto_module['randomBytes'](1)[0] } catch (e) {} } return () => abort('randomDevice') } var PATH_FS = { resolve: function() { var resolvedPath = '', resolvedAbsolute = false for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = i >= 0 ? arguments[i] : FS.cwd() if (typeof path != 'string') { throw new TypeError('Arguments to path.resolve must be strings') } else if (!path) { return '' } resolvedPath = path + '/' + resolvedPath resolvedAbsolute = PATH.isAbs(path) } resolvedPath = PATH.normalizeArray( resolvedPath.split('/').filter(p => !!p), !resolvedAbsolute ).join('/') return (resolvedAbsolute ? '/' : '') + resolvedPath || '.' }, relative: (from, to) => { from = PATH_FS.resolve(from).substr(1) to = PATH_FS.resolve(to).substr(1) function trim(arr) { var start = 0 for (; start < arr.length; start++) { if (arr[start] !== '') break } var end = arr.length - 1 for (; end >= 0; end--) { if (arr[end] !== '') break } if (start > end) return [] return arr.slice(start, end - start + 1) } var fromParts = trim(from.split('/')) var toParts = trim(to.split('/')) var length = Math.min(fromParts.length, toParts.length) var samePartsLength = length for (var i = 0; i < length; i++) { if (fromParts[i] !== toParts[i]) { samePartsLength = i break } } var outputParts = [] for (var i = samePartsLength; i < fromParts.length; i++) { outputParts.push('..') } outputParts = outputParts.concat(toParts.slice(samePartsLength)) return outputParts.join('/') }, } function intArrayFromString(stringy, dontAddNull, length) { var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1 var u8array = new Array(len) var numBytesWritten = stringToUTF8Array( stringy, u8array, 0, u8array.length ) if (dontAddNull) u8array.length = numBytesWritten return u8array } var TTY = { ttys: [], init: function() {}, shutdown: function() {}, register: function(dev, ops) { TTY.ttys[dev] = { input: [], output: [], ops: ops } FS.registerDevice(dev, TTY.stream_ops) }, stream_ops: { open: function(stream) { var tty = TTY.ttys[stream.node.rdev] if (!tty) { throw new FS.ErrnoError(43) } stream.tty = tty stream.seekable = false }, close: function(stream) { stream.tty.ops.fsync(stream.tty) }, fsync: function(stream) { stream.tty.ops.fsync(stream.tty) }, read: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.get_char) { throw new FS.ErrnoError(60) } var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = stream.tty.ops.get_char(stream.tty) } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.put_char) { throw new FS.ErrnoError(60) } try { for (var i = 0; i < length; i++) { stream.tty.ops.put_char(stream.tty, buffer[offset + i]) } } catch (e) { throw new FS.ErrnoError(29) } if (length) { stream.node.timestamp = Date.now() } return i }, }, default_tty_ops: { get_char: function(tty) { if (!tty.input.length) { var result = null if (ENVIRONMENT_IS_NODE) { var BUFSIZE = 256 var buf = Buffer.alloc(BUFSIZE) var bytesRead = 0 try { bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE, -1) } catch (e) { if (e.toString().includes('EOF')) bytesRead = 0 else throw e } if (bytesRead > 0) { result = buf.slice(0, bytesRead).toString('utf-8') } else { result = null } } else if ( typeof window != 'undefined' && typeof window.prompt == 'function' ) { result = window.prompt('Input: ') if (result !== null) { result += '\n' } } else if (typeof readline == 'function') { result = readline() if (result !== null) { result += '\n' } } if (!result) { return null } tty.input = intArrayFromString(result, true) } return tty.input.shift() }, put_char: function(tty, val) { if (val === null || val === 10) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, default_tty1_ops: { put_char: function(tty, val) { if (val === null || val === 10) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, } function mmapAlloc(size) { abort() } var MEMFS = { ops_table: null, mount: function(mount) { return MEMFS.createNode(null, '/', 16384 | 511, 0) }, createNode: function(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { throw new FS.ErrnoError(63) } if (!MEMFS.ops_table) { MEMFS.ops_table = { dir: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, lookup: MEMFS.node_ops.lookup, mknod: MEMFS.node_ops.mknod, rename: MEMFS.node_ops.rename, unlink: MEMFS.node_ops.unlink, rmdir: MEMFS.node_ops.rmdir, readdir: MEMFS.node_ops.readdir, symlink: MEMFS.node_ops.symlink, }, stream: { llseek: MEMFS.stream_ops.llseek }, }, file: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: { llseek: MEMFS.stream_ops.llseek, read: MEMFS.stream_ops.read, write: MEMFS.stream_ops.write, allocate: MEMFS.stream_ops.allocate, mmap: MEMFS.stream_ops.mmap, msync: MEMFS.stream_ops.msync, }, }, link: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, readlink: MEMFS.node_ops.readlink, }, stream: {}, }, chrdev: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: FS.chrdev_stream_ops, }, } } var node = FS.createNode(parent, name, mode, dev) if (FS.isDir(node.mode)) { node.node_ops = MEMFS.ops_table.dir.node node.stream_ops = MEMFS.ops_table.dir.stream node.contents = {} } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node node.stream_ops = MEMFS.ops_table.file.stream node.usedBytes = 0 node.contents = null } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node node.stream_ops = MEMFS.ops_table.link.stream } else if (FS.isChrdev(node.mode)) { node.node_ops = MEMFS.ops_table.chrdev.node node.stream_ops = MEMFS.ops_table.chrdev.stream } node.timestamp = Date.now() if (parent) { parent.contents[name] = node parent.timestamp = node.timestamp } return node }, getFileDataAsTypedArray: function(node) { if (!node.contents) return new Uint8Array(0) if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes) return new Uint8Array(node.contents) }, expandFileStorage: function(node, newCapacity) { var prevCapacity = node.contents ? node.contents.length : 0 if (prevCapacity >= newCapacity) return var CAPACITY_DOUBLING_MAX = 1024 * 1024 newCapacity = Math.max( newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2 : 1.125)) >>> 0 ) if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256) var oldContents = node.contents node.contents = new Uint8Array(newCapacity) if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0) }, resizeFileStorage: function(node, newSize) { if (node.usedBytes == newSize) return if (newSize == 0) { node.contents = null node.usedBytes = 0 } else { var oldContents = node.contents node.contents = new Uint8Array(newSize) if (oldContents) { node.contents.set( oldContents.subarray(0, Math.min(newSize, node.usedBytes)) ) } node.usedBytes = newSize } }, node_ops: { getattr: function(node) { var attr = {} attr.dev = FS.isChrdev(node.mode) ? node.id : 1 attr.ino = node.id attr.mode = node.mode attr.nlink = 1 attr.uid = 0 attr.gid = 0 attr.rdev = node.rdev if (FS.isDir(node.mode)) { attr.size = 4096 } else if (FS.isFile(node.mode)) { attr.size = node.usedBytes } else if (FS.isLink(node.mode)) { attr.size = node.link.length } else { attr.size = 0 } attr.atime = new Date(node.timestamp) attr.mtime = new Date(node.timestamp) attr.ctime = new Date(node.timestamp) attr.blksize = 4096 attr.blocks = Math.ceil(attr.size / attr.blksize) return attr }, setattr: function(node, attr) { if (attr.mode !== undefined) { node.mode = attr.mode } if (attr.timestamp !== undefined) { node.timestamp = attr.timestamp } if (attr.size !== undefined) { MEMFS.resizeFileStorage(node, attr.size) } }, lookup: function(parent, name) { throw FS.genericErrors[44] }, mknod: function(parent, name, mode, dev) { return MEMFS.createNode(parent, name, mode, dev) }, rename: function(old_node, new_dir, new_name) { if (FS.isDir(old_node.mode)) { var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (new_node) { for (var i in new_node.contents) { throw new FS.ErrnoError(55) } } } delete old_node.parent.contents[old_node.name] old_node.parent.timestamp = Date.now() old_node.name = new_name new_dir.contents[new_name] = old_node new_dir.timestamp = old_node.parent.timestamp old_node.parent = new_dir }, unlink: function(parent, name) { delete parent.contents[name] parent.timestamp = Date.now() }, rmdir: function(parent, name) { var node = FS.lookupNode(parent, name) for (var i in node.contents) { throw new FS.ErrnoError(55) } delete parent.contents[name] parent.timestamp = Date.now() }, readdir: function(node) { var entries = ['.', '..'] for (var key in node.contents) { if (!node.contents.hasOwnProperty(key)) { continue } entries.push(key) } return entries }, symlink: function(parent, newname, oldpath) { var node = MEMFS.createNode(parent, newname, 511 | 40960, 0) node.link = oldpath return node }, readlink: function(node) { if (!FS.isLink(node.mode)) { throw new FS.ErrnoError(28) } return node.link }, }, stream_ops: { read: function(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= stream.node.usedBytes) return 0 var size = Math.min(stream.node.usedBytes - position, length) if (size > 8 && contents.subarray) { buffer.set(contents.subarray(position, position + size), offset) } else { for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i] } return size }, write: function(stream, buffer, offset, length, position, canOwn) { if (buffer.buffer === HEAP8.buffer) { canOwn = false } if (!length) return 0 var node = stream.node node.timestamp = Date.now() if (buffer.subarray && (!node.contents || node.contents.subarray)) { if (canOwn) { node.contents = buffer.subarray(offset, offset + length) node.usedBytes = length return length } else if (node.usedBytes === 0 && position === 0) { node.contents = buffer.slice(offset, offset + length) node.usedBytes = length return length } else if (position + length <= node.usedBytes) { node.contents.set( buffer.subarray(offset, offset + length), position ) return length } } MEMFS.expandFileStorage(node, position + length) if (node.contents.subarray && buffer.subarray) { node.contents.set( buffer.subarray(offset, offset + length), position ) } else { for (var i = 0; i < length; i++) { node.contents[position + i] = buffer[offset + i] } } node.usedBytes = Math.max(node.usedBytes, position + length) return length }, llseek: function(stream, offset, whence) { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { position += stream.node.usedBytes } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, allocate: function(stream, offset, length) { MEMFS.expandFileStorage(stream.node, offset + length) stream.node.usedBytes = Math.max( stream.node.usedBytes, offset + length ) }, mmap: function(stream, length, position, prot, flags) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr var allocated var contents = stream.node.contents if (!(flags & 2) && contents.buffer === buffer) { allocated = false ptr = contents.byteOffset } else { if (position > 0 || position + length < contents.length) { if (contents.subarray) { contents = contents.subarray(position, position + length) } else { contents = Array.prototype.slice.call( contents, position, position + length ) } } allocated = true ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } HEAP8.set(contents, ptr) } return { ptr: ptr, allocated: allocated } }, msync: function(stream, buffer, offset, length, mmapFlags) { MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } function asyncLoad(url, onload, onerror, noRunDep) { var dep = !noRunDep ? getUniqueRunDependency('al ' + url) : '' readAsync( url, arrayBuffer => { assert( arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).' ) onload(new Uint8Array(arrayBuffer)) if (dep) removeRunDependency(dep) }, event => { if (onerror) { onerror() } else { throw 'Loading data file "' + url + '" failed.' } } ) if (dep) addRunDependency(dep) } var ERRNO_CODES = {} var NODEFS = { isWindows: false, staticInit: () => { NODEFS.isWindows = !!process.platform.match(/^win/) var flags = process['binding']('constants') if (flags['fs']) { flags = flags['fs'] } NODEFS.flagsForNodeMap = { 1024: flags['O_APPEND'], 64: flags['O_CREAT'], 128: flags['O_EXCL'], 256: flags['O_NOCTTY'], 0: flags['O_RDONLY'], 2: flags['O_RDWR'], 4096: flags['O_SYNC'], 512: flags['O_TRUNC'], 1: flags['O_WRONLY'], 131072: flags['O_NOFOLLOW'], } }, convertNodeCode: e => { var code = e.code return ERRNO_CODES[code] }, mount: mount => { return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0) }, createNode: (parent, name, mode, dev) => { if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { throw new FS.ErrnoError(28) } var node = FS.createNode(parent, name, mode) node.node_ops = NODEFS.node_ops node.stream_ops = NODEFS.stream_ops return node }, getMode: path => { var stat try { stat = fs.lstatSync(path) if (NODEFS.isWindows) { stat.mode = stat.mode | ((stat.mode & 292) >> 2) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return stat.mode }, realPath: node => { var parts = [] while (node.parent !== node) { parts.push(node.name) node = node.parent } parts.push(node.mount.opts.root) parts.reverse() return PATH.join.apply(null, parts) }, flagsForNode: flags => { flags &= ~2097152 flags &= ~2048 flags &= ~32768 flags &= ~524288 flags &= ~65536 var newFlags = 0 for (var k in NODEFS.flagsForNodeMap) { if (flags & k) { newFlags |= NODEFS.flagsForNodeMap[k] flags ^= k } } if (flags) { throw new FS.ErrnoError(28) } return newFlags }, node_ops: { getattr: node => { var path = NODEFS.realPath(node) var stat try { stat = fs.lstatSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } if (NODEFS.isWindows && !stat.blksize) { stat.blksize = 4096 } if (NODEFS.isWindows && !stat.blocks) { stat.blocks = ((stat.size + stat.blksize - 1) / stat.blksize) | 0 } return { dev: stat.dev, ino: stat.ino, mode: stat.mode, nlink: stat.nlink, uid: stat.uid, gid: stat.gid, rdev: stat.rdev, size: stat.size, atime: stat.atime, mtime: stat.mtime, ctime: stat.ctime, blksize: stat.blksize, blocks: stat.blocks, } }, setattr: (node, attr) => { var path = NODEFS.realPath(node) try { if (attr.mode !== undefined) { fs.chmodSync(path, attr.mode) node.mode = attr.mode } if (attr.timestamp !== undefined) { var date = new Date(attr.timestamp) fs.utimesSync(path, date, date) } if (attr.size !== undefined) { fs.truncateSync(path, attr.size) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, lookup: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) var mode = NODEFS.getMode(path) return NODEFS.createNode(parent, name, mode) }, mknod: (parent, name, mode, dev) => { var node = NODEFS.createNode(parent, name, mode, dev) var path = NODEFS.realPath(node) try { if (FS.isDir(node.mode)) { fs.mkdirSync(path, node.mode) } else { fs.writeFileSync(path, '', { mode: node.mode }) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return node }, rename: (oldNode, newDir, newName) => { var oldPath = NODEFS.realPath(oldNode) var newPath = PATH.join2(NODEFS.realPath(newDir), newName) try { fs.renameSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } oldNode.name = newName }, unlink: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.unlinkSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, rmdir: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.rmdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readdir: node => { var path = NODEFS.realPath(node) try { return fs.readdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, symlink: (parent, newName, oldPath) => { var newPath = PATH.join2(NODEFS.realPath(parent), newName) try { fs.symlinkSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readlink: node => { var path = NODEFS.realPath(node) try { path = fs.readlinkSync(path) path = nodePath.relative( nodePath.resolve(node.mount.opts.root), path ) return path } catch (e) { if (!e.code) throw e if (e.code === 'UNKNOWN') throw new FS.ErrnoError(28) throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, }, stream_ops: { open: stream => { var path = NODEFS.realPath(stream.node) try { if (FS.isFile(stream.node.mode)) { stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags)) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, close: stream => { try { if (FS.isFile(stream.node.mode) && stream.nfd) { fs.closeSync(stream.nfd) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, read: (stream, buffer, offset, length, position) => { if (length === 0) return 0 try { return fs.readSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, write: (stream, buffer, offset, length, position) => { try { return fs.writeSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, llseek: (stream, offset, whence) => { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { try { var stat = fs.fstatSync(stream.nfd) position += stat.size } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, mmap: (stream, length, position, prot, flags) => { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr = mmapAlloc(length) NODEFS.stream_ops.read(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } }, msync: (stream, buffer, offset, length, mmapFlags) => { NODEFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } var FS = { root: null, mounts: [], devices: {}, streams: [], nextInode: 1, nameTable: null, currentPath: '/', initialized: false, ignorePermissions: true, ErrnoError: null, genericErrors: {}, filesystems: null, syncFSRequests: 0, lookupPath: (path, opts = {}) => { path = PATH_FS.resolve(path) if (!path) return { path: '', node: null } var defaults = { follow_mount: true, recurse_count: 0 } opts = Object.assign(defaults, opts) if (opts.recurse_count > 8) { throw new FS.ErrnoError(32) } var parts = path.split('/').filter(p => !!p) var current = FS.root var current_path = '/' for (var i = 0; i < parts.length; i++) { var islast = i === parts.length - 1 if (islast && opts.parent) { break } current = FS.lookupNode(current, parts[i]) current_path = PATH.join2(current_path, parts[i]) if (FS.isMountpoint(current)) { if (!islast || (islast && opts.follow_mount)) { current = current.mounted.root } } if (!islast || opts.follow) { var count = 0 while (FS.isLink(current.mode)) { var link = FS.readlink(current_path) current_path = PATH_FS.resolve(PATH.dirname(current_path), link) var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count + 1, }) current = lookup.node if (count++ > 40) { throw new FS.ErrnoError(32) } } } } return { path: current_path, node: current } }, getPath: node => { var path while (true) { if (FS.isRoot(node)) { var mount = node.mount.mountpoint if (!path) return mount return mount[mount.length - 1] !== '/' ? mount + '/' + path : mount + path } path = path ? node.name + '/' + path : node.name node = node.parent } }, hashName: (parentid, name) => { var hash = 0 for (var i = 0; i < name.length; i++) { hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0 } return ((parentid + hash) >>> 0) % FS.nameTable.length }, hashAddNode: node => { var hash = FS.hashName(node.parent.id, node.name) node.name_next = FS.nameTable[hash] FS.nameTable[hash] = node }, hashRemoveNode: node => { var hash = FS.hashName(node.parent.id, node.name) if (FS.nameTable[hash] === node) { FS.nameTable[hash] = node.name_next } else { var current = FS.nameTable[hash] while (current) { if (current.name_next === node) { current.name_next = node.name_next break } current = current.name_next } } }, lookupNode: (parent, name) => { var errCode = FS.mayLookup(parent) if (errCode) { throw new FS.ErrnoError(errCode, parent) } var hash = FS.hashName(parent.id, name) for (var node = FS.nameTable[hash]; node; node = node.name_next) { var nodeName = node.name if (node.parent.id === parent.id && nodeName === name) { return node } } return FS.lookup(parent, name) }, createNode: (parent, name, mode, rdev) => { var node = new FS.FSNode(parent, name, mode, rdev) FS.hashAddNode(node) return node }, destroyNode: node => { FS.hashRemoveNode(node) }, isRoot: node => { return node === node.parent }, isMountpoint: node => { return !!node.mounted }, isFile: mode => { return (mode & 61440) === 32768 }, isDir: mode => { return (mode & 61440) === 16384 }, isLink: mode => { return (mode & 61440) === 40960 }, isChrdev: mode => { return (mode & 61440) === 8192 }, isBlkdev: mode => { return (mode & 61440) === 24576 }, isFIFO: mode => { return (mode & 61440) === 4096 }, isSocket: mode => { return (mode & 49152) === 49152 }, flagModes: { r: 0, 'r+': 2, w: 577, 'w+': 578, a: 1089, 'a+': 1090 }, modeStringToFlags: str => { var flags = FS.flagModes[str] if (typeof flags == 'undefined') { throw new Error('Unknown file open mode: ' + str) } return flags }, flagsToPermissionString: flag => { var perms = ['r', 'w', 'rw'][flag & 3] if (flag & 512) { perms += 'w' } return perms }, nodePermissions: (node, perms) => { if (FS.ignorePermissions) { return 0 } if (perms.includes('r') && !(node.mode & 292)) { return 2 } else if (perms.includes('w') && !(node.mode & 146)) { return 2 } else if (perms.includes('x') && !(node.mode & 73)) { return 2 } return 0 }, mayLookup: dir => { var errCode = FS.nodePermissions(dir, 'x') if (errCode) return errCode if (!dir.node_ops.lookup) return 2 return 0 }, mayCreate: (dir, name) => { try { var node = FS.lookupNode(dir, name) return 20 } catch (e) {} return FS.nodePermissions(dir, 'wx') }, mayDelete: (dir, name, isdir) => { var node try { node = FS.lookupNode(dir, name) } catch (e) { return e.errno } var errCode = FS.nodePermissions(dir, 'wx') if (errCode) { return errCode } if (isdir) { if (!FS.isDir(node.mode)) { return 54 } if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return 10 } } else { if (FS.isDir(node.mode)) { return 31 } } return 0 }, mayOpen: (node, flags) => { if (!node) { return 44 } if (FS.isLink(node.mode)) { return 32 } else if (FS.isDir(node.mode)) { if (FS.flagsToPermissionString(flags) !== 'r' || flags & 512) { return 31 } } return FS.nodePermissions(node, FS.flagsToPermissionString(flags)) }, MAX_OPEN_FDS: 4096, nextfd: (fd_start = 0, fd_end = FS.MAX_OPEN_FDS) => { for (var fd = fd_start; fd <= fd_end; fd++) { if (!FS.streams[fd]) { return fd } } throw new FS.ErrnoError(33) }, getStream: fd => FS.streams[fd], createStream: (stream, fd_start, fd_end) => { if (!FS.FSStream) { FS.FSStream = function() { this.shared = {} } FS.FSStream.prototype = {} Object.defineProperties(FS.FSStream.prototype, { object: { get: function() { return this.node }, set: function(val) { this.node = val }, }, isRead: { get: function() { return (this.flags & 2097155) !== 1 }, }, isWrite: { get: function() { return (this.flags & 2097155) !== 0 }, }, isAppend: { get: function() { return this.flags & 1024 }, }, flags: { get: function() { return this.shared.flags }, set: function(val) { this.shared.flags = val }, }, position: { get: function() { return this.shared.position }, set: function(val) { this.shared.position = val }, }, }) } stream = Object.assign(new FS.FSStream(), stream) var fd = FS.nextfd(fd_start, fd_end) stream.fd = fd FS.streams[fd] = stream return stream }, closeStream: fd => { FS.streams[fd] = null }, chrdev_stream_ops: { open: stream => { var device = FS.getDevice(stream.node.rdev) stream.stream_ops = device.stream_ops if (stream.stream_ops.open) { stream.stream_ops.open(stream) } }, llseek: () => { throw new FS.ErrnoError(70) }, }, major: dev => dev >> 8, minor: dev => dev & 255, makedev: (ma, mi) => (ma << 8) | mi, registerDevice: (dev, ops) => { FS.devices[dev] = { stream_ops: ops } }, getDevice: dev => FS.devices[dev], getMounts: mount => { var mounts = [] var check = [mount] while (check.length) { var m = check.pop() mounts.push(m) check.push.apply(check, m.mounts) } return mounts }, syncfs: (populate, callback) => { if (typeof populate == 'function') { callback = populate populate = false } FS.syncFSRequests++ if (FS.syncFSRequests > 1) { err( 'warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work' ) } var mounts = FS.getMounts(FS.root.mount) var completed = 0 function doCallback(errCode) { FS.syncFSRequests-- return callback(errCode) } function done(errCode) { if (errCode) { if (!done.errored) { done.errored = true return doCallback(errCode) } return } if (++completed >= mounts.length) { doCallback(null) } } mounts.forEach(mount => { if (!mount.type.syncfs) { return done(null) } mount.type.syncfs(mount, populate, done) }) }, mount: (type, opts, mountpoint) => { var root = mountpoint === '/' var pseudo = !mountpoint var node if (root && FS.root) { throw new FS.ErrnoError(10) } else if (!root && !pseudo) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) mountpoint = lookup.path node = lookup.node if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } if (!FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } } var mount = { type: type, opts: opts, mountpoint: mountpoint, mounts: [], } var mountRoot = type.mount(mount) mountRoot.mount = mount mount.root = mountRoot if (root) { FS.root = mountRoot } else if (node) { node.mounted = mount if (node.mount) { node.mount.mounts.push(mount) } } return mountRoot }, unmount: mountpoint => { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) if (!FS.isMountpoint(lookup.node)) { throw new FS.ErrnoError(28) } var node = lookup.node var mount = node.mounted var mounts = FS.getMounts(mount) Object.keys(FS.nameTable).forEach(hash => { var current = FS.nameTable[hash] while (current) { var next = current.name_next if (mounts.includes(current.mount)) { FS.destroyNode(current) } current = next } }) node.mounted = null var idx = node.mount.mounts.indexOf(mount) node.mount.mounts.splice(idx, 1) }, lookup: (parent, name) => { return parent.node_ops.lookup(parent, name) }, mknod: (path, mode, dev) => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) if (!name || name === '.' || name === '..') { throw new FS.ErrnoError(28) } var errCode = FS.mayCreate(parent, name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.mknod) { throw new FS.ErrnoError(63) } return parent.node_ops.mknod(parent, name, mode, dev) }, create: (path, mode) => { mode = mode !== undefined ? mode : 438 mode &= 4095 mode |= 32768 return FS.mknod(path, mode, 0) }, mkdir: (path, mode) => { mode = mode !== undefined ? mode : 511 mode &= 511 | 512 mode |= 16384 return FS.mknod(path, mode, 0) }, mkdirTree: (path, mode) => { var dirs = path.split('/') var d = '' for (var i = 0; i < dirs.length; ++i) { if (!dirs[i]) continue d += '/' + dirs[i] try { FS.mkdir(d, mode) } catch (e) { if (e.errno != 20) throw e } } }, mkdev: (path, mode, dev) => { if (typeof dev == 'undefined') { dev = mode mode = 438 } mode |= 8192 return FS.mknod(path, mode, dev) }, symlink: (oldpath, newpath) => { if (!PATH_FS.resolve(oldpath)) { throw new FS.ErrnoError(44) } var lookup = FS.lookupPath(newpath, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var newname = PATH.basename(newpath) var errCode = FS.mayCreate(parent, newname) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.symlink) { throw new FS.ErrnoError(63) } return parent.node_ops.symlink(parent, newname, oldpath) }, rename: (old_path, new_path) => { var old_dirname = PATH.dirname(old_path) var new_dirname = PATH.dirname(new_path) var old_name = PATH.basename(old_path) var new_name = PATH.basename(new_path) var lookup, old_dir, new_dir lookup = FS.lookupPath(old_path, { parent: true }) old_dir = lookup.node lookup = FS.lookupPath(new_path, { parent: true }) new_dir = lookup.node if (!old_dir || !new_dir) throw new FS.ErrnoError(44) if (old_dir.mount !== new_dir.mount) { throw new FS.ErrnoError(75) } var old_node = FS.lookupNode(old_dir, old_name) var relative = PATH_FS.relative(old_path, new_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(28) } relative = PATH_FS.relative(new_path, old_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(55) } var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (old_node === new_node) { return } var isdir = FS.isDir(old_node.mode) var errCode = FS.mayDelete(old_dir, old_name, isdir) if (errCode) { throw new FS.ErrnoError(errCode) } errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!old_dir.node_ops.rename) { throw new FS.ErrnoError(63) } if ( FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node)) ) { throw new FS.ErrnoError(10) } if (new_dir !== old_dir) { errCode = FS.nodePermissions(old_dir, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } } FS.hashRemoveNode(old_node) try { old_dir.node_ops.rename(old_node, new_dir, new_name) } catch (e) { throw e } finally { FS.hashAddNode(old_node) } }, rmdir: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, true) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.rmdir) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.rmdir(parent, name) FS.destroyNode(node) }, readdir: path => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node if (!node.node_ops.readdir) { throw new FS.ErrnoError(54) } return node.node_ops.readdir(node) }, unlink: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, false) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.unlink) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.unlink(parent, name) FS.destroyNode(node) }, readlink: path => { var lookup = FS.lookupPath(path) var link = lookup.node if (!link) { throw new FS.ErrnoError(44) } if (!link.node_ops.readlink) { throw new FS.ErrnoError(28) } return PATH_FS.resolve( FS.getPath(link.parent), link.node_ops.readlink(link) ) }, stat: (path, dontFollow) => { var lookup = FS.lookupPath(path, { follow: !dontFollow }) var node = lookup.node if (!node) { throw new FS.ErrnoError(44) } if (!node.node_ops.getattr) { throw new FS.ErrnoError(63) } return node.node_ops.getattr(node) }, lstat: path => { return FS.stat(path, true) }, chmod: (path, mode, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { mode: (mode & 4095) | (node.mode & ~4095), timestamp: Date.now(), }) }, lchmod: (path, mode) => { FS.chmod(path, mode, true) }, fchmod: (fd, mode) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chmod(stream.node, mode) }, chown: (path, uid, gid, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { timestamp: Date.now() }) }, lchown: (path, uid, gid) => { FS.chown(path, uid, gid, true) }, fchown: (fd, uid, gid) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chown(stream.node, uid, gid) }, truncate: (path, len) => { if (len < 0) { throw new FS.ErrnoError(28) } var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: true }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } if (FS.isDir(node.mode)) { throw new FS.ErrnoError(31) } if (!FS.isFile(node.mode)) { throw new FS.ErrnoError(28) } var errCode = FS.nodePermissions(node, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } node.node_ops.setattr(node, { size: len, timestamp: Date.now() }) }, ftruncate: (fd, len) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(28) } FS.truncate(stream.node, len) }, utime: (path, atime, mtime) => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node node.node_ops.setattr(node, { timestamp: Math.max(atime, mtime) }) }, open: (path, flags, mode) => { if (path === '') { throw new FS.ErrnoError(44) } flags = typeof flags == 'string' ? FS.modeStringToFlags(flags) : flags mode = typeof mode == 'undefined' ? 438 : mode if (flags & 64) { mode = (mode & 4095) | 32768 } else { mode = 0 } var node if (typeof path == 'object') { node = path } else { path = PATH.normalize(path) try { var lookup = FS.lookupPath(path, { follow: !(flags & 131072) }) node = lookup.node } catch (e) {} } var created = false if (flags & 64) { if (node) { if (flags & 128) { throw new FS.ErrnoError(20) } } else { node = FS.mknod(path, mode, 0) created = true } } if (!node) { throw new FS.ErrnoError(44) } if (FS.isChrdev(node.mode)) { flags &= ~512 } if (flags & 65536 && !FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } if (!created) { var errCode = FS.mayOpen(node, flags) if (errCode) { throw new FS.ErrnoError(errCode) } } if (flags & 512 && !created) { FS.truncate(node, 0) } flags &= ~(128 | 512 | 131072) var stream = FS.createStream({ node: node, path: FS.getPath(node), flags: flags, seekable: true, position: 0, stream_ops: node.stream_ops, ungotten: [], error: false, }) if (stream.stream_ops.open) { stream.stream_ops.open(stream) } if (Module['logReadFiles'] && !(flags & 1)) { if (!FS.readFiles) FS.readFiles = {} if (!(path in FS.readFiles)) { FS.readFiles[path] = 1 } } return stream }, close: stream => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (stream.getdents) stream.getdents = null try { if (stream.stream_ops.close) { stream.stream_ops.close(stream) } } catch (e) { throw e } finally { FS.closeStream(stream.fd) } stream.fd = null }, isClosed: stream => { return stream.fd === null }, llseek: (stream, offset, whence) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (!stream.seekable || !stream.stream_ops.llseek) { throw new FS.ErrnoError(70) } if (whence != 0 && whence != 1 && whence != 2) { throw new FS.ErrnoError(28) } stream.position = stream.stream_ops.llseek(stream, offset, whence) stream.ungotten = [] return stream.position }, read: (stream, buffer, offset, length, position) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.read) { throw new FS.ErrnoError(28) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesRead = stream.stream_ops.read( stream, buffer, offset, length, position ) if (!seeking) stream.position += bytesRead return bytesRead }, write: (stream, buffer, offset, length, position, canOwn) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.write) { throw new FS.ErrnoError(28) } if (stream.seekable && stream.flags & 1024) { FS.llseek(stream, 0, 2) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesWritten = stream.stream_ops.write( stream, buffer, offset, length, position, canOwn ) if (!seeking) stream.position += bytesWritten return bytesWritten }, allocate: (stream, offset, length) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (offset < 0 || length <= 0) { throw new FS.ErrnoError(28) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(43) } if (!stream.stream_ops.allocate) { throw new FS.ErrnoError(138) } stream.stream_ops.allocate(stream, offset, length) }, mmap: (stream, length, position, prot, flags) => { if ( (prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2 ) { throw new FS.ErrnoError(2) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(2) } if (!stream.stream_ops.mmap) { throw new FS.ErrnoError(43) } return stream.stream_ops.mmap(stream, length, position, prot, flags) }, msync: (stream, buffer, offset, length, mmapFlags) => { if (!stream.stream_ops.msync) { return 0 } return stream.stream_ops.msync( stream, buffer, offset, length, mmapFlags ) }, munmap: stream => 0, ioctl: (stream, cmd, arg) => { if (!stream.stream_ops.ioctl) { throw new FS.ErrnoError(59) } return stream.stream_ops.ioctl(stream, cmd, arg) }, readFile: (path, opts = {}) => { opts.flags = opts.flags || 0 opts.encoding = opts.encoding || 'binary' if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { throw new Error('Invalid encoding type "' + opts.encoding + '"') } var ret var stream = FS.open(path, opts.flags) var stat = FS.stat(path) var length = stat.size var buf = new Uint8Array(length) FS.read(stream, buf, 0, length, 0) if (opts.encoding === 'utf8') { ret = UTF8ArrayToString(buf, 0) } else if (opts.encoding === 'binary') { ret = buf } FS.close(stream) return ret }, writeFile: (path, data, opts = {}) => { opts.flags = opts.flags || 577 var stream = FS.open(path, opts.flags, opts.mode) if (typeof data == 'string') { var buf = new Uint8Array(lengthBytesUTF8(data) + 1) var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length) FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn) } else if (ArrayBuffer.isView(data)) { FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn) } else { throw new Error('Unsupported data type') } FS.close(stream) }, cwd: () => FS.currentPath, chdir: path => { var lookup = FS.lookupPath(path, { follow: true }) if (lookup.node === null) { throw new FS.ErrnoError(44) } if (!FS.isDir(lookup.node.mode)) { throw new FS.ErrnoError(54) } var errCode = FS.nodePermissions(lookup.node, 'x') if (errCode) { throw new FS.ErrnoError(errCode) } FS.currentPath = lookup.path }, createDefaultDirectories: () => { FS.mkdir('/tmp') FS.mkdir('/home') FS.mkdir('/home/web_user') }, createDefaultDevices: () => { FS.mkdir('/dev') FS.registerDevice(FS.makedev(1, 3), { read: () => 0, write: (stream, buffer, offset, length, pos) => length, }) FS.mkdev('/dev/null', FS.makedev(1, 3)) TTY.register(FS.makedev(5, 0), TTY.default_tty_ops) TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops) FS.mkdev('/dev/tty', FS.makedev(5, 0)) FS.mkdev('/dev/tty1', FS.makedev(6, 0)) var random_device = getRandomDevice() FS.createDevice('/dev', 'random', random_device) FS.createDevice('/dev', 'urandom', random_device) FS.mkdir('/dev/shm') FS.mkdir('/dev/shm/tmp') }, createSpecialDirectories: () => { FS.mkdir('/proc') var proc_self = FS.mkdir('/proc/self') FS.mkdir('/proc/self/fd') FS.mount( { mount: () => { var node = FS.createNode(proc_self, 'fd', 16384 | 511, 73) node.node_ops = { lookup: (parent, name) => { var fd = +name var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) var ret = { parent: null, mount: { mountpoint: 'fake' }, node_ops: { readlink: () => stream.path }, } ret.parent = ret return ret }, } return node }, }, {}, '/proc/self/fd' ) }, createStandardStreams: () => { if (Module['stdin']) { FS.createDevice('/dev', 'stdin', Module['stdin']) } else { FS.symlink('/dev/tty', '/dev/stdin') } if (Module['stdout']) { FS.createDevice('/dev', 'stdout', null, Module['stdout']) } else { FS.symlink('/dev/tty', '/dev/stdout') } if (Module['stderr']) { FS.createDevice('/dev', 'stderr', null, Module['stderr']) } else { FS.symlink('/dev/tty1', '/dev/stderr') } var stdin = FS.open('/dev/stdin', 0) var stdout = FS.open('/dev/stdout', 1) var stderr = FS.open('/dev/stderr', 1) }, ensureErrnoError: () => { if (FS.ErrnoError) return FS.ErrnoError = function ErrnoError(errno, node) { this.node = node this.setErrno = function(errno) { this.errno = errno } this.setErrno(errno) this.message = 'FS error' } FS.ErrnoError.prototype = new Error() FS.ErrnoError.prototype.constructor = FS.ErrnoError ;[44].forEach(code => { FS.genericErrors[code] = new FS.ErrnoError(code) FS.genericErrors[code].stack = '' }) }, staticInit: () => { FS.ensureErrnoError() FS.nameTable = new Array(4096) FS.mount(MEMFS, {}, '/') FS.createDefaultDirectories() FS.createDefaultDevices() FS.createSpecialDirectories() FS.filesystems = { MEMFS: MEMFS, NODEFS: NODEFS } }, init: (input, output, error) => { FS.init.initialized = true FS.ensureErrnoError() Module['stdin'] = input || Module['stdin'] Module['stdout'] = output || Module['stdout'] Module['stderr'] = error || Module['stderr'] FS.createStandardStreams() }, quit: () => { FS.init.initialized = false for (var i = 0; i < FS.streams.length; i++) { var stream = FS.streams[i] if (!stream) { continue } FS.close(stream) } }, getMode: (canRead, canWrite) => { var mode = 0 if (canRead) mode |= 292 | 73 if (canWrite) mode |= 146 return mode }, findObject: (path, dontResolveLastLink) => { var ret = FS.analyzePath(path, dontResolveLastLink) if (!ret.exists) { return null } return ret.object }, analyzePath: (path, dontResolveLastLink) => { try { var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) path = lookup.path } catch (e) {} var ret = { isRoot: false, exists: false, error: 0, name: null, path: null, object: null, parentExists: false, parentPath: null, parentObject: null, } try { var lookup = FS.lookupPath(path, { parent: true }) ret.parentExists = true ret.parentPath = lookup.path ret.parentObject = lookup.node ret.name = PATH.basename(path) lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) ret.exists = true ret.path = lookup.path ret.object = lookup.node ret.name = lookup.node.name ret.isRoot = lookup.path === '/' } catch (e) { ret.error = e.errno } return ret }, createPath: (parent, path, canRead, canWrite) => { parent = typeof parent == 'string' ? parent : FS.getPath(parent) var parts = path.split('/').reverse() while (parts.length) { var part = parts.pop() if (!part) continue var current = PATH.join2(parent, part) try { FS.mkdir(current) } catch (e) {} parent = current } return current }, createFile: (parent, name, properties, canRead, canWrite) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(canRead, canWrite) return FS.create(path, mode) }, createDataFile: (parent, name, data, canRead, canWrite, canOwn) => { var path = name if (parent) { parent = typeof parent == 'string' ? parent : FS.getPath(parent) path = name ? PATH.join2(parent, name) : parent } var mode = FS.getMode(canRead, canWrite) var node = FS.create(path, mode) if (data) { if (typeof data == 'string') { var arr = new Array(data.length) for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i) data = arr } FS.chmod(node, mode | 146) var stream = FS.open(node, 577) FS.write(stream, data, 0, data.length, 0, canOwn) FS.close(stream) FS.chmod(node, mode) } return node }, createDevice: (parent, name, input, output) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(!!input, !!output) if (!FS.createDevice.major) FS.createDevice.major = 64 var dev = FS.makedev(FS.createDevice.major++, 0) FS.registerDevice(dev, { open: stream => { stream.seekable = false }, close: stream => { if (output && output.buffer && output.buffer.length) { output(10) } }, read: (stream, buffer, offset, length, pos) => { var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = input() } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: (stream, buffer, offset, length, pos) => { for (var i = 0; i < length; i++) { try { output(buffer[offset + i]) } catch (e) { throw new FS.ErrnoError(29) } } if (length) { stream.node.timestamp = Date.now() } return i }, }) return FS.mkdev(path, mode, dev) }, forceLoadFile: obj => { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true if (typeof XMLHttpRequest != 'undefined') { throw new Error( 'Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.' ) } else if (read_) { try { obj.contents = intArrayFromString(read_(obj.url), true) obj.usedBytes = obj.contents.length } catch (e) { throw new FS.ErrnoError(29) } } else { throw new Error('Cannot load without read() or XMLHttpRequest.') } }, createLazyFile: (parent, name, url, canRead, canWrite) => { function LazyUint8Array() { this.lengthKnown = false this.chunks = [] } LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) { if (idx > this.length - 1 || idx < 0) { return undefined } var chunkOffset = idx % this.chunkSize var chunkNum = (idx / this.chunkSize) | 0 return this.getter(chunkNum)[chunkOffset] } LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter( getter ) { this.getter = getter } LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { var xhr = new XMLHttpRequest() xhr.open('HEAD', url, false) xhr.send(null) if (!((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) throw new Error("Couldn't load " + url + '. Status: ' + xhr.status) var datalength = Number(xhr.getResponseHeader('Content-length')) var header var hasByteServing = (header = xhr.getResponseHeader('Accept-Ranges')) && header === 'bytes' var usesGzip = (header = xhr.getResponseHeader('Content-Encoding')) && header === 'gzip' var chunkSize = 1024 * 1024 if (!hasByteServing) chunkSize = datalength var doXHR = (from, to) => { if (from > to) throw new Error( 'invalid range (' + from + ', ' + to + ') or no bytes requested!' ) if (to > datalength - 1) throw new Error( 'only ' + datalength + ' bytes available! programmer error!' ) var xhr = new XMLHttpRequest() xhr.open('GET', url, false) if (datalength !== chunkSize) xhr.setRequestHeader('Range', 'bytes=' + from + '-' + to) xhr.responseType = 'arraybuffer' if (xhr.overrideMimeType) { xhr.overrideMimeType('text/plain; charset=x-user-defined') } xhr.send(null) if ( !((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ) throw new Error( "Couldn't load " + url + '. Status: ' + xhr.status ) if (xhr.response !== undefined) { return new Uint8Array(xhr.response || []) } return intArrayFromString(xhr.responseText || '', true) } var lazyArray = this lazyArray.setDataGetter(chunkNum => { var start = chunkNum * chunkSize var end = (chunkNum + 1) * chunkSize - 1 end = Math.min(end, datalength - 1) if (typeof lazyArray.chunks[chunkNum] == 'undefined') { lazyArray.chunks[chunkNum] = doXHR(start, end) } if (typeof lazyArray.chunks[chunkNum] == 'undefined') throw new Error('doXHR failed!') return lazyArray.chunks[chunkNum] }) if (usesGzip || !datalength) { chunkSize = datalength = 1 datalength = this.getter(0).length chunkSize = datalength out( 'LazyFiles on gzip forces download of the whole file when length is accessed' ) } this._length = datalength this._chunkSize = chunkSize this.lengthKnown = true } if (typeof XMLHttpRequest != 'undefined') { if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc' var lazyArray = new LazyUint8Array() Object.defineProperties(lazyArray, { length: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._length }, }, chunkSize: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._chunkSize }, }, }) var properties = { isDevice: false, contents: lazyArray } } else { var properties = { isDevice: false, url: url } } var node = FS.createFile(parent, name, properties, canRead, canWrite) if (properties.contents) { node.contents = properties.contents } else if (properties.url) { node.contents = null node.url = properties.url } Object.defineProperties(node, { usedBytes: { get: function() { return this.contents.length }, }, }) var stream_ops = {} var keys = Object.keys(node.stream_ops) keys.forEach(key => { var fn = node.stream_ops[key] stream_ops[key] = function forceLoadLazyFile() { FS.forceLoadFile(node) return fn.apply(null, arguments) } }) function writeChunks(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= contents.length) return 0 var size = Math.min(contents.length - position, length) if (contents.slice) { for (var i = 0; i < size; i++) { buffer[offset + i] = contents[position + i] } } else { for (var i = 0; i < size; i++) { buffer[offset + i] = contents.get(position + i) } } return size } stream_ops.read = (stream, buffer, offset, length, position) => { FS.forceLoadFile(node) return writeChunks(stream, buffer, offset, length, position) } stream_ops.mmap = (stream, length, position, prot, flags) => { FS.forceLoadFile(node) var ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } writeChunks(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } } node.stream_ops = stream_ops return node }, createPreloadedFile: ( parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish ) => { var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent var dep = getUniqueRunDependency('cp ' + fullname) function processData(byteArray) { function finish(byteArray) { if (preFinish) preFinish() if (!dontCreateFile) { FS.createDataFile( parent, name, byteArray, canRead, canWrite, canOwn ) } if (onload) onload() removeRunDependency(dep) } if ( Browser.handledByPreloadPlugin(byteArray, fullname, finish, () => { if (onerror) onerror() removeRunDependency(dep) }) ) { return } finish(byteArray) } addRunDependency(dep) if (typeof url == 'string') { asyncLoad(url, byteArray => processData(byteArray), onerror) } else { processData(url) } }, indexedDB: () => { return ( window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB ) }, DB_NAME: () => { return 'EM_FS_' + window.location.pathname }, DB_VERSION: 20, DB_STORE_NAME: 'FILE_DATA', saveFilesToDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = () => { out('creating db') var db = openRequest.result db.createObjectStore(FS.DB_STORE_NAME) } openRequest.onsuccess = () => { var db = openRequest.result var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite') var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var putRequest = files.put( FS.analyzePath(path).object.contents, path ) putRequest.onsuccess = () => { ok++ if (ok + fail == total) finish() } putRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, loadFilesFromDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = onerror openRequest.onsuccess = () => { var db = openRequest.result try { var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly') } catch (e) { onerror(e) return } var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var getRequest = files.get(path) getRequest.onsuccess = () => { if (FS.analyzePath(path).exists) { FS.unlink(path) } FS.createDataFile( PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true ) ok++ if (ok + fail == total) finish() } getRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, } var SYSCALLS = { DEFAULT_POLLMASK: 5, calculateAt: function(dirfd, path, allowEmpty) { if (PATH.isAbs(path)) { return path } var dir if (dirfd === -100) { dir = FS.cwd() } else { var dirstream = SYSCALLS.getStreamFromFD(dirfd) dir = dirstream.path } if (path.length == 0) { if (!allowEmpty) { throw new FS.ErrnoError(44) } return dir } return PATH.join2(dir, path) }, doStat: function(func, path, buf) { try { var stat = func(path) } catch (e) { if ( e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node)) ) { return -54 } throw e } HEAP32[buf >> 2] = stat.dev HEAP32[(buf + 8) >> 2] = stat.ino HEAP32[(buf + 12) >> 2] = stat.mode HEAPU32[(buf + 16) >> 2] = stat.nlink HEAP32[(buf + 20) >> 2] = stat.uid HEAP32[(buf + 24) >> 2] = stat.gid HEAP32[(buf + 28) >> 2] = stat.rdev ;(tempI64 = [ stat.size >>> 0, ((tempDouble = stat.size), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 40) >> 2] = tempI64[0]), (HEAP32[(buf + 44) >> 2] = tempI64[1]) HEAP32[(buf + 48) >> 2] = 4096 HEAP32[(buf + 52) >> 2] = stat.blocks var atime = stat.atime.getTime() var mtime = stat.mtime.getTime() var ctime = stat.ctime.getTime() ;(tempI64 = [ Math.floor(atime / 1e3) >>> 0, ((tempDouble = Math.floor(atime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 56) >> 2] = tempI64[0]), (HEAP32[(buf + 60) >> 2] = tempI64[1]) HEAPU32[(buf + 64) >> 2] = (atime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(mtime / 1e3) >>> 0, ((tempDouble = Math.floor(mtime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 72) >> 2] = tempI64[0]), (HEAP32[(buf + 76) >> 2] = tempI64[1]) HEAPU32[(buf + 80) >> 2] = (mtime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(ctime / 1e3) >>> 0, ((tempDouble = Math.floor(ctime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 88) >> 2] = tempI64[0]), (HEAP32[(buf + 92) >> 2] = tempI64[1]) HEAPU32[(buf + 96) >> 2] = (ctime % 1e3) * 1e3 ;(tempI64 = [ stat.ino >>> 0, ((tempDouble = stat.ino), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 104) >> 2] = tempI64[0]), (HEAP32[(buf + 108) >> 2] = tempI64[1]) return 0 }, doMsync: function(addr, stream, len, flags, offset) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } if (flags & 2) { return 0 } var buffer = HEAPU8.slice(addr, addr + len) FS.msync(stream, buffer, offset, len, flags) }, varargs: undefined, get: function() { SYSCALLS.varargs += 4 var ret = HEAP32[(SYSCALLS.varargs - 4) >> 2] return ret }, getStr: function(ptr) { var ret = UTF8ToString(ptr) return ret }, getStreamFromFD: function(fd) { var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) return stream }, } function ___syscall_fcntl64(fd, cmd, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (cmd) { case 0: { var arg = SYSCALLS.get() if (arg < 0) { return -28 } var newStream newStream = FS.createStream(stream, arg) return newStream.fd } case 1: case 2: return 0 case 3: return stream.flags case 4: { var arg = SYSCALLS.get() stream.flags |= arg return 0 } case 5: { var arg = SYSCALLS.get() var offset = 0 HEAP16[(arg + offset) >> 1] = 2 return 0 } case 6: case 7: return 0 case 16: case 8: return -28 case 9: setErrNo(28) return -1 default: { return -28 } } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_getcwd(buf, size) { try { if (size === 0) return -28 var cwd = FS.cwd() var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1 if (size < cwdLengthInBytes) return -68 stringToUTF8(cwd, buf, size) return cwdLengthInBytes } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_ioctl(fd, op, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (op) { case 21509: case 21505: { if (!stream.tty) return -59 return 0 } case 21510: case 21511: case 21512: case 21506: case 21507: case 21508: { if (!stream.tty) return -59 return 0 } case 21519: { if (!stream.tty) return -59 var argp = SYSCALLS.get() HEAP32[argp >> 2] = 0 return 0 } case 21520: { if (!stream.tty) return -59 return -28 } case 21531: { var argp = SYSCALLS.get() return FS.ioctl(stream, op, argp) } case 21523: { if (!stream.tty) return -59 return 0 } case 21524: { if (!stream.tty) return -59 return 0 } default: return -28 } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_openat(dirfd, path, flags, varargs) { SYSCALLS.varargs = varargs try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) var mode = varargs ? SYSCALLS.get() : 0 return FS.open(path, flags, mode).fd } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_readlinkat(dirfd, path, buf, bufsize) { try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) if (bufsize <= 0) return -28 var ret = FS.readlink(path) var len = Math.min(bufsize, lengthBytesUTF8(ret)) var endChar = HEAP8[buf + len] stringToUTF8(ret, buf, bufsize + 1) HEAP8[buf + len] = endChar return len } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_stat64(path, buf) { try { path = SYSCALLS.getStr(path) return SYSCALLS.doStat(FS.stat, path, buf) } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function _abort() { abort('') } function _emscripten_memcpy_big(dest, src, num) { HEAPU8.copyWithin(dest, src, src + num) } function getHeapMax() { return 2147483648 } function emscripten_realloc_buffer(size) { try { wasmMemory.grow((size - buffer.byteLength + 65535) >>> 16) updateGlobalBufferAndViews(wasmMemory.buffer) return 1 } catch (e) {} } function _emscripten_resize_heap(requestedSize) { var oldSize = HEAPU8.length requestedSize = requestedSize >>> 0 var maxHeapSize = getHeapMax() if (requestedSize > maxHeapSize) { return false } let alignUp = (x, multiple) => x + ((multiple - (x % multiple)) % multiple) for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown) overGrownHeapSize = Math.min( overGrownHeapSize, requestedSize + 100663296 ) var newSize = Math.min( maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536) ) var replacement = emscripten_realloc_buffer(newSize) if (replacement) { return true } } return false } var ENV = {} function getExecutableName() { return thisProgram || './this.program' } function getEnvStrings() { if (!getEnvStrings.strings) { var lang = ( (typeof navigator == 'object' && navigator.languages && navigator.languages[0]) || 'C' ).replace('-', '_') + '.UTF-8' var env = { USER: 'web_user', LOGNAME: 'web_user', PATH: '/', PWD: '/', HOME: '/home/web_user', LANG: lang, _: getExecutableName(), } for (var x in ENV) { if (ENV[x] === undefined) delete env[x] else env[x] = ENV[x] } var strings = [] for (var x in env) { strings.push(x + '=' + env[x]) } getEnvStrings.strings = strings } return getEnvStrings.strings } function writeAsciiToMemory(str, buffer, dontAddNull) { for (var i = 0; i < str.length; ++i) { HEAP8[buffer++ >> 0] = str.charCodeAt(i) } if (!dontAddNull) HEAP8[buffer >> 0] = 0 } function _environ_get(__environ, environ_buf) { var bufSize = 0 getEnvStrings().forEach(function(string, i) { var ptr = environ_buf + bufSize HEAPU32[(__environ + i * 4) >> 2] = ptr writeAsciiToMemory(string, ptr) bufSize += string.length + 1 }) return 0 } function _environ_sizes_get(penviron_count, penviron_buf_size) { var strings = getEnvStrings() HEAPU32[penviron_count >> 2] = strings.length var bufSize = 0 strings.forEach(function(string) { bufSize += string.length + 1 }) HEAPU32[penviron_buf_size >> 2] = bufSize return 0 } function _proc_exit(code) { EXITSTATUS = code if (!keepRuntimeAlive()) { if (Module['onExit']) Module['onExit'](code) ABORT = true } quit_(code, new ExitStatus(code)) } function exitJS(status, implicit) { EXITSTATUS = status _proc_exit(status) } var _exit = exitJS function _fd_close(fd) { try { var stream = SYSCALLS.getStreamFromFD(fd) FS.close(stream) return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doReadv(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.read(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr if (curr < len) break } return ret } function _fd_read(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doReadv(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function convertI32PairToI53Checked(lo, hi) { return (hi + 2097152) >>> 0 < 4194305 - !!lo ? (lo >>> 0) + hi * 4294967296 : NaN } function _fd_seek(fd, offset_low, offset_high, whence, newOffset) { try { var offset = convertI32PairToI53Checked(offset_low, offset_high) if (isNaN(offset)) return 61 var stream = SYSCALLS.getStreamFromFD(fd) FS.llseek(stream, offset, whence) ;(tempI64 = [ stream.position >>> 0, ((tempDouble = stream.position), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[newOffset >> 2] = tempI64[0]), (HEAP32[(newOffset + 4) >> 2] = tempI64[1]) if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doWritev(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.write(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr } return ret } function _fd_write(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doWritev(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function __isLeapYear(year) { return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) } function __arraySum(array, index) { var sum = 0 for (var i = 0; i <= index; sum += array[i++]) {} return sum } var __MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] var __MONTH_DAYS_REGULAR = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] function __addDays(date, days) { var newDate = new Date(date.getTime()) while (days > 0) { var leap = __isLeapYear(newDate.getFullYear()) var currentMonth = newDate.getMonth() var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth] if (days > daysInCurrentMonth - newDate.getDate()) { days -= daysInCurrentMonth - newDate.getDate() + 1 newDate.setDate(1) if (currentMonth < 11) { newDate.setMonth(currentMonth + 1) } else { newDate.setMonth(0) newDate.setFullYear(newDate.getFullYear() + 1) } } else { newDate.setDate(newDate.getDate() + days) return newDate } } return newDate } function writeArrayToMemory(array, buffer) { HEAP8.set(array, buffer) } function _strftime(s, maxsize, format, tm) { var tm_zone = HEAP32[(tm + 40) >> 2] var date = { tm_sec: HEAP32[tm >> 2], tm_min: HEAP32[(tm + 4) >> 2], tm_hour: HEAP32[(tm + 8) >> 2], tm_mday: HEAP32[(tm + 12) >> 2], tm_mon: HEAP32[(tm + 16) >> 2], tm_year: HEAP32[(tm + 20) >> 2], tm_wday: HEAP32[(tm + 24) >> 2], tm_yday: HEAP32[(tm + 28) >> 2], tm_isdst: HEAP32[(tm + 32) >> 2], tm_gmtoff: HEAP32[(tm + 36) >> 2], tm_zone: tm_zone ? UTF8ToString(tm_zone) : '', } var pattern = UTF8ToString(format) var EXPANSION_RULES_1 = { '%c': '%a %b %d %H:%M:%S %Y', '%D': '%m/%d/%y', '%F': '%Y-%m-%d', '%h': '%b', '%r': '%I:%M:%S %p', '%R': '%H:%M', '%T': '%H:%M:%S', '%x': '%m/%d/%y', '%X': '%H:%M:%S', '%Ec': '%c', '%EC': '%C', '%Ex': '%m/%d/%y', '%EX': '%H:%M:%S', '%Ey': '%y', '%EY': '%Y', '%Od': '%d', '%Oe': '%e', '%OH': '%H', '%OI': '%I', '%Om': '%m', '%OM': '%M', '%OS': '%S', '%Ou': '%u', '%OU': '%U', '%OV': '%V', '%Ow': '%w', '%OW': '%W', '%Oy': '%y', } for (var rule in EXPANSION_RULES_1) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_1[rule] ) } var WEEKDAYS = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', ] var MONTHS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ] function leadingSomething(value, digits, character) { var str = typeof value == 'number' ? value.toString() : value || '' while (str.length < digits) { str = character[0] + str } return str } function leadingNulls(value, digits) { return leadingSomething(value, digits, '0') } function compareByDay(date1, date2) { function sgn(value) { return value < 0 ? -1 : value > 0 ? 1 : 0 } var compare if ((compare = sgn(date1.getFullYear() - date2.getFullYear())) === 0) { if ((compare = sgn(date1.getMonth() - date2.getMonth())) === 0) { compare = sgn(date1.getDate() - date2.getDate()) } } return compare } function getFirstWeekStartDate(janFourth) { switch (janFourth.getDay()) { case 0: return new Date(janFourth.getFullYear() - 1, 11, 29) case 1: return janFourth case 2: return new Date(janFourth.getFullYear(), 0, 3) case 3: return new Date(janFourth.getFullYear(), 0, 2) case 4: return new Date(janFourth.getFullYear(), 0, 1) case 5: return new Date(janFourth.getFullYear() - 1, 11, 31) case 6: return new Date(janFourth.getFullYear() - 1, 11, 30) } } function getWeekBasedYear(date) { var thisDate = __addDays( new Date(date.tm_year + 1900, 0, 1), date.tm_yday ) var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4) var janFourthNextYear = new Date(thisDate.getFullYear() + 1, 0, 4) var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear) var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear) if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { return thisDate.getFullYear() + 1 } return thisDate.getFullYear() } return thisDate.getFullYear() - 1 } var EXPANSION_RULES_2 = { '%a': function(date) { return WEEKDAYS[date.tm_wday].substring(0, 3) }, '%A': function(date) { return WEEKDAYS[date.tm_wday] }, '%b': function(date) { return MONTHS[date.tm_mon].substring(0, 3) }, '%B': function(date) { return MONTHS[date.tm_mon] }, '%C': function(date) { var year = date.tm_year + 1900 return leadingNulls((year / 100) | 0, 2) }, '%d': function(date) { return leadingNulls(date.tm_mday, 2) }, '%e': function(date) { return leadingSomething(date.tm_mday, 2, ' ') }, '%g': function(date) { return getWeekBasedYear(date) .toString() .substring(2) }, '%G': function(date) { return getWeekBasedYear(date) }, '%H': function(date) { return leadingNulls(date.tm_hour, 2) }, '%I': function(date) { var twelveHour = date.tm_hour if (twelveHour == 0) twelveHour = 12 else if (twelveHour > 12) twelveHour -= 12 return leadingNulls(twelveHour, 2) }, '%j': function(date) { return leadingNulls( date.tm_mday + __arraySum( __isLeapYear(date.tm_year + 1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon - 1 ), 3 ) }, '%m': function(date) { return leadingNulls(date.tm_mon + 1, 2) }, '%M': function(date) { return leadingNulls(date.tm_min, 2) }, '%n': function() { return '\n' }, '%p': function(date) { if (date.tm_hour >= 0 && date.tm_hour < 12) { return 'AM' } return 'PM' }, '%S': function(date) { return leadingNulls(date.tm_sec, 2) }, '%t': function() { return '\t' }, '%u': function(date) { return date.tm_wday || 7 }, '%U': function(date) { var days = date.tm_yday + 7 - date.tm_wday return leadingNulls(Math.floor(days / 7), 2) }, '%V': function(date) { var val = Math.floor( (date.tm_yday + 7 - ((date.tm_wday + 6) % 7)) / 7 ) if ((date.tm_wday + 371 - date.tm_yday - 2) % 7 <= 2) { val++ } if (!val) { val = 52 var dec31 = (date.tm_wday + 7 - date.tm_yday - 1) % 7 if ( dec31 == 4 || (dec31 == 5 && __isLeapYear((date.tm_year % 400) - 1)) ) { val++ } } else if (val == 53) { var jan1 = (date.tm_wday + 371 - date.tm_yday) % 7 if (jan1 != 4 && (jan1 != 3 || !__isLeapYear(date.tm_year))) val = 1 } return leadingNulls(val, 2) }, '%w': function(date) { return date.tm_wday }, '%W': function(date) { var days = date.tm_yday + 7 - ((date.tm_wday + 6) % 7) return leadingNulls(Math.floor(days / 7), 2) }, '%y': function(date) { return (date.tm_year + 1900).toString().substring(2) }, '%Y': function(date) { return date.tm_year + 1900 }, '%z': function(date) { var off = date.tm_gmtoff var ahead = off >= 0 off = Math.abs(off) / 60 off = (off / 60) * 100 + (off % 60) return (ahead ? '+' : '-') + String('0000' + off).slice(-4) }, '%Z': function(date) { return date.tm_zone }, '%%': function() { return '%' }, } pattern = pattern.replace(/%%/g, '\0\0') for (var rule in EXPANSION_RULES_2) { if (pattern.includes(rule)) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date) ) } } pattern = pattern.replace(/\0\0/g, '%') var bytes = intArrayFromString(pattern, false) if (bytes.length > maxsize) { return 0 } writeArrayToMemory(bytes, s) return bytes.length - 1 } function _strftime_l(s, maxsize, format, tm, loc) { return _strftime(s, maxsize, format, tm) } function handleException(e) { if (e instanceof ExitStatus || e == 'unwind') { return EXITSTATUS } quit_(1, e) } function allocateUTF8OnStack(str) { var size = lengthBytesUTF8(str) + 1 var ret = stackAlloc(size) stringToUTF8Array(str, HEAP8, ret, size) return ret } function getCFunc(ident) { var func = Module['_' + ident] return func } function ccall(ident, returnType, argTypes, args, opts) { var toC = { string: str => { var ret = 0 if (str !== null && str !== undefined && str !== 0) { var len = (str.length << 2) + 1 ret = stackAlloc(len) stringToUTF8(str, ret, len) } return ret }, array: arr => { var ret = stackAlloc(arr.length) writeArrayToMemory(arr, ret) return ret }, } function convertReturnValue(ret) { if (returnType === 'string') { return UTF8ToString(ret) } if (returnType === 'boolean') return Boolean(ret) return ret } var func = getCFunc(ident) var cArgs = [] var stack = 0 if (args) { for (var i = 0; i < args.length; i++) { var converter = toC[argTypes[i]] if (converter) { if (stack === 0) stack = stackSave() cArgs[i] = converter(args[i]) } else { cArgs[i] = args[i] } } } var ret = func.apply(null, cArgs) function onDone(ret) { if (stack !== 0) stackRestore(stack) return convertReturnValue(ret) } ret = onDone(ret) return ret } function cwrap(ident, returnType, argTypes, opts) { argTypes = argTypes || [] var numericArgs = argTypes.every( type => type === 'number' || type === 'boolean' ) var numericRet = returnType !== 'string' if (numericRet && numericArgs && !opts) { return getCFunc(ident) } return function() { return ccall(ident, returnType, argTypes, arguments, opts) } } function AsciiToString(ptr) { var str = '' while (1) { var ch = HEAPU8[ptr++ >> 0] if (!ch) return str str += String.fromCharCode(ch) } } var FSNode = function(parent, name, mode, rdev) { if (!parent) { parent = this } this.parent = parent this.mount = parent.mount this.mounted = null this.id = FS.nextInode++ this.name = name this.mode = mode this.node_ops = {} this.stream_ops = {} this.rdev = rdev } var readMode = 292 | 73 var writeMode = 146 Object.defineProperties(FSNode.prototype, { read: { get: function() { return (this.mode & readMode) === readMode }, set: function(val) { val ? (this.mode |= readMode) : (this.mode &= ~readMode) }, }, write: { get: function() { return (this.mode & writeMode) === writeMode }, set: function(val) { val ? (this.mode |= writeMode) : (this.mode &= ~writeMode) }, }, isFolder: { get: function() { return FS.isDir(this.mode) }, }, isDevice: { get: function() { return FS.isChrdev(this.mode) }, }, }) FS.FSNode = FSNode FS.staticInit() Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_unlink'] = FS.unlink Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice if (ENVIRONMENT_IS_NODE) { NODEFS.staticInit() } ERRNO_CODES = { EPERM: 63, ENOENT: 44, ESRCH: 71, EINTR: 27, EIO: 29, ENXIO: 60, E2BIG: 1, ENOEXEC: 45, EBADF: 8, ECHILD: 12, EAGAIN: 6, EWOULDBLOCK: 6, ENOMEM: 48, EACCES: 2, EFAULT: 21, ENOTBLK: 105, EBUSY: 10, EEXIST: 20, EXDEV: 75, ENODEV: 43, ENOTDIR: 54, EISDIR: 31, EINVAL: 28, ENFILE: 41, EMFILE: 33, ENOTTY: 59, ETXTBSY: 74, EFBIG: 22, ENOSPC: 51, ESPIPE: 70, EROFS: 69, EMLINK: 34, EPIPE: 64, EDOM: 18, ERANGE: 68, ENOMSG: 49, EIDRM: 24, ECHRNG: 106, EL2NSYNC: 156, EL3HLT: 107, EL3RST: 108, ELNRNG: 109, EUNATCH: 110, ENOCSI: 111, EL2HLT: 112, EDEADLK: 16, ENOLCK: 46, EBADE: 113, EBADR: 114, EXFULL: 115, ENOANO: 104, EBADRQC: 103, EBADSLT: 102, EDEADLOCK: 16, EBFONT: 101, ENOSTR: 100, ENODATA: 116, ETIME: 117, ENOSR: 118, ENONET: 119, ENOPKG: 120, EREMOTE: 121, ENOLINK: 47, EADV: 122, ESRMNT: 123, ECOMM: 124, EPROTO: 65, EMULTIHOP: 36, EDOTDOT: 125, EBADMSG: 9, ENOTUNIQ: 126, EBADFD: 127, EREMCHG: 128, ELIBACC: 129, ELIBBAD: 130, ELIBSCN: 131, ELIBMAX: 132, ELIBEXEC: 133, ENOSYS: 52, ENOTEMPTY: 55, ENAMETOOLONG: 37, ELOOP: 32, EOPNOTSUPP: 138, EPFNOSUPPORT: 139, ECONNRESET: 15, ENOBUFS: 42, EAFNOSUPPORT: 5, EPROTOTYPE: 67, ENOTSOCK: 57, ENOPROTOOPT: 50, ESHUTDOWN: 140, ECONNREFUSED: 14, EADDRINUSE: 3, ECONNABORTED: 13, ENETUNREACH: 40, ENETDOWN: 38, ETIMEDOUT: 73, EHOSTDOWN: 142, EHOSTUNREACH: 23, EINPROGRESS: 26, EALREADY: 7, EDESTADDRREQ: 17, EMSGSIZE: 35, EPROTONOSUPPORT: 66, ESOCKTNOSUPPORT: 137, EADDRNOTAVAIL: 4, ENETRESET: 39, EISCONN: 30, ENOTCONN: 53, ETOOMANYREFS: 141, EUSERS: 136, EDQUOT: 19, ESTALE: 72, ENOTSUP: 138, ENOMEDIUM: 148, EILSEQ: 25, EOVERFLOW: 61, ECANCELED: 11, ENOTRECOVERABLE: 56, EOWNERDEAD: 62, ESTRPIPE: 135, } var asmLibraryArg = { a: ___cxa_throw, d: ___syscall_fcntl64, r: ___syscall_getcwd, i: ___syscall_ioctl, j: ___syscall_openat, n: ___syscall_readlinkat, o: ___syscall_stat64, b: _abort, f: _emscripten_memcpy_big, m: _emscripten_resize_heap, p: _environ_get, q: _environ_sizes_get, c: _exit, e: _fd_close, h: _fd_read, k: _fd_seek, g: _fd_write, l: _strftime_l, } var asm = createWasm() var ___wasm_call_ctors = (Module['___wasm_call_ctors'] = function() { return (___wasm_call_ctors = Module['___wasm_call_ctors'] = Module['asm']['t']).apply(null, arguments) }) var _main = (Module['_main'] = function() { return (_main = Module['_main'] = Module['asm']['u']).apply( null, arguments ) }) var ___errno_location = (Module['___errno_location'] = function() { return (___errno_location = Module['___errno_location'] = Module['asm']['v']).apply(null, arguments) }) var _itk_wasm_input_array_alloc = (Module[ '_itk_wasm_input_array_alloc' ] = function() { return (_itk_wasm_input_array_alloc = Module[ '_itk_wasm_input_array_alloc' ] = Module['asm']['w']).apply(null, arguments) }) var _itk_wasm_input_json_alloc = (Module[ '_itk_wasm_input_json_alloc' ] = function() { return (_itk_wasm_input_json_alloc = Module[ '_itk_wasm_input_json_alloc' ] = Module['asm']['x']).apply(null, arguments) }) var _itk_wasm_output_json_address = (Module[ '_itk_wasm_output_json_address' ] = function() { return (_itk_wasm_output_json_address = Module[ '_itk_wasm_output_json_address' ] = Module['asm']['y']).apply(null, arguments) }) var _itk_wasm_output_json_size = (Module[ '_itk_wasm_output_json_size' ] = function() { return (_itk_wasm_output_json_size = Module[ '_itk_wasm_output_json_size' ] = Module['asm']['z']).apply(null, arguments) }) var _itk_wasm_output_array_address = (Module[ '_itk_wasm_output_array_address' ] = function() { return (_itk_wasm_output_array_address = Module[ '_itk_wasm_output_array_address' ] = Module['asm']['A']).apply(null, arguments) }) var _itk_wasm_output_array_size = (Module[ '_itk_wasm_output_array_size' ] = function() { return (_itk_wasm_output_array_size = Module[ '_itk_wasm_output_array_size' ] = Module['asm']['B']).apply(null, arguments) }) var _itk_wasm_free_all = (Module['_itk_wasm_free_all'] = function() { return (_itk_wasm_free_all = Module['_itk_wasm_free_all'] = Module['asm']['C']).apply(null, arguments) }) var stackSave = (Module['stackSave'] = function() { return (stackSave = Module['stackSave'] = Module['asm']['E']).apply( null, arguments ) }) var stackRestore = (Module['stackRestore'] = function() { return (stackRestore = Module['stackRestore'] = Module['asm']['F']).apply( null, arguments ) }) var stackAlloc = (Module['stackAlloc'] = function() { return (stackAlloc = Module['stackAlloc'] = Module['asm']['G']).apply( null, arguments ) }) var ___cxa_is_pointer_type = (Module[ '___cxa_is_pointer_type' ] = function() { return (___cxa_is_pointer_type = Module['___cxa_is_pointer_type'] = Module['asm']['H']).apply(null, arguments) }) Module['addRunDependency'] = addRunDependency Module['removeRunDependency'] = removeRunDependency Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice Module['FS_unlink'] = FS.unlink Module['callMain'] = callMain Module['ccall'] = ccall Module['cwrap'] = cwrap Module['AsciiToString'] = AsciiToString Module['writeArrayToMemory'] = writeArrayToMemory Module['writeAsciiToMemory'] = writeAsciiToMemory var calledRun dependenciesFulfilled = function runCaller() { if (!calledRun) run() if (!calledRun) dependenciesFulfilled = runCaller } function callMain(args) { var entryFunction = Module['_main'] args = args || [] args.unshift(thisProgram) var argc = args.length var argv = stackAlloc((argc + 1) * 4) var argv_ptr = argv >> 2 args.forEach(arg => { HEAP32[argv_ptr++] = allocateUTF8OnStack(arg) }) HEAP32[argv_ptr] = 0 try { var ret = entryFunction(argc, argv) exitJS(ret, true) return ret } catch (e) { return handleException(e) } } function run(args) { args = args || arguments_ if (runDependencies > 0) { return } preRun() if (runDependencies > 0) { return } function doRun() { if (calledRun) return calledRun = true Module['calledRun'] = true if (ABORT) return initRuntime() preMain() readyPromiseResolve(Module) if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized']() if (shouldRunNow) callMain(args) postRun() } if (Module['setStatus']) { Module['setStatus']('Running...') setTimeout(function() { setTimeout(function() { Module['setStatus']('') }, 1) doRun() }, 1) } else { doRun() } } if (Module['preInit']) { if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']] while (Module['preInit'].length > 0) { Module['preInit'].pop()() } } var shouldRunNow = false if (Module['noInitialRun']) shouldRunNow = false run() Module.mountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) if (FS.isDir(containingDir) || containingDir === '/') { return } var currentDir = '/' var splitContainingDir = containingDir.split(path.sep) for (var ii = 1; ii < splitContainingDir.length; ii++) { currentDir += splitContainingDir[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } FS.mount(NODEFS, { root: containingDir }, currentDir) return currentDir + path.basename(filePath) } Module.unmountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) FS.unmount(containingDir) } Module.fs_mkdirs = function(dirs) { var currentDir = '/' var splitDirs = dirs.split('/') for (var ii = 1; ii < splitDirs.length; ++ii) { currentDir += splitDirs[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } } Module.fs_readFile = function(path, opts) { return FS.readFile(path, opts) } Module.fs_writeFile = function(path, data, opts) { return FS.writeFile(path, data, opts) } Module.fs_unlink = function(path) { return FS.unlink(path) } Module.fs_open = function(path, flags, mode) { return FS.open(path, flags, mode) } Module.fs_stat = function(path) { return FS.stat(path) } Module.fs_read = function(stream, buffer, offset, length, position) { return FS.read(stream, buffer, offset, length, position) } Module.fs_close = function(stream) { return FS.close(stream) } return Downsample.ready } })() export default Downsample ================================================ FILE: src/IO/Downsample/emscripten-build/Downsample.umd.js ================================================ var Downsample = (() => { var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename return function(Downsample) { Downsample = Downsample || {} var Module = typeof Downsample != 'undefined' ? Downsample : {} var readyPromiseResolve, readyPromiseReject Module['ready'] = new Promise(function(resolve, reject) { readyPromiseResolve = resolve readyPromiseReject = reject }) var mStdout = null var mStderr = null Module['resetModuleStdout'] = function() { mStdout = '' } Module['resetModuleStderr'] = function() { mStderr = '' } Module['print'] = function(text) { console.log(text) mStdout += text + '\n' } Module['printErr'] = function(text) { console.error(text) mStderr += text + '\n' } Module['getModuleStdout'] = function() { return mStdout } Module['getModuleStderr'] = function() { return mStderr } var moduleOverrides = Object.assign({}, Module) var arguments_ = [] var thisProgram = './this.program' var quit_ = (status, toThrow) => { throw toThrow } var ENVIRONMENT_IS_WEB = typeof window == 'object' var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function' var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string' var scriptDirectory = '' function locateFile(path) { if (Module['locateFile']) { return Module['locateFile'](path, scriptDirectory) } return scriptDirectory + path } var read_, readAsync, readBinary, setWindowTitle function logExceptionOnExit(e) { if (e instanceof ExitStatus) return let toLog = e err('exiting due to exception: ' + toLog) } if (ENVIRONMENT_IS_NODE) { var fs = require('fs') var nodePath = require('path') if (ENVIRONMENT_IS_WORKER) { scriptDirectory = nodePath.dirname(scriptDirectory) + '/' } else { scriptDirectory = __dirname + '/' } read_ = (filename, binary) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) return fs.readFileSync(filename, binary ? undefined : 'utf8') } readBinary = filename => { var ret = read_(filename, true) if (!ret.buffer) { ret = new Uint8Array(ret) } return ret } readAsync = (filename, onload, onerror) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) fs.readFile(filename, function(err, data) { if (err) onerror(err) else onload(data.buffer) }) } if (process['argv'].length > 1) { thisProgram = process['argv'][1].replace(/\\/g, '/') } arguments_ = process['argv'].slice(2) process['on']('uncaughtException', function(ex) { if (!(ex instanceof ExitStatus)) { throw ex } }) process['on']('unhandledRejection', function(reason) { throw reason }) quit_ = (status, toThrow) => { if (keepRuntimeAlive()) { process['exitCode'] = status throw toThrow } logExceptionOnExit(toThrow) process['exit'](status) } Module['inspect'] = function() { return '[Emscripten Module object]' } } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = self.location.href } else if (typeof document != 'undefined' && document.currentScript) { scriptDirectory = document.currentScript.src } if (_scriptDir) { scriptDirectory = _scriptDir } if (scriptDirectory.indexOf('blob:') !== 0) { scriptDirectory = scriptDirectory.substr( 0, scriptDirectory.replace(/[?#].*/, '').lastIndexOf('/') + 1 ) } else { scriptDirectory = '' } { read_ = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.send(null) return xhr.responseText } if (ENVIRONMENT_IS_WORKER) { readBinary = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.responseType = 'arraybuffer' xhr.send(null) return new Uint8Array(xhr.response) } } readAsync = (url, onload, onerror) => { var xhr = new XMLHttpRequest() xhr.open('GET', url, true) xhr.responseType = 'arraybuffer' xhr.onload = () => { if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { onload(xhr.response) return } onerror() } xhr.onerror = onerror xhr.send(null) } } setWindowTitle = title => (document.title = title) } else { } var out = Module['print'] || console.log.bind(console) var err = Module['printErr'] || console.warn.bind(console) Object.assign(Module, moduleOverrides) moduleOverrides = null if (Module['arguments']) arguments_ = Module['arguments'] if (Module['thisProgram']) thisProgram = Module['thisProgram'] if (Module['quit']) quit_ = Module['quit'] var wasmBinary if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'] var noExitRuntime = Module['noExitRuntime'] || true if (typeof WebAssembly != 'object') { abort('no native wasm support detected') } var wasmMemory var ABORT = false var EXITSTATUS function assert(condition, text) { if (!condition) { abort(text) } } var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { var endIdx = idx + maxBytesToRead var endPtr = idx while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)) } var str = '' while (idx < endPtr) { var u0 = heapOrArray[idx++] if (!(u0 & 128)) { str += String.fromCharCode(u0) continue } var u1 = heapOrArray[idx++] & 63 if ((u0 & 224) == 192) { str += String.fromCharCode(((u0 & 31) << 6) | u1) continue } var u2 = heapOrArray[idx++] & 63 if ((u0 & 240) == 224) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2 } else { u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63) } if (u0 < 65536) { str += String.fromCharCode(u0) } else { var ch = u0 - 65536 str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)) } } return str } function UTF8ToString(ptr, maxBytesToRead) { return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '' } function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { if (!(maxBytesToWrite > 0)) return 0 var startIdx = outIdx var endIdx = outIdx + maxBytesToWrite - 1 for (var i = 0; i < str.length; ++i) { var u = str.charCodeAt(i) if (u >= 55296 && u <= 57343) { var u1 = str.charCodeAt(++i) u = (65536 + ((u & 1023) << 10)) | (u1 & 1023) } if (u <= 127) { if (outIdx >= endIdx) break heap[outIdx++] = u } else if (u <= 2047) { if (outIdx + 1 >= endIdx) break heap[outIdx++] = 192 | (u >> 6) heap[outIdx++] = 128 | (u & 63) } else if (u <= 65535) { if (outIdx + 2 >= endIdx) break heap[outIdx++] = 224 | (u >> 12) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } else { if (outIdx + 3 >= endIdx) break heap[outIdx++] = 240 | (u >> 18) heap[outIdx++] = 128 | ((u >> 12) & 63) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } } heap[outIdx] = 0 return outIdx - startIdx } function stringToUTF8(str, outPtr, maxBytesToWrite) { return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite) } function lengthBytesUTF8(str) { var len = 0 for (var i = 0; i < str.length; ++i) { var c = str.charCodeAt(i) if (c <= 127) { len++ } else if (c <= 2047) { len += 2 } else if (c >= 55296 && c <= 57343) { len += 4 ++i } else { len += 3 } } return len } var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64 function updateGlobalBufferAndViews(buf) { buffer = buf Module['HEAP8'] = HEAP8 = new Int8Array(buf) Module['HEAP16'] = HEAP16 = new Int16Array(buf) Module['HEAP32'] = HEAP32 = new Int32Array(buf) Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf) Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf) Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf) Module['HEAPF32'] = HEAPF32 = new Float32Array(buf) Module['HEAPF64'] = HEAPF64 = new Float64Array(buf) } var INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 16777216 var wasmTable var __ATPRERUN__ = [] var __ATINIT__ = [] var __ATMAIN__ = [] var __ATPOSTRUN__ = [] var runtimeInitialized = false function keepRuntimeAlive() { return noExitRuntime } function preRun() { if (Module['preRun']) { if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']] while (Module['preRun'].length) { addOnPreRun(Module['preRun'].shift()) } } callRuntimeCallbacks(__ATPRERUN__) } function initRuntime() { runtimeInitialized = true if (!Module['noFSInit'] && !FS.init.initialized) FS.init() FS.ignorePermissions = false TTY.init() callRuntimeCallbacks(__ATINIT__) } function preMain() { callRuntimeCallbacks(__ATMAIN__) } function postRun() { if (Module['postRun']) { if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']] while (Module['postRun'].length) { addOnPostRun(Module['postRun'].shift()) } } callRuntimeCallbacks(__ATPOSTRUN__) } function addOnPreRun(cb) { __ATPRERUN__.unshift(cb) } function addOnInit(cb) { __ATINIT__.unshift(cb) } function addOnPostRun(cb) { __ATPOSTRUN__.unshift(cb) } var runDependencies = 0 var runDependencyWatcher = null var dependenciesFulfilled = null function getUniqueRunDependency(id) { return id } function addRunDependency(id) { runDependencies++ if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } } function removeRunDependency(id) { runDependencies-- if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } if (runDependencies == 0) { if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher) runDependencyWatcher = null } if (dependenciesFulfilled) { var callback = dependenciesFulfilled dependenciesFulfilled = null callback() } } } function abort(what) { if (Module['onAbort']) { Module['onAbort'](what) } what = 'Aborted(' + what + ')' err(what) ABORT = true EXITSTATUS = 1 what += '. Build with -sASSERTIONS for more info.' var e = new WebAssembly.RuntimeError(what) readyPromiseReject(e) throw e } var dataURIPrefix = 'data:application/octet-stream;base64,' function isDataURI(filename) { return filename.startsWith(dataURIPrefix) } function isFileURI(filename) { return filename.startsWith('file://') } var wasmBinaryFile wasmBinaryFile = 'Downsample.umd.wasm' if (!isDataURI(wasmBinaryFile)) { wasmBinaryFile = locateFile(wasmBinaryFile) } function getBinary(file) { try { if (file == wasmBinaryFile && wasmBinary) { return new Uint8Array(wasmBinary) } if (readBinary) { return readBinary(file) } throw 'both async and sync fetching of the wasm failed' } catch (err) { abort(err) } } function getBinaryPromise() { if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { if (typeof fetch == 'function' && !isFileURI(wasmBinaryFile)) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }) .then(function(response) { if (!response['ok']) { throw "failed to load wasm binary file at '" + wasmBinaryFile + "'" } return response['arrayBuffer']() }) .catch(function() { return getBinary(wasmBinaryFile) }) } else { if (readAsync) { return new Promise(function(resolve, reject) { readAsync( wasmBinaryFile, function(response) { resolve(new Uint8Array(response)) }, reject ) }) } } } return Promise.resolve().then(function() { return getBinary(wasmBinaryFile) }) } function createWasm() { var info = { a: asmLibraryArg } function receiveInstance(instance, module) { var exports = instance.exports Module['asm'] = exports wasmMemory = Module['asm']['s'] updateGlobalBufferAndViews(wasmMemory.buffer) wasmTable = Module['asm']['D'] addOnInit(Module['asm']['t']) removeRunDependency('wasm-instantiate') } addRunDependency('wasm-instantiate') function receiveInstantiationResult(result) { receiveInstance(result['instance']) } function instantiateArrayBuffer(receiver) { return getBinaryPromise() .then(function(binary) { return WebAssembly.instantiate(binary, info) }) .then(function(instance) { return instance }) .then(receiver, function(reason) { err('failed to asynchronously prepare wasm: ' + reason) abort(reason) }) } function instantiateAsync() { if ( !wasmBinary && typeof WebAssembly.instantiateStreaming == 'function' && !isDataURI(wasmBinaryFile) && !isFileURI(wasmBinaryFile) && !ENVIRONMENT_IS_NODE && typeof fetch == 'function' ) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then( function(response) { var result = WebAssembly.instantiateStreaming(response, info) return result.then(receiveInstantiationResult, function(reason) { err('wasm streaming compile failed: ' + reason) err('falling back to ArrayBuffer instantiation') return instantiateArrayBuffer(receiveInstantiationResult) }) } ) } else { return instantiateArrayBuffer(receiveInstantiationResult) } } if (Module['instantiateWasm']) { try { var exports = Module['instantiateWasm'](info, receiveInstance) return exports } catch (e) { err('Module.instantiateWasm callback failed with error: ' + e) readyPromiseReject(e) } } instantiateAsync().catch(readyPromiseReject) return {} } var tempDouble var tempI64 function ExitStatus(status) { this.name = 'ExitStatus' this.message = 'Program terminated with exit(' + status + ')' this.status = status } function callRuntimeCallbacks(callbacks) { while (callbacks.length > 0) { callbacks.shift()(Module) } } function ExceptionInfo(excPtr) { this.excPtr = excPtr this.ptr = excPtr - 24 this.set_type = function(type) { HEAPU32[(this.ptr + 4) >> 2] = type } this.get_type = function() { return HEAPU32[(this.ptr + 4) >> 2] } this.set_destructor = function(destructor) { HEAPU32[(this.ptr + 8) >> 2] = destructor } this.get_destructor = function() { return HEAPU32[(this.ptr + 8) >> 2] } this.set_refcount = function(refcount) { HEAP32[this.ptr >> 2] = refcount } this.set_caught = function(caught) { caught = caught ? 1 : 0 HEAP8[(this.ptr + 12) >> 0] = caught } this.get_caught = function() { return HEAP8[(this.ptr + 12) >> 0] != 0 } this.set_rethrown = function(rethrown) { rethrown = rethrown ? 1 : 0 HEAP8[(this.ptr + 13) >> 0] = rethrown } this.get_rethrown = function() { return HEAP8[(this.ptr + 13) >> 0] != 0 } this.init = function(type, destructor) { this.set_adjusted_ptr(0) this.set_type(type) this.set_destructor(destructor) this.set_refcount(0) this.set_caught(false) this.set_rethrown(false) } this.add_ref = function() { var value = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = value + 1 } this.release_ref = function() { var prev = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = prev - 1 return prev === 1 } this.set_adjusted_ptr = function(adjustedPtr) { HEAPU32[(this.ptr + 16) >> 2] = adjustedPtr } this.get_adjusted_ptr = function() { return HEAPU32[(this.ptr + 16) >> 2] } this.get_exception_ptr = function() { var isPointer = ___cxa_is_pointer_type(this.get_type()) if (isPointer) { return HEAPU32[this.excPtr >> 2] } var adjusted = this.get_adjusted_ptr() if (adjusted !== 0) return adjusted return this.excPtr } } var exceptionLast = 0 var uncaughtExceptionCount = 0 function ___cxa_throw(ptr, type, destructor) { var info = new ExceptionInfo(ptr) info.init(type, destructor) exceptionLast = ptr uncaughtExceptionCount++ throw ptr } function setErrNo(value) { HEAP32[___errno_location() >> 2] = value return value } var PATH = { isAbs: path => path.charAt(0) === '/', splitPath: filename => { var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/ return splitPathRe.exec(filename).slice(1) }, normalizeArray: (parts, allowAboveRoot) => { var up = 0 for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i] if (last === '.') { parts.splice(i, 1) } else if (last === '..') { parts.splice(i, 1) up++ } else if (up) { parts.splice(i, 1) up-- } } if (allowAboveRoot) { for (; up; up--) { parts.unshift('..') } } return parts }, normalize: path => { var isAbsolute = PATH.isAbs(path), trailingSlash = path.substr(-1) === '/' path = PATH.normalizeArray( path.split('/').filter(p => !!p), !isAbsolute ).join('/') if (!path && !isAbsolute) { path = '.' } if (path && trailingSlash) { path += '/' } return (isAbsolute ? '/' : '') + path }, dirname: path => { var result = PATH.splitPath(path), root = result[0], dir = result[1] if (!root && !dir) { return '.' } if (dir) { dir = dir.substr(0, dir.length - 1) } return root + dir }, basename: path => { if (path === '/') return '/' path = PATH.normalize(path) path = path.replace(/\/$/, '') var lastSlash = path.lastIndexOf('/') if (lastSlash === -1) return path return path.substr(lastSlash + 1) }, join: function() { var paths = Array.prototype.slice.call(arguments) return PATH.normalize(paths.join('/')) }, join2: (l, r) => { return PATH.normalize(l + '/' + r) }, } function getRandomDevice() { if ( typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function' ) { var randomBuffer = new Uint8Array(1) return () => { crypto.getRandomValues(randomBuffer) return randomBuffer[0] } } else if (ENVIRONMENT_IS_NODE) { try { var crypto_module = require('crypto') return () => crypto_module['randomBytes'](1)[0] } catch (e) {} } return () => abort('randomDevice') } var PATH_FS = { resolve: function() { var resolvedPath = '', resolvedAbsolute = false for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = i >= 0 ? arguments[i] : FS.cwd() if (typeof path != 'string') { throw new TypeError('Arguments to path.resolve must be strings') } else if (!path) { return '' } resolvedPath = path + '/' + resolvedPath resolvedAbsolute = PATH.isAbs(path) } resolvedPath = PATH.normalizeArray( resolvedPath.split('/').filter(p => !!p), !resolvedAbsolute ).join('/') return (resolvedAbsolute ? '/' : '') + resolvedPath || '.' }, relative: (from, to) => { from = PATH_FS.resolve(from).substr(1) to = PATH_FS.resolve(to).substr(1) function trim(arr) { var start = 0 for (; start < arr.length; start++) { if (arr[start] !== '') break } var end = arr.length - 1 for (; end >= 0; end--) { if (arr[end] !== '') break } if (start > end) return [] return arr.slice(start, end - start + 1) } var fromParts = trim(from.split('/')) var toParts = trim(to.split('/')) var length = Math.min(fromParts.length, toParts.length) var samePartsLength = length for (var i = 0; i < length; i++) { if (fromParts[i] !== toParts[i]) { samePartsLength = i break } } var outputParts = [] for (var i = samePartsLength; i < fromParts.length; i++) { outputParts.push('..') } outputParts = outputParts.concat(toParts.slice(samePartsLength)) return outputParts.join('/') }, } function intArrayFromString(stringy, dontAddNull, length) { var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1 var u8array = new Array(len) var numBytesWritten = stringToUTF8Array( stringy, u8array, 0, u8array.length ) if (dontAddNull) u8array.length = numBytesWritten return u8array } var TTY = { ttys: [], init: function() {}, shutdown: function() {}, register: function(dev, ops) { TTY.ttys[dev] = { input: [], output: [], ops: ops } FS.registerDevice(dev, TTY.stream_ops) }, stream_ops: { open: function(stream) { var tty = TTY.ttys[stream.node.rdev] if (!tty) { throw new FS.ErrnoError(43) } stream.tty = tty stream.seekable = false }, close: function(stream) { stream.tty.ops.fsync(stream.tty) }, fsync: function(stream) { stream.tty.ops.fsync(stream.tty) }, read: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.get_char) { throw new FS.ErrnoError(60) } var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = stream.tty.ops.get_char(stream.tty) } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.put_char) { throw new FS.ErrnoError(60) } try { for (var i = 0; i < length; i++) { stream.tty.ops.put_char(stream.tty, buffer[offset + i]) } } catch (e) { throw new FS.ErrnoError(29) } if (length) { stream.node.timestamp = Date.now() } return i }, }, default_tty_ops: { get_char: function(tty) { if (!tty.input.length) { var result = null if (ENVIRONMENT_IS_NODE) { var BUFSIZE = 256 var buf = Buffer.alloc(BUFSIZE) var bytesRead = 0 try { bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE, -1) } catch (e) { if (e.toString().includes('EOF')) bytesRead = 0 else throw e } if (bytesRead > 0) { result = buf.slice(0, bytesRead).toString('utf-8') } else { result = null } } else if ( typeof window != 'undefined' && typeof window.prompt == 'function' ) { result = window.prompt('Input: ') if (result !== null) { result += '\n' } } else if (typeof readline == 'function') { result = readline() if (result !== null) { result += '\n' } } if (!result) { return null } tty.input = intArrayFromString(result, true) } return tty.input.shift() }, put_char: function(tty, val) { if (val === null || val === 10) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, default_tty1_ops: { put_char: function(tty, val) { if (val === null || val === 10) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, } function mmapAlloc(size) { abort() } var MEMFS = { ops_table: null, mount: function(mount) { return MEMFS.createNode(null, '/', 16384 | 511, 0) }, createNode: function(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { throw new FS.ErrnoError(63) } if (!MEMFS.ops_table) { MEMFS.ops_table = { dir: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, lookup: MEMFS.node_ops.lookup, mknod: MEMFS.node_ops.mknod, rename: MEMFS.node_ops.rename, unlink: MEMFS.node_ops.unlink, rmdir: MEMFS.node_ops.rmdir, readdir: MEMFS.node_ops.readdir, symlink: MEMFS.node_ops.symlink, }, stream: { llseek: MEMFS.stream_ops.llseek }, }, file: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: { llseek: MEMFS.stream_ops.llseek, read: MEMFS.stream_ops.read, write: MEMFS.stream_ops.write, allocate: MEMFS.stream_ops.allocate, mmap: MEMFS.stream_ops.mmap, msync: MEMFS.stream_ops.msync, }, }, link: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, readlink: MEMFS.node_ops.readlink, }, stream: {}, }, chrdev: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: FS.chrdev_stream_ops, }, } } var node = FS.createNode(parent, name, mode, dev) if (FS.isDir(node.mode)) { node.node_ops = MEMFS.ops_table.dir.node node.stream_ops = MEMFS.ops_table.dir.stream node.contents = {} } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node node.stream_ops = MEMFS.ops_table.file.stream node.usedBytes = 0 node.contents = null } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node node.stream_ops = MEMFS.ops_table.link.stream } else if (FS.isChrdev(node.mode)) { node.node_ops = MEMFS.ops_table.chrdev.node node.stream_ops = MEMFS.ops_table.chrdev.stream } node.timestamp = Date.now() if (parent) { parent.contents[name] = node parent.timestamp = node.timestamp } return node }, getFileDataAsTypedArray: function(node) { if (!node.contents) return new Uint8Array(0) if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes) return new Uint8Array(node.contents) }, expandFileStorage: function(node, newCapacity) { var prevCapacity = node.contents ? node.contents.length : 0 if (prevCapacity >= newCapacity) return var CAPACITY_DOUBLING_MAX = 1024 * 1024 newCapacity = Math.max( newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2 : 1.125)) >>> 0 ) if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256) var oldContents = node.contents node.contents = new Uint8Array(newCapacity) if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0) }, resizeFileStorage: function(node, newSize) { if (node.usedBytes == newSize) return if (newSize == 0) { node.contents = null node.usedBytes = 0 } else { var oldContents = node.contents node.contents = new Uint8Array(newSize) if (oldContents) { node.contents.set( oldContents.subarray(0, Math.min(newSize, node.usedBytes)) ) } node.usedBytes = newSize } }, node_ops: { getattr: function(node) { var attr = {} attr.dev = FS.isChrdev(node.mode) ? node.id : 1 attr.ino = node.id attr.mode = node.mode attr.nlink = 1 attr.uid = 0 attr.gid = 0 attr.rdev = node.rdev if (FS.isDir(node.mode)) { attr.size = 4096 } else if (FS.isFile(node.mode)) { attr.size = node.usedBytes } else if (FS.isLink(node.mode)) { attr.size = node.link.length } else { attr.size = 0 } attr.atime = new Date(node.timestamp) attr.mtime = new Date(node.timestamp) attr.ctime = new Date(node.timestamp) attr.blksize = 4096 attr.blocks = Math.ceil(attr.size / attr.blksize) return attr }, setattr: function(node, attr) { if (attr.mode !== undefined) { node.mode = attr.mode } if (attr.timestamp !== undefined) { node.timestamp = attr.timestamp } if (attr.size !== undefined) { MEMFS.resizeFileStorage(node, attr.size) } }, lookup: function(parent, name) { throw FS.genericErrors[44] }, mknod: function(parent, name, mode, dev) { return MEMFS.createNode(parent, name, mode, dev) }, rename: function(old_node, new_dir, new_name) { if (FS.isDir(old_node.mode)) { var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (new_node) { for (var i in new_node.contents) { throw new FS.ErrnoError(55) } } } delete old_node.parent.contents[old_node.name] old_node.parent.timestamp = Date.now() old_node.name = new_name new_dir.contents[new_name] = old_node new_dir.timestamp = old_node.parent.timestamp old_node.parent = new_dir }, unlink: function(parent, name) { delete parent.contents[name] parent.timestamp = Date.now() }, rmdir: function(parent, name) { var node = FS.lookupNode(parent, name) for (var i in node.contents) { throw new FS.ErrnoError(55) } delete parent.contents[name] parent.timestamp = Date.now() }, readdir: function(node) { var entries = ['.', '..'] for (var key in node.contents) { if (!node.contents.hasOwnProperty(key)) { continue } entries.push(key) } return entries }, symlink: function(parent, newname, oldpath) { var node = MEMFS.createNode(parent, newname, 511 | 40960, 0) node.link = oldpath return node }, readlink: function(node) { if (!FS.isLink(node.mode)) { throw new FS.ErrnoError(28) } return node.link }, }, stream_ops: { read: function(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= stream.node.usedBytes) return 0 var size = Math.min(stream.node.usedBytes - position, length) if (size > 8 && contents.subarray) { buffer.set(contents.subarray(position, position + size), offset) } else { for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i] } return size }, write: function(stream, buffer, offset, length, position, canOwn) { if (buffer.buffer === HEAP8.buffer) { canOwn = false } if (!length) return 0 var node = stream.node node.timestamp = Date.now() if (buffer.subarray && (!node.contents || node.contents.subarray)) { if (canOwn) { node.contents = buffer.subarray(offset, offset + length) node.usedBytes = length return length } else if (node.usedBytes === 0 && position === 0) { node.contents = buffer.slice(offset, offset + length) node.usedBytes = length return length } else if (position + length <= node.usedBytes) { node.contents.set( buffer.subarray(offset, offset + length), position ) return length } } MEMFS.expandFileStorage(node, position + length) if (node.contents.subarray && buffer.subarray) { node.contents.set( buffer.subarray(offset, offset + length), position ) } else { for (var i = 0; i < length; i++) { node.contents[position + i] = buffer[offset + i] } } node.usedBytes = Math.max(node.usedBytes, position + length) return length }, llseek: function(stream, offset, whence) { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { position += stream.node.usedBytes } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, allocate: function(stream, offset, length) { MEMFS.expandFileStorage(stream.node, offset + length) stream.node.usedBytes = Math.max( stream.node.usedBytes, offset + length ) }, mmap: function(stream, length, position, prot, flags) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr var allocated var contents = stream.node.contents if (!(flags & 2) && contents.buffer === buffer) { allocated = false ptr = contents.byteOffset } else { if (position > 0 || position + length < contents.length) { if (contents.subarray) { contents = contents.subarray(position, position + length) } else { contents = Array.prototype.slice.call( contents, position, position + length ) } } allocated = true ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } HEAP8.set(contents, ptr) } return { ptr: ptr, allocated: allocated } }, msync: function(stream, buffer, offset, length, mmapFlags) { MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } function asyncLoad(url, onload, onerror, noRunDep) { var dep = !noRunDep ? getUniqueRunDependency('al ' + url) : '' readAsync( url, arrayBuffer => { assert( arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).' ) onload(new Uint8Array(arrayBuffer)) if (dep) removeRunDependency(dep) }, event => { if (onerror) { onerror() } else { throw 'Loading data file "' + url + '" failed.' } } ) if (dep) addRunDependency(dep) } var ERRNO_CODES = {} var NODEFS = { isWindows: false, staticInit: () => { NODEFS.isWindows = !!process.platform.match(/^win/) var flags = process['binding']('constants') if (flags['fs']) { flags = flags['fs'] } NODEFS.flagsForNodeMap = { 1024: flags['O_APPEND'], 64: flags['O_CREAT'], 128: flags['O_EXCL'], 256: flags['O_NOCTTY'], 0: flags['O_RDONLY'], 2: flags['O_RDWR'], 4096: flags['O_SYNC'], 512: flags['O_TRUNC'], 1: flags['O_WRONLY'], 131072: flags['O_NOFOLLOW'], } }, convertNodeCode: e => { var code = e.code return ERRNO_CODES[code] }, mount: mount => { return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0) }, createNode: (parent, name, mode, dev) => { if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { throw new FS.ErrnoError(28) } var node = FS.createNode(parent, name, mode) node.node_ops = NODEFS.node_ops node.stream_ops = NODEFS.stream_ops return node }, getMode: path => { var stat try { stat = fs.lstatSync(path) if (NODEFS.isWindows) { stat.mode = stat.mode | ((stat.mode & 292) >> 2) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return stat.mode }, realPath: node => { var parts = [] while (node.parent !== node) { parts.push(node.name) node = node.parent } parts.push(node.mount.opts.root) parts.reverse() return PATH.join.apply(null, parts) }, flagsForNode: flags => { flags &= ~2097152 flags &= ~2048 flags &= ~32768 flags &= ~524288 flags &= ~65536 var newFlags = 0 for (var k in NODEFS.flagsForNodeMap) { if (flags & k) { newFlags |= NODEFS.flagsForNodeMap[k] flags ^= k } } if (flags) { throw new FS.ErrnoError(28) } return newFlags }, node_ops: { getattr: node => { var path = NODEFS.realPath(node) var stat try { stat = fs.lstatSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } if (NODEFS.isWindows && !stat.blksize) { stat.blksize = 4096 } if (NODEFS.isWindows && !stat.blocks) { stat.blocks = ((stat.size + stat.blksize - 1) / stat.blksize) | 0 } return { dev: stat.dev, ino: stat.ino, mode: stat.mode, nlink: stat.nlink, uid: stat.uid, gid: stat.gid, rdev: stat.rdev, size: stat.size, atime: stat.atime, mtime: stat.mtime, ctime: stat.ctime, blksize: stat.blksize, blocks: stat.blocks, } }, setattr: (node, attr) => { var path = NODEFS.realPath(node) try { if (attr.mode !== undefined) { fs.chmodSync(path, attr.mode) node.mode = attr.mode } if (attr.timestamp !== undefined) { var date = new Date(attr.timestamp) fs.utimesSync(path, date, date) } if (attr.size !== undefined) { fs.truncateSync(path, attr.size) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, lookup: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) var mode = NODEFS.getMode(path) return NODEFS.createNode(parent, name, mode) }, mknod: (parent, name, mode, dev) => { var node = NODEFS.createNode(parent, name, mode, dev) var path = NODEFS.realPath(node) try { if (FS.isDir(node.mode)) { fs.mkdirSync(path, node.mode) } else { fs.writeFileSync(path, '', { mode: node.mode }) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return node }, rename: (oldNode, newDir, newName) => { var oldPath = NODEFS.realPath(oldNode) var newPath = PATH.join2(NODEFS.realPath(newDir), newName) try { fs.renameSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } oldNode.name = newName }, unlink: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.unlinkSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, rmdir: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.rmdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readdir: node => { var path = NODEFS.realPath(node) try { return fs.readdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, symlink: (parent, newName, oldPath) => { var newPath = PATH.join2(NODEFS.realPath(parent), newName) try { fs.symlinkSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readlink: node => { var path = NODEFS.realPath(node) try { path = fs.readlinkSync(path) path = nodePath.relative( nodePath.resolve(node.mount.opts.root), path ) return path } catch (e) { if (!e.code) throw e if (e.code === 'UNKNOWN') throw new FS.ErrnoError(28) throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, }, stream_ops: { open: stream => { var path = NODEFS.realPath(stream.node) try { if (FS.isFile(stream.node.mode)) { stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags)) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, close: stream => { try { if (FS.isFile(stream.node.mode) && stream.nfd) { fs.closeSync(stream.nfd) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, read: (stream, buffer, offset, length, position) => { if (length === 0) return 0 try { return fs.readSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, write: (stream, buffer, offset, length, position) => { try { return fs.writeSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, llseek: (stream, offset, whence) => { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { try { var stat = fs.fstatSync(stream.nfd) position += stat.size } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, mmap: (stream, length, position, prot, flags) => { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr = mmapAlloc(length) NODEFS.stream_ops.read(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } }, msync: (stream, buffer, offset, length, mmapFlags) => { NODEFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } var FS = { root: null, mounts: [], devices: {}, streams: [], nextInode: 1, nameTable: null, currentPath: '/', initialized: false, ignorePermissions: true, ErrnoError: null, genericErrors: {}, filesystems: null, syncFSRequests: 0, lookupPath: (path, opts = {}) => { path = PATH_FS.resolve(path) if (!path) return { path: '', node: null } var defaults = { follow_mount: true, recurse_count: 0 } opts = Object.assign(defaults, opts) if (opts.recurse_count > 8) { throw new FS.ErrnoError(32) } var parts = path.split('/').filter(p => !!p) var current = FS.root var current_path = '/' for (var i = 0; i < parts.length; i++) { var islast = i === parts.length - 1 if (islast && opts.parent) { break } current = FS.lookupNode(current, parts[i]) current_path = PATH.join2(current_path, parts[i]) if (FS.isMountpoint(current)) { if (!islast || (islast && opts.follow_mount)) { current = current.mounted.root } } if (!islast || opts.follow) { var count = 0 while (FS.isLink(current.mode)) { var link = FS.readlink(current_path) current_path = PATH_FS.resolve(PATH.dirname(current_path), link) var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count + 1, }) current = lookup.node if (count++ > 40) { throw new FS.ErrnoError(32) } } } } return { path: current_path, node: current } }, getPath: node => { var path while (true) { if (FS.isRoot(node)) { var mount = node.mount.mountpoint if (!path) return mount return mount[mount.length - 1] !== '/' ? mount + '/' + path : mount + path } path = path ? node.name + '/' + path : node.name node = node.parent } }, hashName: (parentid, name) => { var hash = 0 for (var i = 0; i < name.length; i++) { hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0 } return ((parentid + hash) >>> 0) % FS.nameTable.length }, hashAddNode: node => { var hash = FS.hashName(node.parent.id, node.name) node.name_next = FS.nameTable[hash] FS.nameTable[hash] = node }, hashRemoveNode: node => { var hash = FS.hashName(node.parent.id, node.name) if (FS.nameTable[hash] === node) { FS.nameTable[hash] = node.name_next } else { var current = FS.nameTable[hash] while (current) { if (current.name_next === node) { current.name_next = node.name_next break } current = current.name_next } } }, lookupNode: (parent, name) => { var errCode = FS.mayLookup(parent) if (errCode) { throw new FS.ErrnoError(errCode, parent) } var hash = FS.hashName(parent.id, name) for (var node = FS.nameTable[hash]; node; node = node.name_next) { var nodeName = node.name if (node.parent.id === parent.id && nodeName === name) { return node } } return FS.lookup(parent, name) }, createNode: (parent, name, mode, rdev) => { var node = new FS.FSNode(parent, name, mode, rdev) FS.hashAddNode(node) return node }, destroyNode: node => { FS.hashRemoveNode(node) }, isRoot: node => { return node === node.parent }, isMountpoint: node => { return !!node.mounted }, isFile: mode => { return (mode & 61440) === 32768 }, isDir: mode => { return (mode & 61440) === 16384 }, isLink: mode => { return (mode & 61440) === 40960 }, isChrdev: mode => { return (mode & 61440) === 8192 }, isBlkdev: mode => { return (mode & 61440) === 24576 }, isFIFO: mode => { return (mode & 61440) === 4096 }, isSocket: mode => { return (mode & 49152) === 49152 }, flagModes: { r: 0, 'r+': 2, w: 577, 'w+': 578, a: 1089, 'a+': 1090 }, modeStringToFlags: str => { var flags = FS.flagModes[str] if (typeof flags == 'undefined') { throw new Error('Unknown file open mode: ' + str) } return flags }, flagsToPermissionString: flag => { var perms = ['r', 'w', 'rw'][flag & 3] if (flag & 512) { perms += 'w' } return perms }, nodePermissions: (node, perms) => { if (FS.ignorePermissions) { return 0 } if (perms.includes('r') && !(node.mode & 292)) { return 2 } else if (perms.includes('w') && !(node.mode & 146)) { return 2 } else if (perms.includes('x') && !(node.mode & 73)) { return 2 } return 0 }, mayLookup: dir => { var errCode = FS.nodePermissions(dir, 'x') if (errCode) return errCode if (!dir.node_ops.lookup) return 2 return 0 }, mayCreate: (dir, name) => { try { var node = FS.lookupNode(dir, name) return 20 } catch (e) {} return FS.nodePermissions(dir, 'wx') }, mayDelete: (dir, name, isdir) => { var node try { node = FS.lookupNode(dir, name) } catch (e) { return e.errno } var errCode = FS.nodePermissions(dir, 'wx') if (errCode) { return errCode } if (isdir) { if (!FS.isDir(node.mode)) { return 54 } if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return 10 } } else { if (FS.isDir(node.mode)) { return 31 } } return 0 }, mayOpen: (node, flags) => { if (!node) { return 44 } if (FS.isLink(node.mode)) { return 32 } else if (FS.isDir(node.mode)) { if (FS.flagsToPermissionString(flags) !== 'r' || flags & 512) { return 31 } } return FS.nodePermissions(node, FS.flagsToPermissionString(flags)) }, MAX_OPEN_FDS: 4096, nextfd: (fd_start = 0, fd_end = FS.MAX_OPEN_FDS) => { for (var fd = fd_start; fd <= fd_end; fd++) { if (!FS.streams[fd]) { return fd } } throw new FS.ErrnoError(33) }, getStream: fd => FS.streams[fd], createStream: (stream, fd_start, fd_end) => { if (!FS.FSStream) { FS.FSStream = function() { this.shared = {} } FS.FSStream.prototype = {} Object.defineProperties(FS.FSStream.prototype, { object: { get: function() { return this.node }, set: function(val) { this.node = val }, }, isRead: { get: function() { return (this.flags & 2097155) !== 1 }, }, isWrite: { get: function() { return (this.flags & 2097155) !== 0 }, }, isAppend: { get: function() { return this.flags & 1024 }, }, flags: { get: function() { return this.shared.flags }, set: function(val) { this.shared.flags = val }, }, position: { get: function() { return this.shared.position }, set: function(val) { this.shared.position = val }, }, }) } stream = Object.assign(new FS.FSStream(), stream) var fd = FS.nextfd(fd_start, fd_end) stream.fd = fd FS.streams[fd] = stream return stream }, closeStream: fd => { FS.streams[fd] = null }, chrdev_stream_ops: { open: stream => { var device = FS.getDevice(stream.node.rdev) stream.stream_ops = device.stream_ops if (stream.stream_ops.open) { stream.stream_ops.open(stream) } }, llseek: () => { throw new FS.ErrnoError(70) }, }, major: dev => dev >> 8, minor: dev => dev & 255, makedev: (ma, mi) => (ma << 8) | mi, registerDevice: (dev, ops) => { FS.devices[dev] = { stream_ops: ops } }, getDevice: dev => FS.devices[dev], getMounts: mount => { var mounts = [] var check = [mount] while (check.length) { var m = check.pop() mounts.push(m) check.push.apply(check, m.mounts) } return mounts }, syncfs: (populate, callback) => { if (typeof populate == 'function') { callback = populate populate = false } FS.syncFSRequests++ if (FS.syncFSRequests > 1) { err( 'warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work' ) } var mounts = FS.getMounts(FS.root.mount) var completed = 0 function doCallback(errCode) { FS.syncFSRequests-- return callback(errCode) } function done(errCode) { if (errCode) { if (!done.errored) { done.errored = true return doCallback(errCode) } return } if (++completed >= mounts.length) { doCallback(null) } } mounts.forEach(mount => { if (!mount.type.syncfs) { return done(null) } mount.type.syncfs(mount, populate, done) }) }, mount: (type, opts, mountpoint) => { var root = mountpoint === '/' var pseudo = !mountpoint var node if (root && FS.root) { throw new FS.ErrnoError(10) } else if (!root && !pseudo) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) mountpoint = lookup.path node = lookup.node if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } if (!FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } } var mount = { type: type, opts: opts, mountpoint: mountpoint, mounts: [], } var mountRoot = type.mount(mount) mountRoot.mount = mount mount.root = mountRoot if (root) { FS.root = mountRoot } else if (node) { node.mounted = mount if (node.mount) { node.mount.mounts.push(mount) } } return mountRoot }, unmount: mountpoint => { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) if (!FS.isMountpoint(lookup.node)) { throw new FS.ErrnoError(28) } var node = lookup.node var mount = node.mounted var mounts = FS.getMounts(mount) Object.keys(FS.nameTable).forEach(hash => { var current = FS.nameTable[hash] while (current) { var next = current.name_next if (mounts.includes(current.mount)) { FS.destroyNode(current) } current = next } }) node.mounted = null var idx = node.mount.mounts.indexOf(mount) node.mount.mounts.splice(idx, 1) }, lookup: (parent, name) => { return parent.node_ops.lookup(parent, name) }, mknod: (path, mode, dev) => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) if (!name || name === '.' || name === '..') { throw new FS.ErrnoError(28) } var errCode = FS.mayCreate(parent, name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.mknod) { throw new FS.ErrnoError(63) } return parent.node_ops.mknod(parent, name, mode, dev) }, create: (path, mode) => { mode = mode !== undefined ? mode : 438 mode &= 4095 mode |= 32768 return FS.mknod(path, mode, 0) }, mkdir: (path, mode) => { mode = mode !== undefined ? mode : 511 mode &= 511 | 512 mode |= 16384 return FS.mknod(path, mode, 0) }, mkdirTree: (path, mode) => { var dirs = path.split('/') var d = '' for (var i = 0; i < dirs.length; ++i) { if (!dirs[i]) continue d += '/' + dirs[i] try { FS.mkdir(d, mode) } catch (e) { if (e.errno != 20) throw e } } }, mkdev: (path, mode, dev) => { if (typeof dev == 'undefined') { dev = mode mode = 438 } mode |= 8192 return FS.mknod(path, mode, dev) }, symlink: (oldpath, newpath) => { if (!PATH_FS.resolve(oldpath)) { throw new FS.ErrnoError(44) } var lookup = FS.lookupPath(newpath, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var newname = PATH.basename(newpath) var errCode = FS.mayCreate(parent, newname) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.symlink) { throw new FS.ErrnoError(63) } return parent.node_ops.symlink(parent, newname, oldpath) }, rename: (old_path, new_path) => { var old_dirname = PATH.dirname(old_path) var new_dirname = PATH.dirname(new_path) var old_name = PATH.basename(old_path) var new_name = PATH.basename(new_path) var lookup, old_dir, new_dir lookup = FS.lookupPath(old_path, { parent: true }) old_dir = lookup.node lookup = FS.lookupPath(new_path, { parent: true }) new_dir = lookup.node if (!old_dir || !new_dir) throw new FS.ErrnoError(44) if (old_dir.mount !== new_dir.mount) { throw new FS.ErrnoError(75) } var old_node = FS.lookupNode(old_dir, old_name) var relative = PATH_FS.relative(old_path, new_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(28) } relative = PATH_FS.relative(new_path, old_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(55) } var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (old_node === new_node) { return } var isdir = FS.isDir(old_node.mode) var errCode = FS.mayDelete(old_dir, old_name, isdir) if (errCode) { throw new FS.ErrnoError(errCode) } errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!old_dir.node_ops.rename) { throw new FS.ErrnoError(63) } if ( FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node)) ) { throw new FS.ErrnoError(10) } if (new_dir !== old_dir) { errCode = FS.nodePermissions(old_dir, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } } FS.hashRemoveNode(old_node) try { old_dir.node_ops.rename(old_node, new_dir, new_name) } catch (e) { throw e } finally { FS.hashAddNode(old_node) } }, rmdir: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, true) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.rmdir) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.rmdir(parent, name) FS.destroyNode(node) }, readdir: path => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node if (!node.node_ops.readdir) { throw new FS.ErrnoError(54) } return node.node_ops.readdir(node) }, unlink: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, false) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.unlink) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.unlink(parent, name) FS.destroyNode(node) }, readlink: path => { var lookup = FS.lookupPath(path) var link = lookup.node if (!link) { throw new FS.ErrnoError(44) } if (!link.node_ops.readlink) { throw new FS.ErrnoError(28) } return PATH_FS.resolve( FS.getPath(link.parent), link.node_ops.readlink(link) ) }, stat: (path, dontFollow) => { var lookup = FS.lookupPath(path, { follow: !dontFollow }) var node = lookup.node if (!node) { throw new FS.ErrnoError(44) } if (!node.node_ops.getattr) { throw new FS.ErrnoError(63) } return node.node_ops.getattr(node) }, lstat: path => { return FS.stat(path, true) }, chmod: (path, mode, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { mode: (mode & 4095) | (node.mode & ~4095), timestamp: Date.now(), }) }, lchmod: (path, mode) => { FS.chmod(path, mode, true) }, fchmod: (fd, mode) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chmod(stream.node, mode) }, chown: (path, uid, gid, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { timestamp: Date.now() }) }, lchown: (path, uid, gid) => { FS.chown(path, uid, gid, true) }, fchown: (fd, uid, gid) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chown(stream.node, uid, gid) }, truncate: (path, len) => { if (len < 0) { throw new FS.ErrnoError(28) } var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: true }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } if (FS.isDir(node.mode)) { throw new FS.ErrnoError(31) } if (!FS.isFile(node.mode)) { throw new FS.ErrnoError(28) } var errCode = FS.nodePermissions(node, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } node.node_ops.setattr(node, { size: len, timestamp: Date.now() }) }, ftruncate: (fd, len) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(28) } FS.truncate(stream.node, len) }, utime: (path, atime, mtime) => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node node.node_ops.setattr(node, { timestamp: Math.max(atime, mtime) }) }, open: (path, flags, mode) => { if (path === '') { throw new FS.ErrnoError(44) } flags = typeof flags == 'string' ? FS.modeStringToFlags(flags) : flags mode = typeof mode == 'undefined' ? 438 : mode if (flags & 64) { mode = (mode & 4095) | 32768 } else { mode = 0 } var node if (typeof path == 'object') { node = path } else { path = PATH.normalize(path) try { var lookup = FS.lookupPath(path, { follow: !(flags & 131072) }) node = lookup.node } catch (e) {} } var created = false if (flags & 64) { if (node) { if (flags & 128) { throw new FS.ErrnoError(20) } } else { node = FS.mknod(path, mode, 0) created = true } } if (!node) { throw new FS.ErrnoError(44) } if (FS.isChrdev(node.mode)) { flags &= ~512 } if (flags & 65536 && !FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } if (!created) { var errCode = FS.mayOpen(node, flags) if (errCode) { throw new FS.ErrnoError(errCode) } } if (flags & 512 && !created) { FS.truncate(node, 0) } flags &= ~(128 | 512 | 131072) var stream = FS.createStream({ node: node, path: FS.getPath(node), flags: flags, seekable: true, position: 0, stream_ops: node.stream_ops, ungotten: [], error: false, }) if (stream.stream_ops.open) { stream.stream_ops.open(stream) } if (Module['logReadFiles'] && !(flags & 1)) { if (!FS.readFiles) FS.readFiles = {} if (!(path in FS.readFiles)) { FS.readFiles[path] = 1 } } return stream }, close: stream => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (stream.getdents) stream.getdents = null try { if (stream.stream_ops.close) { stream.stream_ops.close(stream) } } catch (e) { throw e } finally { FS.closeStream(stream.fd) } stream.fd = null }, isClosed: stream => { return stream.fd === null }, llseek: (stream, offset, whence) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (!stream.seekable || !stream.stream_ops.llseek) { throw new FS.ErrnoError(70) } if (whence != 0 && whence != 1 && whence != 2) { throw new FS.ErrnoError(28) } stream.position = stream.stream_ops.llseek(stream, offset, whence) stream.ungotten = [] return stream.position }, read: (stream, buffer, offset, length, position) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.read) { throw new FS.ErrnoError(28) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesRead = stream.stream_ops.read( stream, buffer, offset, length, position ) if (!seeking) stream.position += bytesRead return bytesRead }, write: (stream, buffer, offset, length, position, canOwn) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.write) { throw new FS.ErrnoError(28) } if (stream.seekable && stream.flags & 1024) { FS.llseek(stream, 0, 2) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesWritten = stream.stream_ops.write( stream, buffer, offset, length, position, canOwn ) if (!seeking) stream.position += bytesWritten return bytesWritten }, allocate: (stream, offset, length) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (offset < 0 || length <= 0) { throw new FS.ErrnoError(28) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(43) } if (!stream.stream_ops.allocate) { throw new FS.ErrnoError(138) } stream.stream_ops.allocate(stream, offset, length) }, mmap: (stream, length, position, prot, flags) => { if ( (prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2 ) { throw new FS.ErrnoError(2) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(2) } if (!stream.stream_ops.mmap) { throw new FS.ErrnoError(43) } return stream.stream_ops.mmap(stream, length, position, prot, flags) }, msync: (stream, buffer, offset, length, mmapFlags) => { if (!stream.stream_ops.msync) { return 0 } return stream.stream_ops.msync( stream, buffer, offset, length, mmapFlags ) }, munmap: stream => 0, ioctl: (stream, cmd, arg) => { if (!stream.stream_ops.ioctl) { throw new FS.ErrnoError(59) } return stream.stream_ops.ioctl(stream, cmd, arg) }, readFile: (path, opts = {}) => { opts.flags = opts.flags || 0 opts.encoding = opts.encoding || 'binary' if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { throw new Error('Invalid encoding type "' + opts.encoding + '"') } var ret var stream = FS.open(path, opts.flags) var stat = FS.stat(path) var length = stat.size var buf = new Uint8Array(length) FS.read(stream, buf, 0, length, 0) if (opts.encoding === 'utf8') { ret = UTF8ArrayToString(buf, 0) } else if (opts.encoding === 'binary') { ret = buf } FS.close(stream) return ret }, writeFile: (path, data, opts = {}) => { opts.flags = opts.flags || 577 var stream = FS.open(path, opts.flags, opts.mode) if (typeof data == 'string') { var buf = new Uint8Array(lengthBytesUTF8(data) + 1) var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length) FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn) } else if (ArrayBuffer.isView(data)) { FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn) } else { throw new Error('Unsupported data type') } FS.close(stream) }, cwd: () => FS.currentPath, chdir: path => { var lookup = FS.lookupPath(path, { follow: true }) if (lookup.node === null) { throw new FS.ErrnoError(44) } if (!FS.isDir(lookup.node.mode)) { throw new FS.ErrnoError(54) } var errCode = FS.nodePermissions(lookup.node, 'x') if (errCode) { throw new FS.ErrnoError(errCode) } FS.currentPath = lookup.path }, createDefaultDirectories: () => { FS.mkdir('/tmp') FS.mkdir('/home') FS.mkdir('/home/web_user') }, createDefaultDevices: () => { FS.mkdir('/dev') FS.registerDevice(FS.makedev(1, 3), { read: () => 0, write: (stream, buffer, offset, length, pos) => length, }) FS.mkdev('/dev/null', FS.makedev(1, 3)) TTY.register(FS.makedev(5, 0), TTY.default_tty_ops) TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops) FS.mkdev('/dev/tty', FS.makedev(5, 0)) FS.mkdev('/dev/tty1', FS.makedev(6, 0)) var random_device = getRandomDevice() FS.createDevice('/dev', 'random', random_device) FS.createDevice('/dev', 'urandom', random_device) FS.mkdir('/dev/shm') FS.mkdir('/dev/shm/tmp') }, createSpecialDirectories: () => { FS.mkdir('/proc') var proc_self = FS.mkdir('/proc/self') FS.mkdir('/proc/self/fd') FS.mount( { mount: () => { var node = FS.createNode(proc_self, 'fd', 16384 | 511, 73) node.node_ops = { lookup: (parent, name) => { var fd = +name var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) var ret = { parent: null, mount: { mountpoint: 'fake' }, node_ops: { readlink: () => stream.path }, } ret.parent = ret return ret }, } return node }, }, {}, '/proc/self/fd' ) }, createStandardStreams: () => { if (Module['stdin']) { FS.createDevice('/dev', 'stdin', Module['stdin']) } else { FS.symlink('/dev/tty', '/dev/stdin') } if (Module['stdout']) { FS.createDevice('/dev', 'stdout', null, Module['stdout']) } else { FS.symlink('/dev/tty', '/dev/stdout') } if (Module['stderr']) { FS.createDevice('/dev', 'stderr', null, Module['stderr']) } else { FS.symlink('/dev/tty1', '/dev/stderr') } var stdin = FS.open('/dev/stdin', 0) var stdout = FS.open('/dev/stdout', 1) var stderr = FS.open('/dev/stderr', 1) }, ensureErrnoError: () => { if (FS.ErrnoError) return FS.ErrnoError = function ErrnoError(errno, node) { this.node = node this.setErrno = function(errno) { this.errno = errno } this.setErrno(errno) this.message = 'FS error' } FS.ErrnoError.prototype = new Error() FS.ErrnoError.prototype.constructor = FS.ErrnoError ;[44].forEach(code => { FS.genericErrors[code] = new FS.ErrnoError(code) FS.genericErrors[code].stack = '' }) }, staticInit: () => { FS.ensureErrnoError() FS.nameTable = new Array(4096) FS.mount(MEMFS, {}, '/') FS.createDefaultDirectories() FS.createDefaultDevices() FS.createSpecialDirectories() FS.filesystems = { MEMFS: MEMFS, NODEFS: NODEFS } }, init: (input, output, error) => { FS.init.initialized = true FS.ensureErrnoError() Module['stdin'] = input || Module['stdin'] Module['stdout'] = output || Module['stdout'] Module['stderr'] = error || Module['stderr'] FS.createStandardStreams() }, quit: () => { FS.init.initialized = false for (var i = 0; i < FS.streams.length; i++) { var stream = FS.streams[i] if (!stream) { continue } FS.close(stream) } }, getMode: (canRead, canWrite) => { var mode = 0 if (canRead) mode |= 292 | 73 if (canWrite) mode |= 146 return mode }, findObject: (path, dontResolveLastLink) => { var ret = FS.analyzePath(path, dontResolveLastLink) if (!ret.exists) { return null } return ret.object }, analyzePath: (path, dontResolveLastLink) => { try { var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) path = lookup.path } catch (e) {} var ret = { isRoot: false, exists: false, error: 0, name: null, path: null, object: null, parentExists: false, parentPath: null, parentObject: null, } try { var lookup = FS.lookupPath(path, { parent: true }) ret.parentExists = true ret.parentPath = lookup.path ret.parentObject = lookup.node ret.name = PATH.basename(path) lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) ret.exists = true ret.path = lookup.path ret.object = lookup.node ret.name = lookup.node.name ret.isRoot = lookup.path === '/' } catch (e) { ret.error = e.errno } return ret }, createPath: (parent, path, canRead, canWrite) => { parent = typeof parent == 'string' ? parent : FS.getPath(parent) var parts = path.split('/').reverse() while (parts.length) { var part = parts.pop() if (!part) continue var current = PATH.join2(parent, part) try { FS.mkdir(current) } catch (e) {} parent = current } return current }, createFile: (parent, name, properties, canRead, canWrite) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(canRead, canWrite) return FS.create(path, mode) }, createDataFile: (parent, name, data, canRead, canWrite, canOwn) => { var path = name if (parent) { parent = typeof parent == 'string' ? parent : FS.getPath(parent) path = name ? PATH.join2(parent, name) : parent } var mode = FS.getMode(canRead, canWrite) var node = FS.create(path, mode) if (data) { if (typeof data == 'string') { var arr = new Array(data.length) for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i) data = arr } FS.chmod(node, mode | 146) var stream = FS.open(node, 577) FS.write(stream, data, 0, data.length, 0, canOwn) FS.close(stream) FS.chmod(node, mode) } return node }, createDevice: (parent, name, input, output) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(!!input, !!output) if (!FS.createDevice.major) FS.createDevice.major = 64 var dev = FS.makedev(FS.createDevice.major++, 0) FS.registerDevice(dev, { open: stream => { stream.seekable = false }, close: stream => { if (output && output.buffer && output.buffer.length) { output(10) } }, read: (stream, buffer, offset, length, pos) => { var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = input() } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: (stream, buffer, offset, length, pos) => { for (var i = 0; i < length; i++) { try { output(buffer[offset + i]) } catch (e) { throw new FS.ErrnoError(29) } } if (length) { stream.node.timestamp = Date.now() } return i }, }) return FS.mkdev(path, mode, dev) }, forceLoadFile: obj => { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true if (typeof XMLHttpRequest != 'undefined') { throw new Error( 'Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.' ) } else if (read_) { try { obj.contents = intArrayFromString(read_(obj.url), true) obj.usedBytes = obj.contents.length } catch (e) { throw new FS.ErrnoError(29) } } else { throw new Error('Cannot load without read() or XMLHttpRequest.') } }, createLazyFile: (parent, name, url, canRead, canWrite) => { function LazyUint8Array() { this.lengthKnown = false this.chunks = [] } LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) { if (idx > this.length - 1 || idx < 0) { return undefined } var chunkOffset = idx % this.chunkSize var chunkNum = (idx / this.chunkSize) | 0 return this.getter(chunkNum)[chunkOffset] } LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter( getter ) { this.getter = getter } LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { var xhr = new XMLHttpRequest() xhr.open('HEAD', url, false) xhr.send(null) if (!((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) throw new Error("Couldn't load " + url + '. Status: ' + xhr.status) var datalength = Number(xhr.getResponseHeader('Content-length')) var header var hasByteServing = (header = xhr.getResponseHeader('Accept-Ranges')) && header === 'bytes' var usesGzip = (header = xhr.getResponseHeader('Content-Encoding')) && header === 'gzip' var chunkSize = 1024 * 1024 if (!hasByteServing) chunkSize = datalength var doXHR = (from, to) => { if (from > to) throw new Error( 'invalid range (' + from + ', ' + to + ') or no bytes requested!' ) if (to > datalength - 1) throw new Error( 'only ' + datalength + ' bytes available! programmer error!' ) var xhr = new XMLHttpRequest() xhr.open('GET', url, false) if (datalength !== chunkSize) xhr.setRequestHeader('Range', 'bytes=' + from + '-' + to) xhr.responseType = 'arraybuffer' if (xhr.overrideMimeType) { xhr.overrideMimeType('text/plain; charset=x-user-defined') } xhr.send(null) if ( !((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ) throw new Error( "Couldn't load " + url + '. Status: ' + xhr.status ) if (xhr.response !== undefined) { return new Uint8Array(xhr.response || []) } return intArrayFromString(xhr.responseText || '', true) } var lazyArray = this lazyArray.setDataGetter(chunkNum => { var start = chunkNum * chunkSize var end = (chunkNum + 1) * chunkSize - 1 end = Math.min(end, datalength - 1) if (typeof lazyArray.chunks[chunkNum] == 'undefined') { lazyArray.chunks[chunkNum] = doXHR(start, end) } if (typeof lazyArray.chunks[chunkNum] == 'undefined') throw new Error('doXHR failed!') return lazyArray.chunks[chunkNum] }) if (usesGzip || !datalength) { chunkSize = datalength = 1 datalength = this.getter(0).length chunkSize = datalength out( 'LazyFiles on gzip forces download of the whole file when length is accessed' ) } this._length = datalength this._chunkSize = chunkSize this.lengthKnown = true } if (typeof XMLHttpRequest != 'undefined') { if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc' var lazyArray = new LazyUint8Array() Object.defineProperties(lazyArray, { length: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._length }, }, chunkSize: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._chunkSize }, }, }) var properties = { isDevice: false, contents: lazyArray } } else { var properties = { isDevice: false, url: url } } var node = FS.createFile(parent, name, properties, canRead, canWrite) if (properties.contents) { node.contents = properties.contents } else if (properties.url) { node.contents = null node.url = properties.url } Object.defineProperties(node, { usedBytes: { get: function() { return this.contents.length }, }, }) var stream_ops = {} var keys = Object.keys(node.stream_ops) keys.forEach(key => { var fn = node.stream_ops[key] stream_ops[key] = function forceLoadLazyFile() { FS.forceLoadFile(node) return fn.apply(null, arguments) } }) function writeChunks(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= contents.length) return 0 var size = Math.min(contents.length - position, length) if (contents.slice) { for (var i = 0; i < size; i++) { buffer[offset + i] = contents[position + i] } } else { for (var i = 0; i < size; i++) { buffer[offset + i] = contents.get(position + i) } } return size } stream_ops.read = (stream, buffer, offset, length, position) => { FS.forceLoadFile(node) return writeChunks(stream, buffer, offset, length, position) } stream_ops.mmap = (stream, length, position, prot, flags) => { FS.forceLoadFile(node) var ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } writeChunks(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } } node.stream_ops = stream_ops return node }, createPreloadedFile: ( parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish ) => { var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent var dep = getUniqueRunDependency('cp ' + fullname) function processData(byteArray) { function finish(byteArray) { if (preFinish) preFinish() if (!dontCreateFile) { FS.createDataFile( parent, name, byteArray, canRead, canWrite, canOwn ) } if (onload) onload() removeRunDependency(dep) } if ( Browser.handledByPreloadPlugin(byteArray, fullname, finish, () => { if (onerror) onerror() removeRunDependency(dep) }) ) { return } finish(byteArray) } addRunDependency(dep) if (typeof url == 'string') { asyncLoad(url, byteArray => processData(byteArray), onerror) } else { processData(url) } }, indexedDB: () => { return ( window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB ) }, DB_NAME: () => { return 'EM_FS_' + window.location.pathname }, DB_VERSION: 20, DB_STORE_NAME: 'FILE_DATA', saveFilesToDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = () => { out('creating db') var db = openRequest.result db.createObjectStore(FS.DB_STORE_NAME) } openRequest.onsuccess = () => { var db = openRequest.result var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite') var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var putRequest = files.put( FS.analyzePath(path).object.contents, path ) putRequest.onsuccess = () => { ok++ if (ok + fail == total) finish() } putRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, loadFilesFromDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = onerror openRequest.onsuccess = () => { var db = openRequest.result try { var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly') } catch (e) { onerror(e) return } var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var getRequest = files.get(path) getRequest.onsuccess = () => { if (FS.analyzePath(path).exists) { FS.unlink(path) } FS.createDataFile( PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true ) ok++ if (ok + fail == total) finish() } getRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, } var SYSCALLS = { DEFAULT_POLLMASK: 5, calculateAt: function(dirfd, path, allowEmpty) { if (PATH.isAbs(path)) { return path } var dir if (dirfd === -100) { dir = FS.cwd() } else { var dirstream = SYSCALLS.getStreamFromFD(dirfd) dir = dirstream.path } if (path.length == 0) { if (!allowEmpty) { throw new FS.ErrnoError(44) } return dir } return PATH.join2(dir, path) }, doStat: function(func, path, buf) { try { var stat = func(path) } catch (e) { if ( e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node)) ) { return -54 } throw e } HEAP32[buf >> 2] = stat.dev HEAP32[(buf + 8) >> 2] = stat.ino HEAP32[(buf + 12) >> 2] = stat.mode HEAPU32[(buf + 16) >> 2] = stat.nlink HEAP32[(buf + 20) >> 2] = stat.uid HEAP32[(buf + 24) >> 2] = stat.gid HEAP32[(buf + 28) >> 2] = stat.rdev ;(tempI64 = [ stat.size >>> 0, ((tempDouble = stat.size), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 40) >> 2] = tempI64[0]), (HEAP32[(buf + 44) >> 2] = tempI64[1]) HEAP32[(buf + 48) >> 2] = 4096 HEAP32[(buf + 52) >> 2] = stat.blocks var atime = stat.atime.getTime() var mtime = stat.mtime.getTime() var ctime = stat.ctime.getTime() ;(tempI64 = [ Math.floor(atime / 1e3) >>> 0, ((tempDouble = Math.floor(atime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 56) >> 2] = tempI64[0]), (HEAP32[(buf + 60) >> 2] = tempI64[1]) HEAPU32[(buf + 64) >> 2] = (atime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(mtime / 1e3) >>> 0, ((tempDouble = Math.floor(mtime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 72) >> 2] = tempI64[0]), (HEAP32[(buf + 76) >> 2] = tempI64[1]) HEAPU32[(buf + 80) >> 2] = (mtime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(ctime / 1e3) >>> 0, ((tempDouble = Math.floor(ctime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 88) >> 2] = tempI64[0]), (HEAP32[(buf + 92) >> 2] = tempI64[1]) HEAPU32[(buf + 96) >> 2] = (ctime % 1e3) * 1e3 ;(tempI64 = [ stat.ino >>> 0, ((tempDouble = stat.ino), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 104) >> 2] = tempI64[0]), (HEAP32[(buf + 108) >> 2] = tempI64[1]) return 0 }, doMsync: function(addr, stream, len, flags, offset) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } if (flags & 2) { return 0 } var buffer = HEAPU8.slice(addr, addr + len) FS.msync(stream, buffer, offset, len, flags) }, varargs: undefined, get: function() { SYSCALLS.varargs += 4 var ret = HEAP32[(SYSCALLS.varargs - 4) >> 2] return ret }, getStr: function(ptr) { var ret = UTF8ToString(ptr) return ret }, getStreamFromFD: function(fd) { var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) return stream }, } function ___syscall_fcntl64(fd, cmd, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (cmd) { case 0: { var arg = SYSCALLS.get() if (arg < 0) { return -28 } var newStream newStream = FS.createStream(stream, arg) return newStream.fd } case 1: case 2: return 0 case 3: return stream.flags case 4: { var arg = SYSCALLS.get() stream.flags |= arg return 0 } case 5: { var arg = SYSCALLS.get() var offset = 0 HEAP16[(arg + offset) >> 1] = 2 return 0 } case 6: case 7: return 0 case 16: case 8: return -28 case 9: setErrNo(28) return -1 default: { return -28 } } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_getcwd(buf, size) { try { if (size === 0) return -28 var cwd = FS.cwd() var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1 if (size < cwdLengthInBytes) return -68 stringToUTF8(cwd, buf, size) return cwdLengthInBytes } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_ioctl(fd, op, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (op) { case 21509: case 21505: { if (!stream.tty) return -59 return 0 } case 21510: case 21511: case 21512: case 21506: case 21507: case 21508: { if (!stream.tty) return -59 return 0 } case 21519: { if (!stream.tty) return -59 var argp = SYSCALLS.get() HEAP32[argp >> 2] = 0 return 0 } case 21520: { if (!stream.tty) return -59 return -28 } case 21531: { var argp = SYSCALLS.get() return FS.ioctl(stream, op, argp) } case 21523: { if (!stream.tty) return -59 return 0 } case 21524: { if (!stream.tty) return -59 return 0 } default: return -28 } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_openat(dirfd, path, flags, varargs) { SYSCALLS.varargs = varargs try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) var mode = varargs ? SYSCALLS.get() : 0 return FS.open(path, flags, mode).fd } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_readlinkat(dirfd, path, buf, bufsize) { try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) if (bufsize <= 0) return -28 var ret = FS.readlink(path) var len = Math.min(bufsize, lengthBytesUTF8(ret)) var endChar = HEAP8[buf + len] stringToUTF8(ret, buf, bufsize + 1) HEAP8[buf + len] = endChar return len } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_stat64(path, buf) { try { path = SYSCALLS.getStr(path) return SYSCALLS.doStat(FS.stat, path, buf) } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function _abort() { abort('') } function _emscripten_memcpy_big(dest, src, num) { HEAPU8.copyWithin(dest, src, src + num) } function getHeapMax() { return 2147483648 } function emscripten_realloc_buffer(size) { try { wasmMemory.grow((size - buffer.byteLength + 65535) >>> 16) updateGlobalBufferAndViews(wasmMemory.buffer) return 1 } catch (e) {} } function _emscripten_resize_heap(requestedSize) { var oldSize = HEAPU8.length requestedSize = requestedSize >>> 0 var maxHeapSize = getHeapMax() if (requestedSize > maxHeapSize) { return false } let alignUp = (x, multiple) => x + ((multiple - (x % multiple)) % multiple) for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown) overGrownHeapSize = Math.min( overGrownHeapSize, requestedSize + 100663296 ) var newSize = Math.min( maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536) ) var replacement = emscripten_realloc_buffer(newSize) if (replacement) { return true } } return false } var ENV = {} function getExecutableName() { return thisProgram || './this.program' } function getEnvStrings() { if (!getEnvStrings.strings) { var lang = ( (typeof navigator == 'object' && navigator.languages && navigator.languages[0]) || 'C' ).replace('-', '_') + '.UTF-8' var env = { USER: 'web_user', LOGNAME: 'web_user', PATH: '/', PWD: '/', HOME: '/home/web_user', LANG: lang, _: getExecutableName(), } for (var x in ENV) { if (ENV[x] === undefined) delete env[x] else env[x] = ENV[x] } var strings = [] for (var x in env) { strings.push(x + '=' + env[x]) } getEnvStrings.strings = strings } return getEnvStrings.strings } function writeAsciiToMemory(str, buffer, dontAddNull) { for (var i = 0; i < str.length; ++i) { HEAP8[buffer++ >> 0] = str.charCodeAt(i) } if (!dontAddNull) HEAP8[buffer >> 0] = 0 } function _environ_get(__environ, environ_buf) { var bufSize = 0 getEnvStrings().forEach(function(string, i) { var ptr = environ_buf + bufSize HEAPU32[(__environ + i * 4) >> 2] = ptr writeAsciiToMemory(string, ptr) bufSize += string.length + 1 }) return 0 } function _environ_sizes_get(penviron_count, penviron_buf_size) { var strings = getEnvStrings() HEAPU32[penviron_count >> 2] = strings.length var bufSize = 0 strings.forEach(function(string) { bufSize += string.length + 1 }) HEAPU32[penviron_buf_size >> 2] = bufSize return 0 } function _proc_exit(code) { EXITSTATUS = code if (!keepRuntimeAlive()) { if (Module['onExit']) Module['onExit'](code) ABORT = true } quit_(code, new ExitStatus(code)) } function exitJS(status, implicit) { EXITSTATUS = status _proc_exit(status) } var _exit = exitJS function _fd_close(fd) { try { var stream = SYSCALLS.getStreamFromFD(fd) FS.close(stream) return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doReadv(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.read(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr if (curr < len) break } return ret } function _fd_read(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doReadv(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function convertI32PairToI53Checked(lo, hi) { return (hi + 2097152) >>> 0 < 4194305 - !!lo ? (lo >>> 0) + hi * 4294967296 : NaN } function _fd_seek(fd, offset_low, offset_high, whence, newOffset) { try { var offset = convertI32PairToI53Checked(offset_low, offset_high) if (isNaN(offset)) return 61 var stream = SYSCALLS.getStreamFromFD(fd) FS.llseek(stream, offset, whence) ;(tempI64 = [ stream.position >>> 0, ((tempDouble = stream.position), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[newOffset >> 2] = tempI64[0]), (HEAP32[(newOffset + 4) >> 2] = tempI64[1]) if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doWritev(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.write(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr } return ret } function _fd_write(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doWritev(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function __isLeapYear(year) { return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) } function __arraySum(array, index) { var sum = 0 for (var i = 0; i <= index; sum += array[i++]) {} return sum } var __MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] var __MONTH_DAYS_REGULAR = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] function __addDays(date, days) { var newDate = new Date(date.getTime()) while (days > 0) { var leap = __isLeapYear(newDate.getFullYear()) var currentMonth = newDate.getMonth() var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth] if (days > daysInCurrentMonth - newDate.getDate()) { days -= daysInCurrentMonth - newDate.getDate() + 1 newDate.setDate(1) if (currentMonth < 11) { newDate.setMonth(currentMonth + 1) } else { newDate.setMonth(0) newDate.setFullYear(newDate.getFullYear() + 1) } } else { newDate.setDate(newDate.getDate() + days) return newDate } } return newDate } function writeArrayToMemory(array, buffer) { HEAP8.set(array, buffer) } function _strftime(s, maxsize, format, tm) { var tm_zone = HEAP32[(tm + 40) >> 2] var date = { tm_sec: HEAP32[tm >> 2], tm_min: HEAP32[(tm + 4) >> 2], tm_hour: HEAP32[(tm + 8) >> 2], tm_mday: HEAP32[(tm + 12) >> 2], tm_mon: HEAP32[(tm + 16) >> 2], tm_year: HEAP32[(tm + 20) >> 2], tm_wday: HEAP32[(tm + 24) >> 2], tm_yday: HEAP32[(tm + 28) >> 2], tm_isdst: HEAP32[(tm + 32) >> 2], tm_gmtoff: HEAP32[(tm + 36) >> 2], tm_zone: tm_zone ? UTF8ToString(tm_zone) : '', } var pattern = UTF8ToString(format) var EXPANSION_RULES_1 = { '%c': '%a %b %d %H:%M:%S %Y', '%D': '%m/%d/%y', '%F': '%Y-%m-%d', '%h': '%b', '%r': '%I:%M:%S %p', '%R': '%H:%M', '%T': '%H:%M:%S', '%x': '%m/%d/%y', '%X': '%H:%M:%S', '%Ec': '%c', '%EC': '%C', '%Ex': '%m/%d/%y', '%EX': '%H:%M:%S', '%Ey': '%y', '%EY': '%Y', '%Od': '%d', '%Oe': '%e', '%OH': '%H', '%OI': '%I', '%Om': '%m', '%OM': '%M', '%OS': '%S', '%Ou': '%u', '%OU': '%U', '%OV': '%V', '%Ow': '%w', '%OW': '%W', '%Oy': '%y', } for (var rule in EXPANSION_RULES_1) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_1[rule] ) } var WEEKDAYS = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', ] var MONTHS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ] function leadingSomething(value, digits, character) { var str = typeof value == 'number' ? value.toString() : value || '' while (str.length < digits) { str = character[0] + str } return str } function leadingNulls(value, digits) { return leadingSomething(value, digits, '0') } function compareByDay(date1, date2) { function sgn(value) { return value < 0 ? -1 : value > 0 ? 1 : 0 } var compare if ((compare = sgn(date1.getFullYear() - date2.getFullYear())) === 0) { if ((compare = sgn(date1.getMonth() - date2.getMonth())) === 0) { compare = sgn(date1.getDate() - date2.getDate()) } } return compare } function getFirstWeekStartDate(janFourth) { switch (janFourth.getDay()) { case 0: return new Date(janFourth.getFullYear() - 1, 11, 29) case 1: return janFourth case 2: return new Date(janFourth.getFullYear(), 0, 3) case 3: return new Date(janFourth.getFullYear(), 0, 2) case 4: return new Date(janFourth.getFullYear(), 0, 1) case 5: return new Date(janFourth.getFullYear() - 1, 11, 31) case 6: return new Date(janFourth.getFullYear() - 1, 11, 30) } } function getWeekBasedYear(date) { var thisDate = __addDays( new Date(date.tm_year + 1900, 0, 1), date.tm_yday ) var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4) var janFourthNextYear = new Date(thisDate.getFullYear() + 1, 0, 4) var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear) var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear) if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { return thisDate.getFullYear() + 1 } return thisDate.getFullYear() } return thisDate.getFullYear() - 1 } var EXPANSION_RULES_2 = { '%a': function(date) { return WEEKDAYS[date.tm_wday].substring(0, 3) }, '%A': function(date) { return WEEKDAYS[date.tm_wday] }, '%b': function(date) { return MONTHS[date.tm_mon].substring(0, 3) }, '%B': function(date) { return MONTHS[date.tm_mon] }, '%C': function(date) { var year = date.tm_year + 1900 return leadingNulls((year / 100) | 0, 2) }, '%d': function(date) { return leadingNulls(date.tm_mday, 2) }, '%e': function(date) { return leadingSomething(date.tm_mday, 2, ' ') }, '%g': function(date) { return getWeekBasedYear(date) .toString() .substring(2) }, '%G': function(date) { return getWeekBasedYear(date) }, '%H': function(date) { return leadingNulls(date.tm_hour, 2) }, '%I': function(date) { var twelveHour = date.tm_hour if (twelveHour == 0) twelveHour = 12 else if (twelveHour > 12) twelveHour -= 12 return leadingNulls(twelveHour, 2) }, '%j': function(date) { return leadingNulls( date.tm_mday + __arraySum( __isLeapYear(date.tm_year + 1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon - 1 ), 3 ) }, '%m': function(date) { return leadingNulls(date.tm_mon + 1, 2) }, '%M': function(date) { return leadingNulls(date.tm_min, 2) }, '%n': function() { return '\n' }, '%p': function(date) { if (date.tm_hour >= 0 && date.tm_hour < 12) { return 'AM' } return 'PM' }, '%S': function(date) { return leadingNulls(date.tm_sec, 2) }, '%t': function() { return '\t' }, '%u': function(date) { return date.tm_wday || 7 }, '%U': function(date) { var days = date.tm_yday + 7 - date.tm_wday return leadingNulls(Math.floor(days / 7), 2) }, '%V': function(date) { var val = Math.floor( (date.tm_yday + 7 - ((date.tm_wday + 6) % 7)) / 7 ) if ((date.tm_wday + 371 - date.tm_yday - 2) % 7 <= 2) { val++ } if (!val) { val = 52 var dec31 = (date.tm_wday + 7 - date.tm_yday - 1) % 7 if ( dec31 == 4 || (dec31 == 5 && __isLeapYear((date.tm_year % 400) - 1)) ) { val++ } } else if (val == 53) { var jan1 = (date.tm_wday + 371 - date.tm_yday) % 7 if (jan1 != 4 && (jan1 != 3 || !__isLeapYear(date.tm_year))) val = 1 } return leadingNulls(val, 2) }, '%w': function(date) { return date.tm_wday }, '%W': function(date) { var days = date.tm_yday + 7 - ((date.tm_wday + 6) % 7) return leadingNulls(Math.floor(days / 7), 2) }, '%y': function(date) { return (date.tm_year + 1900).toString().substring(2) }, '%Y': function(date) { return date.tm_year + 1900 }, '%z': function(date) { var off = date.tm_gmtoff var ahead = off >= 0 off = Math.abs(off) / 60 off = (off / 60) * 100 + (off % 60) return (ahead ? '+' : '-') + String('0000' + off).slice(-4) }, '%Z': function(date) { return date.tm_zone }, '%%': function() { return '%' }, } pattern = pattern.replace(/%%/g, '\0\0') for (var rule in EXPANSION_RULES_2) { if (pattern.includes(rule)) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date) ) } } pattern = pattern.replace(/\0\0/g, '%') var bytes = intArrayFromString(pattern, false) if (bytes.length > maxsize) { return 0 } writeArrayToMemory(bytes, s) return bytes.length - 1 } function _strftime_l(s, maxsize, format, tm, loc) { return _strftime(s, maxsize, format, tm) } function handleException(e) { if (e instanceof ExitStatus || e == 'unwind') { return EXITSTATUS } quit_(1, e) } function allocateUTF8OnStack(str) { var size = lengthBytesUTF8(str) + 1 var ret = stackAlloc(size) stringToUTF8Array(str, HEAP8, ret, size) return ret } function getCFunc(ident) { var func = Module['_' + ident] return func } function ccall(ident, returnType, argTypes, args, opts) { var toC = { string: str => { var ret = 0 if (str !== null && str !== undefined && str !== 0) { var len = (str.length << 2) + 1 ret = stackAlloc(len) stringToUTF8(str, ret, len) } return ret }, array: arr => { var ret = stackAlloc(arr.length) writeArrayToMemory(arr, ret) return ret }, } function convertReturnValue(ret) { if (returnType === 'string') { return UTF8ToString(ret) } if (returnType === 'boolean') return Boolean(ret) return ret } var func = getCFunc(ident) var cArgs = [] var stack = 0 if (args) { for (var i = 0; i < args.length; i++) { var converter = toC[argTypes[i]] if (converter) { if (stack === 0) stack = stackSave() cArgs[i] = converter(args[i]) } else { cArgs[i] = args[i] } } } var ret = func.apply(null, cArgs) function onDone(ret) { if (stack !== 0) stackRestore(stack) return convertReturnValue(ret) } ret = onDone(ret) return ret } function cwrap(ident, returnType, argTypes, opts) { argTypes = argTypes || [] var numericArgs = argTypes.every( type => type === 'number' || type === 'boolean' ) var numericRet = returnType !== 'string' if (numericRet && numericArgs && !opts) { return getCFunc(ident) } return function() { return ccall(ident, returnType, argTypes, arguments, opts) } } function AsciiToString(ptr) { var str = '' while (1) { var ch = HEAPU8[ptr++ >> 0] if (!ch) return str str += String.fromCharCode(ch) } } var FSNode = function(parent, name, mode, rdev) { if (!parent) { parent = this } this.parent = parent this.mount = parent.mount this.mounted = null this.id = FS.nextInode++ this.name = name this.mode = mode this.node_ops = {} this.stream_ops = {} this.rdev = rdev } var readMode = 292 | 73 var writeMode = 146 Object.defineProperties(FSNode.prototype, { read: { get: function() { return (this.mode & readMode) === readMode }, set: function(val) { val ? (this.mode |= readMode) : (this.mode &= ~readMode) }, }, write: { get: function() { return (this.mode & writeMode) === writeMode }, set: function(val) { val ? (this.mode |= writeMode) : (this.mode &= ~writeMode) }, }, isFolder: { get: function() { return FS.isDir(this.mode) }, }, isDevice: { get: function() { return FS.isChrdev(this.mode) }, }, }) FS.FSNode = FSNode FS.staticInit() Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_unlink'] = FS.unlink Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice if (ENVIRONMENT_IS_NODE) { NODEFS.staticInit() } ERRNO_CODES = { EPERM: 63, ENOENT: 44, ESRCH: 71, EINTR: 27, EIO: 29, ENXIO: 60, E2BIG: 1, ENOEXEC: 45, EBADF: 8, ECHILD: 12, EAGAIN: 6, EWOULDBLOCK: 6, ENOMEM: 48, EACCES: 2, EFAULT: 21, ENOTBLK: 105, EBUSY: 10, EEXIST: 20, EXDEV: 75, ENODEV: 43, ENOTDIR: 54, EISDIR: 31, EINVAL: 28, ENFILE: 41, EMFILE: 33, ENOTTY: 59, ETXTBSY: 74, EFBIG: 22, ENOSPC: 51, ESPIPE: 70, EROFS: 69, EMLINK: 34, EPIPE: 64, EDOM: 18, ERANGE: 68, ENOMSG: 49, EIDRM: 24, ECHRNG: 106, EL2NSYNC: 156, EL3HLT: 107, EL3RST: 108, ELNRNG: 109, EUNATCH: 110, ENOCSI: 111, EL2HLT: 112, EDEADLK: 16, ENOLCK: 46, EBADE: 113, EBADR: 114, EXFULL: 115, ENOANO: 104, EBADRQC: 103, EBADSLT: 102, EDEADLOCK: 16, EBFONT: 101, ENOSTR: 100, ENODATA: 116, ETIME: 117, ENOSR: 118, ENONET: 119, ENOPKG: 120, EREMOTE: 121, ENOLINK: 47, EADV: 122, ESRMNT: 123, ECOMM: 124, EPROTO: 65, EMULTIHOP: 36, EDOTDOT: 125, EBADMSG: 9, ENOTUNIQ: 126, EBADFD: 127, EREMCHG: 128, ELIBACC: 129, ELIBBAD: 130, ELIBSCN: 131, ELIBMAX: 132, ELIBEXEC: 133, ENOSYS: 52, ENOTEMPTY: 55, ENAMETOOLONG: 37, ELOOP: 32, EOPNOTSUPP: 138, EPFNOSUPPORT: 139, ECONNRESET: 15, ENOBUFS: 42, EAFNOSUPPORT: 5, EPROTOTYPE: 67, ENOTSOCK: 57, ENOPROTOOPT: 50, ESHUTDOWN: 140, ECONNREFUSED: 14, EADDRINUSE: 3, ECONNABORTED: 13, ENETUNREACH: 40, ENETDOWN: 38, ETIMEDOUT: 73, EHOSTDOWN: 142, EHOSTUNREACH: 23, EINPROGRESS: 26, EALREADY: 7, EDESTADDRREQ: 17, EMSGSIZE: 35, EPROTONOSUPPORT: 66, ESOCKTNOSUPPORT: 137, EADDRNOTAVAIL: 4, ENETRESET: 39, EISCONN: 30, ENOTCONN: 53, ETOOMANYREFS: 141, EUSERS: 136, EDQUOT: 19, ESTALE: 72, ENOTSUP: 138, ENOMEDIUM: 148, EILSEQ: 25, EOVERFLOW: 61, ECANCELED: 11, ENOTRECOVERABLE: 56, EOWNERDEAD: 62, ESTRPIPE: 135, } var asmLibraryArg = { a: ___cxa_throw, d: ___syscall_fcntl64, r: ___syscall_getcwd, i: ___syscall_ioctl, j: ___syscall_openat, n: ___syscall_readlinkat, o: ___syscall_stat64, b: _abort, f: _emscripten_memcpy_big, m: _emscripten_resize_heap, p: _environ_get, q: _environ_sizes_get, c: _exit, e: _fd_close, h: _fd_read, k: _fd_seek, g: _fd_write, l: _strftime_l, } var asm = createWasm() var ___wasm_call_ctors = (Module['___wasm_call_ctors'] = function() { return (___wasm_call_ctors = Module['___wasm_call_ctors'] = Module['asm']['t']).apply(null, arguments) }) var _main = (Module['_main'] = function() { return (_main = Module['_main'] = Module['asm']['u']).apply( null, arguments ) }) var ___errno_location = (Module['___errno_location'] = function() { return (___errno_location = Module['___errno_location'] = Module['asm']['v']).apply(null, arguments) }) var _itk_wasm_input_array_alloc = (Module[ '_itk_wasm_input_array_alloc' ] = function() { return (_itk_wasm_input_array_alloc = Module[ '_itk_wasm_input_array_alloc' ] = Module['asm']['w']).apply(null, arguments) }) var _itk_wasm_input_json_alloc = (Module[ '_itk_wasm_input_json_alloc' ] = function() { return (_itk_wasm_input_json_alloc = Module[ '_itk_wasm_input_json_alloc' ] = Module['asm']['x']).apply(null, arguments) }) var _itk_wasm_output_json_address = (Module[ '_itk_wasm_output_json_address' ] = function() { return (_itk_wasm_output_json_address = Module[ '_itk_wasm_output_json_address' ] = Module['asm']['y']).apply(null, arguments) }) var _itk_wasm_output_json_size = (Module[ '_itk_wasm_output_json_size' ] = function() { return (_itk_wasm_output_json_size = Module[ '_itk_wasm_output_json_size' ] = Module['asm']['z']).apply(null, arguments) }) var _itk_wasm_output_array_address = (Module[ '_itk_wasm_output_array_address' ] = function() { return (_itk_wasm_output_array_address = Module[ '_itk_wasm_output_array_address' ] = Module['asm']['A']).apply(null, arguments) }) var _itk_wasm_output_array_size = (Module[ '_itk_wasm_output_array_size' ] = function() { return (_itk_wasm_output_array_size = Module[ '_itk_wasm_output_array_size' ] = Module['asm']['B']).apply(null, arguments) }) var _itk_wasm_free_all = (Module['_itk_wasm_free_all'] = function() { return (_itk_wasm_free_all = Module['_itk_wasm_free_all'] = Module['asm']['C']).apply(null, arguments) }) var stackSave = (Module['stackSave'] = function() { return (stackSave = Module['stackSave'] = Module['asm']['E']).apply( null, arguments ) }) var stackRestore = (Module['stackRestore'] = function() { return (stackRestore = Module['stackRestore'] = Module['asm']['F']).apply( null, arguments ) }) var stackAlloc = (Module['stackAlloc'] = function() { return (stackAlloc = Module['stackAlloc'] = Module['asm']['G']).apply( null, arguments ) }) var ___cxa_is_pointer_type = (Module[ '___cxa_is_pointer_type' ] = function() { return (___cxa_is_pointer_type = Module['___cxa_is_pointer_type'] = Module['asm']['H']).apply(null, arguments) }) Module['addRunDependency'] = addRunDependency Module['removeRunDependency'] = removeRunDependency Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice Module['FS_unlink'] = FS.unlink Module['callMain'] = callMain Module['ccall'] = ccall Module['cwrap'] = cwrap Module['AsciiToString'] = AsciiToString Module['writeArrayToMemory'] = writeArrayToMemory Module['writeAsciiToMemory'] = writeAsciiToMemory var calledRun dependenciesFulfilled = function runCaller() { if (!calledRun) run() if (!calledRun) dependenciesFulfilled = runCaller } function callMain(args) { var entryFunction = Module['_main'] args = args || [] args.unshift(thisProgram) var argc = args.length var argv = stackAlloc((argc + 1) * 4) var argv_ptr = argv >> 2 args.forEach(arg => { HEAP32[argv_ptr++] = allocateUTF8OnStack(arg) }) HEAP32[argv_ptr] = 0 try { var ret = entryFunction(argc, argv) exitJS(ret, true) return ret } catch (e) { return handleException(e) } } function run(args) { args = args || arguments_ if (runDependencies > 0) { return } preRun() if (runDependencies > 0) { return } function doRun() { if (calledRun) return calledRun = true Module['calledRun'] = true if (ABORT) return initRuntime() preMain() readyPromiseResolve(Module) if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized']() if (shouldRunNow) callMain(args) postRun() } if (Module['setStatus']) { Module['setStatus']('Running...') setTimeout(function() { setTimeout(function() { Module['setStatus']('') }, 1) doRun() }, 1) } else { doRun() } } if (Module['preInit']) { if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']] while (Module['preInit'].length > 0) { Module['preInit'].pop()() } } var shouldRunNow = false if (Module['noInitialRun']) shouldRunNow = false run() Module.mountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) if (FS.isDir(containingDir) || containingDir === '/') { return } var currentDir = '/' var splitContainingDir = containingDir.split(path.sep) for (var ii = 1; ii < splitContainingDir.length; ii++) { currentDir += splitContainingDir[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } FS.mount(NODEFS, { root: containingDir }, currentDir) return currentDir + path.basename(filePath) } Module.unmountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) FS.unmount(containingDir) } Module.fs_mkdirs = function(dirs) { var currentDir = '/' var splitDirs = dirs.split('/') for (var ii = 1; ii < splitDirs.length; ++ii) { currentDir += splitDirs[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } } Module.fs_readFile = function(path, opts) { return FS.readFile(path, opts) } Module.fs_writeFile = function(path, data, opts) { return FS.writeFile(path, data, opts) } Module.fs_unlink = function(path) { return FS.unlink(path) } Module.fs_open = function(path, flags, mode) { return FS.open(path, flags, mode) } Module.fs_stat = function(path) { return FS.stat(path) } Module.fs_read = function(stream, buffer, offset, length, position) { return FS.read(stream, buffer, offset, length, position) } Module.fs_close = function(stream) { return FS.close(stream) } return Downsample.ready } })() if (typeof exports === 'object' && typeof module === 'object') module.exports = Downsample else if (typeof define === 'function' && define['amd']) define([], function() { return Downsample }) else if (typeof exports === 'object') exports['Downsample'] = Downsample ================================================ FILE: src/IO/Downsample/emscripten-build/DownsampleLabelImage.js ================================================ var DownsampleLabelImage = (() => { var _scriptDir = import.meta.url return async function(DownsampleLabelImage) { DownsampleLabelImage = DownsampleLabelImage || {} var Module = typeof DownsampleLabelImage != 'undefined' ? DownsampleLabelImage : {} var readyPromiseResolve, readyPromiseReject Module['ready'] = new Promise(function(resolve, reject) { readyPromiseResolve = resolve readyPromiseReject = reject }) var mStdout = null var mStderr = null Module['resetModuleStdout'] = function() { mStdout = '' } Module['resetModuleStderr'] = function() { mStderr = '' } Module['print'] = function(text) { console.log(text) mStdout += text + '\n' } Module['printErr'] = function(text) { console.error(text) mStderr += text + '\n' } Module['getModuleStdout'] = function() { return mStdout } Module['getModuleStderr'] = function() { return mStderr } var moduleOverrides = Object.assign({}, Module) var arguments_ = [] var thisProgram = './this.program' var quit_ = (status, toThrow) => { throw toThrow } var ENVIRONMENT_IS_WEB = typeof window == 'object' var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function' var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string' var scriptDirectory = '' function locateFile(path) { if (Module['locateFile']) { return Module['locateFile'](path, scriptDirectory) } return scriptDirectory + path } var read_, readAsync, readBinary, setWindowTitle function logExceptionOnExit(e) { if (e instanceof ExitStatus) return let toLog = e err('exiting due to exception: ' + toLog) } if (ENVIRONMENT_IS_NODE) { const { createRequire: createRequire } = await import('module') var require = createRequire(import.meta.url) var fs = require('fs') var nodePath = require('path') if (ENVIRONMENT_IS_WORKER) { scriptDirectory = nodePath.dirname(scriptDirectory) + '/' } else { scriptDirectory = require('url').fileURLToPath( new URL('./', import.meta.url) ) } read_ = (filename, binary) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) return fs.readFileSync(filename, binary ? undefined : 'utf8') } readBinary = filename => { var ret = read_(filename, true) if (!ret.buffer) { ret = new Uint8Array(ret) } return ret } readAsync = (filename, onload, onerror) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) fs.readFile(filename, function(err, data) { if (err) onerror(err) else onload(data.buffer) }) } if (process['argv'].length > 1) { thisProgram = process['argv'][1].replace(/\\/g, '/') } arguments_ = process['argv'].slice(2) process['on']('uncaughtException', function(ex) { if (!(ex instanceof ExitStatus)) { throw ex } }) process['on']('unhandledRejection', function(reason) { throw reason }) quit_ = (status, toThrow) => { if (keepRuntimeAlive()) { process['exitCode'] = status throw toThrow } logExceptionOnExit(toThrow) process['exit'](status) } Module['inspect'] = function() { return '[Emscripten Module object]' } } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = self.location.href } else if (typeof document != 'undefined' && document.currentScript) { scriptDirectory = document.currentScript.src } if (_scriptDir) { scriptDirectory = _scriptDir } if (scriptDirectory.indexOf('blob:') !== 0) { scriptDirectory = scriptDirectory.substr( 0, scriptDirectory.replace(/[?#].*/, '').lastIndexOf('/') + 1 ) } else { scriptDirectory = '' } { read_ = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.send(null) return xhr.responseText } if (ENVIRONMENT_IS_WORKER) { readBinary = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.responseType = 'arraybuffer' xhr.send(null) return new Uint8Array(xhr.response) } } readAsync = (url, onload, onerror) => { var xhr = new XMLHttpRequest() xhr.open('GET', url, true) xhr.responseType = 'arraybuffer' xhr.onload = () => { if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { onload(xhr.response) return } onerror() } xhr.onerror = onerror xhr.send(null) } } setWindowTitle = title => (document.title = title) } else { } var out = Module['print'] || console.log.bind(console) var err = Module['printErr'] || console.warn.bind(console) Object.assign(Module, moduleOverrides) moduleOverrides = null if (Module['arguments']) arguments_ = Module['arguments'] if (Module['thisProgram']) thisProgram = Module['thisProgram'] if (Module['quit']) quit_ = Module['quit'] var wasmBinary if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'] var noExitRuntime = Module['noExitRuntime'] || true if (typeof WebAssembly != 'object') { abort('no native wasm support detected') } var wasmMemory var ABORT = false var EXITSTATUS function assert(condition, text) { if (!condition) { abort(text) } } var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { var endIdx = idx + maxBytesToRead var endPtr = idx while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)) } var str = '' while (idx < endPtr) { var u0 = heapOrArray[idx++] if (!(u0 & 128)) { str += String.fromCharCode(u0) continue } var u1 = heapOrArray[idx++] & 63 if ((u0 & 224) == 192) { str += String.fromCharCode(((u0 & 31) << 6) | u1) continue } var u2 = heapOrArray[idx++] & 63 if ((u0 & 240) == 224) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2 } else { u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63) } if (u0 < 65536) { str += String.fromCharCode(u0) } else { var ch = u0 - 65536 str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)) } } return str } function UTF8ToString(ptr, maxBytesToRead) { return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '' } function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { if (!(maxBytesToWrite > 0)) return 0 var startIdx = outIdx var endIdx = outIdx + maxBytesToWrite - 1 for (var i = 0; i < str.length; ++i) { var u = str.charCodeAt(i) if (u >= 55296 && u <= 57343) { var u1 = str.charCodeAt(++i) u = (65536 + ((u & 1023) << 10)) | (u1 & 1023) } if (u <= 127) { if (outIdx >= endIdx) break heap[outIdx++] = u } else if (u <= 2047) { if (outIdx + 1 >= endIdx) break heap[outIdx++] = 192 | (u >> 6) heap[outIdx++] = 128 | (u & 63) } else if (u <= 65535) { if (outIdx + 2 >= endIdx) break heap[outIdx++] = 224 | (u >> 12) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } else { if (outIdx + 3 >= endIdx) break heap[outIdx++] = 240 | (u >> 18) heap[outIdx++] = 128 | ((u >> 12) & 63) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } } heap[outIdx] = 0 return outIdx - startIdx } function stringToUTF8(str, outPtr, maxBytesToWrite) { return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite) } function lengthBytesUTF8(str) { var len = 0 for (var i = 0; i < str.length; ++i) { var c = str.charCodeAt(i) if (c <= 127) { len++ } else if (c <= 2047) { len += 2 } else if (c >= 55296 && c <= 57343) { len += 4 ++i } else { len += 3 } } return len } var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64 function updateGlobalBufferAndViews(buf) { buffer = buf Module['HEAP8'] = HEAP8 = new Int8Array(buf) Module['HEAP16'] = HEAP16 = new Int16Array(buf) Module['HEAP32'] = HEAP32 = new Int32Array(buf) Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf) Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf) Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf) Module['HEAPF32'] = HEAPF32 = new Float32Array(buf) Module['HEAPF64'] = HEAPF64 = new Float64Array(buf) } var INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 16777216 var wasmTable var __ATPRERUN__ = [] var __ATINIT__ = [] var __ATMAIN__ = [] var __ATPOSTRUN__ = [] var runtimeInitialized = false function keepRuntimeAlive() { return noExitRuntime } function preRun() { if (Module['preRun']) { if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']] while (Module['preRun'].length) { addOnPreRun(Module['preRun'].shift()) } } callRuntimeCallbacks(__ATPRERUN__) } function initRuntime() { runtimeInitialized = true if (!Module['noFSInit'] && !FS.init.initialized) FS.init() FS.ignorePermissions = false TTY.init() callRuntimeCallbacks(__ATINIT__) } function preMain() { callRuntimeCallbacks(__ATMAIN__) } function postRun() { if (Module['postRun']) { if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']] while (Module['postRun'].length) { addOnPostRun(Module['postRun'].shift()) } } callRuntimeCallbacks(__ATPOSTRUN__) } function addOnPreRun(cb) { __ATPRERUN__.unshift(cb) } function addOnInit(cb) { __ATINIT__.unshift(cb) } function addOnPostRun(cb) { __ATPOSTRUN__.unshift(cb) } var runDependencies = 0 var runDependencyWatcher = null var dependenciesFulfilled = null function getUniqueRunDependency(id) { return id } function addRunDependency(id) { runDependencies++ if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } } function removeRunDependency(id) { runDependencies-- if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } if (runDependencies == 0) { if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher) runDependencyWatcher = null } if (dependenciesFulfilled) { var callback = dependenciesFulfilled dependenciesFulfilled = null callback() } } } function abort(what) { if (Module['onAbort']) { Module['onAbort'](what) } what = 'Aborted(' + what + ')' err(what) ABORT = true EXITSTATUS = 1 what += '. Build with -sASSERTIONS for more info.' var e = new WebAssembly.RuntimeError(what) readyPromiseReject(e) throw e } var dataURIPrefix = 'data:application/octet-stream;base64,' function isDataURI(filename) { return filename.startsWith(dataURIPrefix) } function isFileURI(filename) { return filename.startsWith('file://') } var wasmBinaryFile if (Module['locateFile']) { wasmBinaryFile = 'DownsampleLabelImage.wasm' if (!isDataURI(wasmBinaryFile)) { wasmBinaryFile = locateFile(wasmBinaryFile) } } else { wasmBinaryFile = new URL('DownsampleLabelImage.wasm', import.meta.url) .href } function getBinary(file) { try { if (file == wasmBinaryFile && wasmBinary) { return new Uint8Array(wasmBinary) } if (readBinary) { return readBinary(file) } throw 'both async and sync fetching of the wasm failed' } catch (err) { abort(err) } } function getBinaryPromise() { if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { if (typeof fetch == 'function' && !isFileURI(wasmBinaryFile)) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }) .then(function(response) { if (!response['ok']) { throw "failed to load wasm binary file at '" + wasmBinaryFile + "'" } return response['arrayBuffer']() }) .catch(function() { return getBinary(wasmBinaryFile) }) } else { if (readAsync) { return new Promise(function(resolve, reject) { readAsync( wasmBinaryFile, function(response) { resolve(new Uint8Array(response)) }, reject ) }) } } } return Promise.resolve().then(function() { return getBinary(wasmBinaryFile) }) } function createWasm() { var info = { a: asmLibraryArg } function receiveInstance(instance, module) { var exports = instance.exports Module['asm'] = exports wasmMemory = Module['asm']['s'] updateGlobalBufferAndViews(wasmMemory.buffer) wasmTable = Module['asm']['D'] addOnInit(Module['asm']['t']) removeRunDependency('wasm-instantiate') } addRunDependency('wasm-instantiate') function receiveInstantiationResult(result) { receiveInstance(result['instance']) } function instantiateArrayBuffer(receiver) { return getBinaryPromise() .then(function(binary) { return WebAssembly.instantiate(binary, info) }) .then(function(instance) { return instance }) .then(receiver, function(reason) { err('failed to asynchronously prepare wasm: ' + reason) abort(reason) }) } function instantiateAsync() { if ( !wasmBinary && typeof WebAssembly.instantiateStreaming == 'function' && !isDataURI(wasmBinaryFile) && !isFileURI(wasmBinaryFile) && !ENVIRONMENT_IS_NODE && typeof fetch == 'function' ) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then( function(response) { var result = WebAssembly.instantiateStreaming(response, info) return result.then(receiveInstantiationResult, function(reason) { err('wasm streaming compile failed: ' + reason) err('falling back to ArrayBuffer instantiation') return instantiateArrayBuffer(receiveInstantiationResult) }) } ) } else { return instantiateArrayBuffer(receiveInstantiationResult) } } if (Module['instantiateWasm']) { try { var exports = Module['instantiateWasm'](info, receiveInstance) return exports } catch (e) { err('Module.instantiateWasm callback failed with error: ' + e) readyPromiseReject(e) } } instantiateAsync().catch(readyPromiseReject) return {} } var tempDouble var tempI64 function ExitStatus(status) { this.name = 'ExitStatus' this.message = 'Program terminated with exit(' + status + ')' this.status = status } function callRuntimeCallbacks(callbacks) { while (callbacks.length > 0) { callbacks.shift()(Module) } } function ExceptionInfo(excPtr) { this.excPtr = excPtr this.ptr = excPtr - 24 this.set_type = function(type) { HEAPU32[(this.ptr + 4) >> 2] = type } this.get_type = function() { return HEAPU32[(this.ptr + 4) >> 2] } this.set_destructor = function(destructor) { HEAPU32[(this.ptr + 8) >> 2] = destructor } this.get_destructor = function() { return HEAPU32[(this.ptr + 8) >> 2] } this.set_refcount = function(refcount) { HEAP32[this.ptr >> 2] = refcount } this.set_caught = function(caught) { caught = caught ? 1 : 0 HEAP8[(this.ptr + 12) >> 0] = caught } this.get_caught = function() { return HEAP8[(this.ptr + 12) >> 0] != 0 } this.set_rethrown = function(rethrown) { rethrown = rethrown ? 1 : 0 HEAP8[(this.ptr + 13) >> 0] = rethrown } this.get_rethrown = function() { return HEAP8[(this.ptr + 13) >> 0] != 0 } this.init = function(type, destructor) { this.set_adjusted_ptr(0) this.set_type(type) this.set_destructor(destructor) this.set_refcount(0) this.set_caught(false) this.set_rethrown(false) } this.add_ref = function() { var value = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = value + 1 } this.release_ref = function() { var prev = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = prev - 1 return prev === 1 } this.set_adjusted_ptr = function(adjustedPtr) { HEAPU32[(this.ptr + 16) >> 2] = adjustedPtr } this.get_adjusted_ptr = function() { return HEAPU32[(this.ptr + 16) >> 2] } this.get_exception_ptr = function() { var isPointer = ___cxa_is_pointer_type(this.get_type()) if (isPointer) { return HEAPU32[this.excPtr >> 2] } var adjusted = this.get_adjusted_ptr() if (adjusted !== 0) return adjusted return this.excPtr } } var exceptionLast = 0 var uncaughtExceptionCount = 0 function ___cxa_throw(ptr, type, destructor) { var info = new ExceptionInfo(ptr) info.init(type, destructor) exceptionLast = ptr uncaughtExceptionCount++ throw ptr } function setErrNo(value) { HEAP32[___errno_location() >> 2] = value return value } var PATH = { isAbs: path => path.charAt(0) === '/', splitPath: filename => { var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/ return splitPathRe.exec(filename).slice(1) }, normalizeArray: (parts, allowAboveRoot) => { var up = 0 for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i] if (last === '.') { parts.splice(i, 1) } else if (last === '..') { parts.splice(i, 1) up++ } else if (up) { parts.splice(i, 1) up-- } } if (allowAboveRoot) { for (; up; up--) { parts.unshift('..') } } return parts }, normalize: path => { var isAbsolute = PATH.isAbs(path), trailingSlash = path.substr(-1) === '/' path = PATH.normalizeArray( path.split('/').filter(p => !!p), !isAbsolute ).join('/') if (!path && !isAbsolute) { path = '.' } if (path && trailingSlash) { path += '/' } return (isAbsolute ? '/' : '') + path }, dirname: path => { var result = PATH.splitPath(path), root = result[0], dir = result[1] if (!root && !dir) { return '.' } if (dir) { dir = dir.substr(0, dir.length - 1) } return root + dir }, basename: path => { if (path === '/') return '/' path = PATH.normalize(path) path = path.replace(/\/$/, '') var lastSlash = path.lastIndexOf('/') if (lastSlash === -1) return path return path.substr(lastSlash + 1) }, join: function() { var paths = Array.prototype.slice.call(arguments) return PATH.normalize(paths.join('/')) }, join2: (l, r) => { return PATH.normalize(l + '/' + r) }, } function getRandomDevice() { if ( typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function' ) { var randomBuffer = new Uint8Array(1) return () => { crypto.getRandomValues(randomBuffer) return randomBuffer[0] } } else if (ENVIRONMENT_IS_NODE) { try { var crypto_module = require('crypto') return () => crypto_module['randomBytes'](1)[0] } catch (e) {} } return () => abort('randomDevice') } var PATH_FS = { resolve: function() { var resolvedPath = '', resolvedAbsolute = false for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = i >= 0 ? arguments[i] : FS.cwd() if (typeof path != 'string') { throw new TypeError('Arguments to path.resolve must be strings') } else if (!path) { return '' } resolvedPath = path + '/' + resolvedPath resolvedAbsolute = PATH.isAbs(path) } resolvedPath = PATH.normalizeArray( resolvedPath.split('/').filter(p => !!p), !resolvedAbsolute ).join('/') return (resolvedAbsolute ? '/' : '') + resolvedPath || '.' }, relative: (from, to) => { from = PATH_FS.resolve(from).substr(1) to = PATH_FS.resolve(to).substr(1) function trim(arr) { var start = 0 for (; start < arr.length; start++) { if (arr[start] !== '') break } var end = arr.length - 1 for (; end >= 0; end--) { if (arr[end] !== '') break } if (start > end) return [] return arr.slice(start, end - start + 1) } var fromParts = trim(from.split('/')) var toParts = trim(to.split('/')) var length = Math.min(fromParts.length, toParts.length) var samePartsLength = length for (var i = 0; i < length; i++) { if (fromParts[i] !== toParts[i]) { samePartsLength = i break } } var outputParts = [] for (var i = samePartsLength; i < fromParts.length; i++) { outputParts.push('..') } outputParts = outputParts.concat(toParts.slice(samePartsLength)) return outputParts.join('/') }, } function intArrayFromString(stringy, dontAddNull, length) { var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1 var u8array = new Array(len) var numBytesWritten = stringToUTF8Array( stringy, u8array, 0, u8array.length ) if (dontAddNull) u8array.length = numBytesWritten return u8array } var TTY = { ttys: [], init: function() {}, shutdown: function() {}, register: function(dev, ops) { TTY.ttys[dev] = { input: [], output: [], ops: ops } FS.registerDevice(dev, TTY.stream_ops) }, stream_ops: { open: function(stream) { var tty = TTY.ttys[stream.node.rdev] if (!tty) { throw new FS.ErrnoError(43) } stream.tty = tty stream.seekable = false }, close: function(stream) { stream.tty.ops.fsync(stream.tty) }, fsync: function(stream) { stream.tty.ops.fsync(stream.tty) }, read: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.get_char) { throw new FS.ErrnoError(60) } var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = stream.tty.ops.get_char(stream.tty) } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.put_char) { throw new FS.ErrnoError(60) } try { for (var i = 0; i < length; i++) { stream.tty.ops.put_char(stream.tty, buffer[offset + i]) } } catch (e) { throw new FS.ErrnoError(29) } if (length) { stream.node.timestamp = Date.now() } return i }, }, default_tty_ops: { get_char: function(tty) { if (!tty.input.length) { var result = null if (ENVIRONMENT_IS_NODE) { var BUFSIZE = 256 var buf = Buffer.alloc(BUFSIZE) var bytesRead = 0 try { bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE, -1) } catch (e) { if (e.toString().includes('EOF')) bytesRead = 0 else throw e } if (bytesRead > 0) { result = buf.slice(0, bytesRead).toString('utf-8') } else { result = null } } else if ( typeof window != 'undefined' && typeof window.prompt == 'function' ) { result = window.prompt('Input: ') if (result !== null) { result += '\n' } } else if (typeof readline == 'function') { result = readline() if (result !== null) { result += '\n' } } if (!result) { return null } tty.input = intArrayFromString(result, true) } return tty.input.shift() }, put_char: function(tty, val) { if (val === null || val === 10) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, default_tty1_ops: { put_char: function(tty, val) { if (val === null || val === 10) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, } function mmapAlloc(size) { abort() } var MEMFS = { ops_table: null, mount: function(mount) { return MEMFS.createNode(null, '/', 16384 | 511, 0) }, createNode: function(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { throw new FS.ErrnoError(63) } if (!MEMFS.ops_table) { MEMFS.ops_table = { dir: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, lookup: MEMFS.node_ops.lookup, mknod: MEMFS.node_ops.mknod, rename: MEMFS.node_ops.rename, unlink: MEMFS.node_ops.unlink, rmdir: MEMFS.node_ops.rmdir, readdir: MEMFS.node_ops.readdir, symlink: MEMFS.node_ops.symlink, }, stream: { llseek: MEMFS.stream_ops.llseek }, }, file: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: { llseek: MEMFS.stream_ops.llseek, read: MEMFS.stream_ops.read, write: MEMFS.stream_ops.write, allocate: MEMFS.stream_ops.allocate, mmap: MEMFS.stream_ops.mmap, msync: MEMFS.stream_ops.msync, }, }, link: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, readlink: MEMFS.node_ops.readlink, }, stream: {}, }, chrdev: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: FS.chrdev_stream_ops, }, } } var node = FS.createNode(parent, name, mode, dev) if (FS.isDir(node.mode)) { node.node_ops = MEMFS.ops_table.dir.node node.stream_ops = MEMFS.ops_table.dir.stream node.contents = {} } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node node.stream_ops = MEMFS.ops_table.file.stream node.usedBytes = 0 node.contents = null } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node node.stream_ops = MEMFS.ops_table.link.stream } else if (FS.isChrdev(node.mode)) { node.node_ops = MEMFS.ops_table.chrdev.node node.stream_ops = MEMFS.ops_table.chrdev.stream } node.timestamp = Date.now() if (parent) { parent.contents[name] = node parent.timestamp = node.timestamp } return node }, getFileDataAsTypedArray: function(node) { if (!node.contents) return new Uint8Array(0) if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes) return new Uint8Array(node.contents) }, expandFileStorage: function(node, newCapacity) { var prevCapacity = node.contents ? node.contents.length : 0 if (prevCapacity >= newCapacity) return var CAPACITY_DOUBLING_MAX = 1024 * 1024 newCapacity = Math.max( newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2 : 1.125)) >>> 0 ) if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256) var oldContents = node.contents node.contents = new Uint8Array(newCapacity) if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0) }, resizeFileStorage: function(node, newSize) { if (node.usedBytes == newSize) return if (newSize == 0) { node.contents = null node.usedBytes = 0 } else { var oldContents = node.contents node.contents = new Uint8Array(newSize) if (oldContents) { node.contents.set( oldContents.subarray(0, Math.min(newSize, node.usedBytes)) ) } node.usedBytes = newSize } }, node_ops: { getattr: function(node) { var attr = {} attr.dev = FS.isChrdev(node.mode) ? node.id : 1 attr.ino = node.id attr.mode = node.mode attr.nlink = 1 attr.uid = 0 attr.gid = 0 attr.rdev = node.rdev if (FS.isDir(node.mode)) { attr.size = 4096 } else if (FS.isFile(node.mode)) { attr.size = node.usedBytes } else if (FS.isLink(node.mode)) { attr.size = node.link.length } else { attr.size = 0 } attr.atime = new Date(node.timestamp) attr.mtime = new Date(node.timestamp) attr.ctime = new Date(node.timestamp) attr.blksize = 4096 attr.blocks = Math.ceil(attr.size / attr.blksize) return attr }, setattr: function(node, attr) { if (attr.mode !== undefined) { node.mode = attr.mode } if (attr.timestamp !== undefined) { node.timestamp = attr.timestamp } if (attr.size !== undefined) { MEMFS.resizeFileStorage(node, attr.size) } }, lookup: function(parent, name) { throw FS.genericErrors[44] }, mknod: function(parent, name, mode, dev) { return MEMFS.createNode(parent, name, mode, dev) }, rename: function(old_node, new_dir, new_name) { if (FS.isDir(old_node.mode)) { var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (new_node) { for (var i in new_node.contents) { throw new FS.ErrnoError(55) } } } delete old_node.parent.contents[old_node.name] old_node.parent.timestamp = Date.now() old_node.name = new_name new_dir.contents[new_name] = old_node new_dir.timestamp = old_node.parent.timestamp old_node.parent = new_dir }, unlink: function(parent, name) { delete parent.contents[name] parent.timestamp = Date.now() }, rmdir: function(parent, name) { var node = FS.lookupNode(parent, name) for (var i in node.contents) { throw new FS.ErrnoError(55) } delete parent.contents[name] parent.timestamp = Date.now() }, readdir: function(node) { var entries = ['.', '..'] for (var key in node.contents) { if (!node.contents.hasOwnProperty(key)) { continue } entries.push(key) } return entries }, symlink: function(parent, newname, oldpath) { var node = MEMFS.createNode(parent, newname, 511 | 40960, 0) node.link = oldpath return node }, readlink: function(node) { if (!FS.isLink(node.mode)) { throw new FS.ErrnoError(28) } return node.link }, }, stream_ops: { read: function(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= stream.node.usedBytes) return 0 var size = Math.min(stream.node.usedBytes - position, length) if (size > 8 && contents.subarray) { buffer.set(contents.subarray(position, position + size), offset) } else { for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i] } return size }, write: function(stream, buffer, offset, length, position, canOwn) { if (buffer.buffer === HEAP8.buffer) { canOwn = false } if (!length) return 0 var node = stream.node node.timestamp = Date.now() if (buffer.subarray && (!node.contents || node.contents.subarray)) { if (canOwn) { node.contents = buffer.subarray(offset, offset + length) node.usedBytes = length return length } else if (node.usedBytes === 0 && position === 0) { node.contents = buffer.slice(offset, offset + length) node.usedBytes = length return length } else if (position + length <= node.usedBytes) { node.contents.set( buffer.subarray(offset, offset + length), position ) return length } } MEMFS.expandFileStorage(node, position + length) if (node.contents.subarray && buffer.subarray) { node.contents.set( buffer.subarray(offset, offset + length), position ) } else { for (var i = 0; i < length; i++) { node.contents[position + i] = buffer[offset + i] } } node.usedBytes = Math.max(node.usedBytes, position + length) return length }, llseek: function(stream, offset, whence) { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { position += stream.node.usedBytes } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, allocate: function(stream, offset, length) { MEMFS.expandFileStorage(stream.node, offset + length) stream.node.usedBytes = Math.max( stream.node.usedBytes, offset + length ) }, mmap: function(stream, length, position, prot, flags) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr var allocated var contents = stream.node.contents if (!(flags & 2) && contents.buffer === buffer) { allocated = false ptr = contents.byteOffset } else { if (position > 0 || position + length < contents.length) { if (contents.subarray) { contents = contents.subarray(position, position + length) } else { contents = Array.prototype.slice.call( contents, position, position + length ) } } allocated = true ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } HEAP8.set(contents, ptr) } return { ptr: ptr, allocated: allocated } }, msync: function(stream, buffer, offset, length, mmapFlags) { MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } function asyncLoad(url, onload, onerror, noRunDep) { var dep = !noRunDep ? getUniqueRunDependency('al ' + url) : '' readAsync( url, arrayBuffer => { assert( arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).' ) onload(new Uint8Array(arrayBuffer)) if (dep) removeRunDependency(dep) }, event => { if (onerror) { onerror() } else { throw 'Loading data file "' + url + '" failed.' } } ) if (dep) addRunDependency(dep) } var ERRNO_CODES = {} var NODEFS = { isWindows: false, staticInit: () => { NODEFS.isWindows = !!process.platform.match(/^win/) var flags = process['binding']('constants') if (flags['fs']) { flags = flags['fs'] } NODEFS.flagsForNodeMap = { 1024: flags['O_APPEND'], 64: flags['O_CREAT'], 128: flags['O_EXCL'], 256: flags['O_NOCTTY'], 0: flags['O_RDONLY'], 2: flags['O_RDWR'], 4096: flags['O_SYNC'], 512: flags['O_TRUNC'], 1: flags['O_WRONLY'], 131072: flags['O_NOFOLLOW'], } }, convertNodeCode: e => { var code = e.code return ERRNO_CODES[code] }, mount: mount => { return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0) }, createNode: (parent, name, mode, dev) => { if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { throw new FS.ErrnoError(28) } var node = FS.createNode(parent, name, mode) node.node_ops = NODEFS.node_ops node.stream_ops = NODEFS.stream_ops return node }, getMode: path => { var stat try { stat = fs.lstatSync(path) if (NODEFS.isWindows) { stat.mode = stat.mode | ((stat.mode & 292) >> 2) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return stat.mode }, realPath: node => { var parts = [] while (node.parent !== node) { parts.push(node.name) node = node.parent } parts.push(node.mount.opts.root) parts.reverse() return PATH.join.apply(null, parts) }, flagsForNode: flags => { flags &= ~2097152 flags &= ~2048 flags &= ~32768 flags &= ~524288 flags &= ~65536 var newFlags = 0 for (var k in NODEFS.flagsForNodeMap) { if (flags & k) { newFlags |= NODEFS.flagsForNodeMap[k] flags ^= k } } if (flags) { throw new FS.ErrnoError(28) } return newFlags }, node_ops: { getattr: node => { var path = NODEFS.realPath(node) var stat try { stat = fs.lstatSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } if (NODEFS.isWindows && !stat.blksize) { stat.blksize = 4096 } if (NODEFS.isWindows && !stat.blocks) { stat.blocks = ((stat.size + stat.blksize - 1) / stat.blksize) | 0 } return { dev: stat.dev, ino: stat.ino, mode: stat.mode, nlink: stat.nlink, uid: stat.uid, gid: stat.gid, rdev: stat.rdev, size: stat.size, atime: stat.atime, mtime: stat.mtime, ctime: stat.ctime, blksize: stat.blksize, blocks: stat.blocks, } }, setattr: (node, attr) => { var path = NODEFS.realPath(node) try { if (attr.mode !== undefined) { fs.chmodSync(path, attr.mode) node.mode = attr.mode } if (attr.timestamp !== undefined) { var date = new Date(attr.timestamp) fs.utimesSync(path, date, date) } if (attr.size !== undefined) { fs.truncateSync(path, attr.size) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, lookup: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) var mode = NODEFS.getMode(path) return NODEFS.createNode(parent, name, mode) }, mknod: (parent, name, mode, dev) => { var node = NODEFS.createNode(parent, name, mode, dev) var path = NODEFS.realPath(node) try { if (FS.isDir(node.mode)) { fs.mkdirSync(path, node.mode) } else { fs.writeFileSync(path, '', { mode: node.mode }) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return node }, rename: (oldNode, newDir, newName) => { var oldPath = NODEFS.realPath(oldNode) var newPath = PATH.join2(NODEFS.realPath(newDir), newName) try { fs.renameSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } oldNode.name = newName }, unlink: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.unlinkSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, rmdir: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.rmdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readdir: node => { var path = NODEFS.realPath(node) try { return fs.readdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, symlink: (parent, newName, oldPath) => { var newPath = PATH.join2(NODEFS.realPath(parent), newName) try { fs.symlinkSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readlink: node => { var path = NODEFS.realPath(node) try { path = fs.readlinkSync(path) path = nodePath.relative( nodePath.resolve(node.mount.opts.root), path ) return path } catch (e) { if (!e.code) throw e if (e.code === 'UNKNOWN') throw new FS.ErrnoError(28) throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, }, stream_ops: { open: stream => { var path = NODEFS.realPath(stream.node) try { if (FS.isFile(stream.node.mode)) { stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags)) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, close: stream => { try { if (FS.isFile(stream.node.mode) && stream.nfd) { fs.closeSync(stream.nfd) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, read: (stream, buffer, offset, length, position) => { if (length === 0) return 0 try { return fs.readSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, write: (stream, buffer, offset, length, position) => { try { return fs.writeSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, llseek: (stream, offset, whence) => { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { try { var stat = fs.fstatSync(stream.nfd) position += stat.size } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, mmap: (stream, length, position, prot, flags) => { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr = mmapAlloc(length) NODEFS.stream_ops.read(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } }, msync: (stream, buffer, offset, length, mmapFlags) => { NODEFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } var FS = { root: null, mounts: [], devices: {}, streams: [], nextInode: 1, nameTable: null, currentPath: '/', initialized: false, ignorePermissions: true, ErrnoError: null, genericErrors: {}, filesystems: null, syncFSRequests: 0, lookupPath: (path, opts = {}) => { path = PATH_FS.resolve(path) if (!path) return { path: '', node: null } var defaults = { follow_mount: true, recurse_count: 0 } opts = Object.assign(defaults, opts) if (opts.recurse_count > 8) { throw new FS.ErrnoError(32) } var parts = path.split('/').filter(p => !!p) var current = FS.root var current_path = '/' for (var i = 0; i < parts.length; i++) { var islast = i === parts.length - 1 if (islast && opts.parent) { break } current = FS.lookupNode(current, parts[i]) current_path = PATH.join2(current_path, parts[i]) if (FS.isMountpoint(current)) { if (!islast || (islast && opts.follow_mount)) { current = current.mounted.root } } if (!islast || opts.follow) { var count = 0 while (FS.isLink(current.mode)) { var link = FS.readlink(current_path) current_path = PATH_FS.resolve(PATH.dirname(current_path), link) var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count + 1, }) current = lookup.node if (count++ > 40) { throw new FS.ErrnoError(32) } } } } return { path: current_path, node: current } }, getPath: node => { var path while (true) { if (FS.isRoot(node)) { var mount = node.mount.mountpoint if (!path) return mount return mount[mount.length - 1] !== '/' ? mount + '/' + path : mount + path } path = path ? node.name + '/' + path : node.name node = node.parent } }, hashName: (parentid, name) => { var hash = 0 for (var i = 0; i < name.length; i++) { hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0 } return ((parentid + hash) >>> 0) % FS.nameTable.length }, hashAddNode: node => { var hash = FS.hashName(node.parent.id, node.name) node.name_next = FS.nameTable[hash] FS.nameTable[hash] = node }, hashRemoveNode: node => { var hash = FS.hashName(node.parent.id, node.name) if (FS.nameTable[hash] === node) { FS.nameTable[hash] = node.name_next } else { var current = FS.nameTable[hash] while (current) { if (current.name_next === node) { current.name_next = node.name_next break } current = current.name_next } } }, lookupNode: (parent, name) => { var errCode = FS.mayLookup(parent) if (errCode) { throw new FS.ErrnoError(errCode, parent) } var hash = FS.hashName(parent.id, name) for (var node = FS.nameTable[hash]; node; node = node.name_next) { var nodeName = node.name if (node.parent.id === parent.id && nodeName === name) { return node } } return FS.lookup(parent, name) }, createNode: (parent, name, mode, rdev) => { var node = new FS.FSNode(parent, name, mode, rdev) FS.hashAddNode(node) return node }, destroyNode: node => { FS.hashRemoveNode(node) }, isRoot: node => { return node === node.parent }, isMountpoint: node => { return !!node.mounted }, isFile: mode => { return (mode & 61440) === 32768 }, isDir: mode => { return (mode & 61440) === 16384 }, isLink: mode => { return (mode & 61440) === 40960 }, isChrdev: mode => { return (mode & 61440) === 8192 }, isBlkdev: mode => { return (mode & 61440) === 24576 }, isFIFO: mode => { return (mode & 61440) === 4096 }, isSocket: mode => { return (mode & 49152) === 49152 }, flagModes: { r: 0, 'r+': 2, w: 577, 'w+': 578, a: 1089, 'a+': 1090 }, modeStringToFlags: str => { var flags = FS.flagModes[str] if (typeof flags == 'undefined') { throw new Error('Unknown file open mode: ' + str) } return flags }, flagsToPermissionString: flag => { var perms = ['r', 'w', 'rw'][flag & 3] if (flag & 512) { perms += 'w' } return perms }, nodePermissions: (node, perms) => { if (FS.ignorePermissions) { return 0 } if (perms.includes('r') && !(node.mode & 292)) { return 2 } else if (perms.includes('w') && !(node.mode & 146)) { return 2 } else if (perms.includes('x') && !(node.mode & 73)) { return 2 } return 0 }, mayLookup: dir => { var errCode = FS.nodePermissions(dir, 'x') if (errCode) return errCode if (!dir.node_ops.lookup) return 2 return 0 }, mayCreate: (dir, name) => { try { var node = FS.lookupNode(dir, name) return 20 } catch (e) {} return FS.nodePermissions(dir, 'wx') }, mayDelete: (dir, name, isdir) => { var node try { node = FS.lookupNode(dir, name) } catch (e) { return e.errno } var errCode = FS.nodePermissions(dir, 'wx') if (errCode) { return errCode } if (isdir) { if (!FS.isDir(node.mode)) { return 54 } if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return 10 } } else { if (FS.isDir(node.mode)) { return 31 } } return 0 }, mayOpen: (node, flags) => { if (!node) { return 44 } if (FS.isLink(node.mode)) { return 32 } else if (FS.isDir(node.mode)) { if (FS.flagsToPermissionString(flags) !== 'r' || flags & 512) { return 31 } } return FS.nodePermissions(node, FS.flagsToPermissionString(flags)) }, MAX_OPEN_FDS: 4096, nextfd: (fd_start = 0, fd_end = FS.MAX_OPEN_FDS) => { for (var fd = fd_start; fd <= fd_end; fd++) { if (!FS.streams[fd]) { return fd } } throw new FS.ErrnoError(33) }, getStream: fd => FS.streams[fd], createStream: (stream, fd_start, fd_end) => { if (!FS.FSStream) { FS.FSStream = function() { this.shared = {} } FS.FSStream.prototype = {} Object.defineProperties(FS.FSStream.prototype, { object: { get: function() { return this.node }, set: function(val) { this.node = val }, }, isRead: { get: function() { return (this.flags & 2097155) !== 1 }, }, isWrite: { get: function() { return (this.flags & 2097155) !== 0 }, }, isAppend: { get: function() { return this.flags & 1024 }, }, flags: { get: function() { return this.shared.flags }, set: function(val) { this.shared.flags = val }, }, position: { get: function() { return this.shared.position }, set: function(val) { this.shared.position = val }, }, }) } stream = Object.assign(new FS.FSStream(), stream) var fd = FS.nextfd(fd_start, fd_end) stream.fd = fd FS.streams[fd] = stream return stream }, closeStream: fd => { FS.streams[fd] = null }, chrdev_stream_ops: { open: stream => { var device = FS.getDevice(stream.node.rdev) stream.stream_ops = device.stream_ops if (stream.stream_ops.open) { stream.stream_ops.open(stream) } }, llseek: () => { throw new FS.ErrnoError(70) }, }, major: dev => dev >> 8, minor: dev => dev & 255, makedev: (ma, mi) => (ma << 8) | mi, registerDevice: (dev, ops) => { FS.devices[dev] = { stream_ops: ops } }, getDevice: dev => FS.devices[dev], getMounts: mount => { var mounts = [] var check = [mount] while (check.length) { var m = check.pop() mounts.push(m) check.push.apply(check, m.mounts) } return mounts }, syncfs: (populate, callback) => { if (typeof populate == 'function') { callback = populate populate = false } FS.syncFSRequests++ if (FS.syncFSRequests > 1) { err( 'warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work' ) } var mounts = FS.getMounts(FS.root.mount) var completed = 0 function doCallback(errCode) { FS.syncFSRequests-- return callback(errCode) } function done(errCode) { if (errCode) { if (!done.errored) { done.errored = true return doCallback(errCode) } return } if (++completed >= mounts.length) { doCallback(null) } } mounts.forEach(mount => { if (!mount.type.syncfs) { return done(null) } mount.type.syncfs(mount, populate, done) }) }, mount: (type, opts, mountpoint) => { var root = mountpoint === '/' var pseudo = !mountpoint var node if (root && FS.root) { throw new FS.ErrnoError(10) } else if (!root && !pseudo) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) mountpoint = lookup.path node = lookup.node if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } if (!FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } } var mount = { type: type, opts: opts, mountpoint: mountpoint, mounts: [], } var mountRoot = type.mount(mount) mountRoot.mount = mount mount.root = mountRoot if (root) { FS.root = mountRoot } else if (node) { node.mounted = mount if (node.mount) { node.mount.mounts.push(mount) } } return mountRoot }, unmount: mountpoint => { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) if (!FS.isMountpoint(lookup.node)) { throw new FS.ErrnoError(28) } var node = lookup.node var mount = node.mounted var mounts = FS.getMounts(mount) Object.keys(FS.nameTable).forEach(hash => { var current = FS.nameTable[hash] while (current) { var next = current.name_next if (mounts.includes(current.mount)) { FS.destroyNode(current) } current = next } }) node.mounted = null var idx = node.mount.mounts.indexOf(mount) node.mount.mounts.splice(idx, 1) }, lookup: (parent, name) => { return parent.node_ops.lookup(parent, name) }, mknod: (path, mode, dev) => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) if (!name || name === '.' || name === '..') { throw new FS.ErrnoError(28) } var errCode = FS.mayCreate(parent, name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.mknod) { throw new FS.ErrnoError(63) } return parent.node_ops.mknod(parent, name, mode, dev) }, create: (path, mode) => { mode = mode !== undefined ? mode : 438 mode &= 4095 mode |= 32768 return FS.mknod(path, mode, 0) }, mkdir: (path, mode) => { mode = mode !== undefined ? mode : 511 mode &= 511 | 512 mode |= 16384 return FS.mknod(path, mode, 0) }, mkdirTree: (path, mode) => { var dirs = path.split('/') var d = '' for (var i = 0; i < dirs.length; ++i) { if (!dirs[i]) continue d += '/' + dirs[i] try { FS.mkdir(d, mode) } catch (e) { if (e.errno != 20) throw e } } }, mkdev: (path, mode, dev) => { if (typeof dev == 'undefined') { dev = mode mode = 438 } mode |= 8192 return FS.mknod(path, mode, dev) }, symlink: (oldpath, newpath) => { if (!PATH_FS.resolve(oldpath)) { throw new FS.ErrnoError(44) } var lookup = FS.lookupPath(newpath, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var newname = PATH.basename(newpath) var errCode = FS.mayCreate(parent, newname) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.symlink) { throw new FS.ErrnoError(63) } return parent.node_ops.symlink(parent, newname, oldpath) }, rename: (old_path, new_path) => { var old_dirname = PATH.dirname(old_path) var new_dirname = PATH.dirname(new_path) var old_name = PATH.basename(old_path) var new_name = PATH.basename(new_path) var lookup, old_dir, new_dir lookup = FS.lookupPath(old_path, { parent: true }) old_dir = lookup.node lookup = FS.lookupPath(new_path, { parent: true }) new_dir = lookup.node if (!old_dir || !new_dir) throw new FS.ErrnoError(44) if (old_dir.mount !== new_dir.mount) { throw new FS.ErrnoError(75) } var old_node = FS.lookupNode(old_dir, old_name) var relative = PATH_FS.relative(old_path, new_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(28) } relative = PATH_FS.relative(new_path, old_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(55) } var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (old_node === new_node) { return } var isdir = FS.isDir(old_node.mode) var errCode = FS.mayDelete(old_dir, old_name, isdir) if (errCode) { throw new FS.ErrnoError(errCode) } errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!old_dir.node_ops.rename) { throw new FS.ErrnoError(63) } if ( FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node)) ) { throw new FS.ErrnoError(10) } if (new_dir !== old_dir) { errCode = FS.nodePermissions(old_dir, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } } FS.hashRemoveNode(old_node) try { old_dir.node_ops.rename(old_node, new_dir, new_name) } catch (e) { throw e } finally { FS.hashAddNode(old_node) } }, rmdir: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, true) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.rmdir) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.rmdir(parent, name) FS.destroyNode(node) }, readdir: path => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node if (!node.node_ops.readdir) { throw new FS.ErrnoError(54) } return node.node_ops.readdir(node) }, unlink: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, false) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.unlink) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.unlink(parent, name) FS.destroyNode(node) }, readlink: path => { var lookup = FS.lookupPath(path) var link = lookup.node if (!link) { throw new FS.ErrnoError(44) } if (!link.node_ops.readlink) { throw new FS.ErrnoError(28) } return PATH_FS.resolve( FS.getPath(link.parent), link.node_ops.readlink(link) ) }, stat: (path, dontFollow) => { var lookup = FS.lookupPath(path, { follow: !dontFollow }) var node = lookup.node if (!node) { throw new FS.ErrnoError(44) } if (!node.node_ops.getattr) { throw new FS.ErrnoError(63) } return node.node_ops.getattr(node) }, lstat: path => { return FS.stat(path, true) }, chmod: (path, mode, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { mode: (mode & 4095) | (node.mode & ~4095), timestamp: Date.now(), }) }, lchmod: (path, mode) => { FS.chmod(path, mode, true) }, fchmod: (fd, mode) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chmod(stream.node, mode) }, chown: (path, uid, gid, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { timestamp: Date.now() }) }, lchown: (path, uid, gid) => { FS.chown(path, uid, gid, true) }, fchown: (fd, uid, gid) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chown(stream.node, uid, gid) }, truncate: (path, len) => { if (len < 0) { throw new FS.ErrnoError(28) } var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: true }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } if (FS.isDir(node.mode)) { throw new FS.ErrnoError(31) } if (!FS.isFile(node.mode)) { throw new FS.ErrnoError(28) } var errCode = FS.nodePermissions(node, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } node.node_ops.setattr(node, { size: len, timestamp: Date.now() }) }, ftruncate: (fd, len) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(28) } FS.truncate(stream.node, len) }, utime: (path, atime, mtime) => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node node.node_ops.setattr(node, { timestamp: Math.max(atime, mtime) }) }, open: (path, flags, mode) => { if (path === '') { throw new FS.ErrnoError(44) } flags = typeof flags == 'string' ? FS.modeStringToFlags(flags) : flags mode = typeof mode == 'undefined' ? 438 : mode if (flags & 64) { mode = (mode & 4095) | 32768 } else { mode = 0 } var node if (typeof path == 'object') { node = path } else { path = PATH.normalize(path) try { var lookup = FS.lookupPath(path, { follow: !(flags & 131072) }) node = lookup.node } catch (e) {} } var created = false if (flags & 64) { if (node) { if (flags & 128) { throw new FS.ErrnoError(20) } } else { node = FS.mknod(path, mode, 0) created = true } } if (!node) { throw new FS.ErrnoError(44) } if (FS.isChrdev(node.mode)) { flags &= ~512 } if (flags & 65536 && !FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } if (!created) { var errCode = FS.mayOpen(node, flags) if (errCode) { throw new FS.ErrnoError(errCode) } } if (flags & 512 && !created) { FS.truncate(node, 0) } flags &= ~(128 | 512 | 131072) var stream = FS.createStream({ node: node, path: FS.getPath(node), flags: flags, seekable: true, position: 0, stream_ops: node.stream_ops, ungotten: [], error: false, }) if (stream.stream_ops.open) { stream.stream_ops.open(stream) } if (Module['logReadFiles'] && !(flags & 1)) { if (!FS.readFiles) FS.readFiles = {} if (!(path in FS.readFiles)) { FS.readFiles[path] = 1 } } return stream }, close: stream => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (stream.getdents) stream.getdents = null try { if (stream.stream_ops.close) { stream.stream_ops.close(stream) } } catch (e) { throw e } finally { FS.closeStream(stream.fd) } stream.fd = null }, isClosed: stream => { return stream.fd === null }, llseek: (stream, offset, whence) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (!stream.seekable || !stream.stream_ops.llseek) { throw new FS.ErrnoError(70) } if (whence != 0 && whence != 1 && whence != 2) { throw new FS.ErrnoError(28) } stream.position = stream.stream_ops.llseek(stream, offset, whence) stream.ungotten = [] return stream.position }, read: (stream, buffer, offset, length, position) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.read) { throw new FS.ErrnoError(28) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesRead = stream.stream_ops.read( stream, buffer, offset, length, position ) if (!seeking) stream.position += bytesRead return bytesRead }, write: (stream, buffer, offset, length, position, canOwn) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.write) { throw new FS.ErrnoError(28) } if (stream.seekable && stream.flags & 1024) { FS.llseek(stream, 0, 2) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesWritten = stream.stream_ops.write( stream, buffer, offset, length, position, canOwn ) if (!seeking) stream.position += bytesWritten return bytesWritten }, allocate: (stream, offset, length) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (offset < 0 || length <= 0) { throw new FS.ErrnoError(28) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(43) } if (!stream.stream_ops.allocate) { throw new FS.ErrnoError(138) } stream.stream_ops.allocate(stream, offset, length) }, mmap: (stream, length, position, prot, flags) => { if ( (prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2 ) { throw new FS.ErrnoError(2) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(2) } if (!stream.stream_ops.mmap) { throw new FS.ErrnoError(43) } return stream.stream_ops.mmap(stream, length, position, prot, flags) }, msync: (stream, buffer, offset, length, mmapFlags) => { if (!stream.stream_ops.msync) { return 0 } return stream.stream_ops.msync( stream, buffer, offset, length, mmapFlags ) }, munmap: stream => 0, ioctl: (stream, cmd, arg) => { if (!stream.stream_ops.ioctl) { throw new FS.ErrnoError(59) } return stream.stream_ops.ioctl(stream, cmd, arg) }, readFile: (path, opts = {}) => { opts.flags = opts.flags || 0 opts.encoding = opts.encoding || 'binary' if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { throw new Error('Invalid encoding type "' + opts.encoding + '"') } var ret var stream = FS.open(path, opts.flags) var stat = FS.stat(path) var length = stat.size var buf = new Uint8Array(length) FS.read(stream, buf, 0, length, 0) if (opts.encoding === 'utf8') { ret = UTF8ArrayToString(buf, 0) } else if (opts.encoding === 'binary') { ret = buf } FS.close(stream) return ret }, writeFile: (path, data, opts = {}) => { opts.flags = opts.flags || 577 var stream = FS.open(path, opts.flags, opts.mode) if (typeof data == 'string') { var buf = new Uint8Array(lengthBytesUTF8(data) + 1) var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length) FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn) } else if (ArrayBuffer.isView(data)) { FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn) } else { throw new Error('Unsupported data type') } FS.close(stream) }, cwd: () => FS.currentPath, chdir: path => { var lookup = FS.lookupPath(path, { follow: true }) if (lookup.node === null) { throw new FS.ErrnoError(44) } if (!FS.isDir(lookup.node.mode)) { throw new FS.ErrnoError(54) } var errCode = FS.nodePermissions(lookup.node, 'x') if (errCode) { throw new FS.ErrnoError(errCode) } FS.currentPath = lookup.path }, createDefaultDirectories: () => { FS.mkdir('/tmp') FS.mkdir('/home') FS.mkdir('/home/web_user') }, createDefaultDevices: () => { FS.mkdir('/dev') FS.registerDevice(FS.makedev(1, 3), { read: () => 0, write: (stream, buffer, offset, length, pos) => length, }) FS.mkdev('/dev/null', FS.makedev(1, 3)) TTY.register(FS.makedev(5, 0), TTY.default_tty_ops) TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops) FS.mkdev('/dev/tty', FS.makedev(5, 0)) FS.mkdev('/dev/tty1', FS.makedev(6, 0)) var random_device = getRandomDevice() FS.createDevice('/dev', 'random', random_device) FS.createDevice('/dev', 'urandom', random_device) FS.mkdir('/dev/shm') FS.mkdir('/dev/shm/tmp') }, createSpecialDirectories: () => { FS.mkdir('/proc') var proc_self = FS.mkdir('/proc/self') FS.mkdir('/proc/self/fd') FS.mount( { mount: () => { var node = FS.createNode(proc_self, 'fd', 16384 | 511, 73) node.node_ops = { lookup: (parent, name) => { var fd = +name var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) var ret = { parent: null, mount: { mountpoint: 'fake' }, node_ops: { readlink: () => stream.path }, } ret.parent = ret return ret }, } return node }, }, {}, '/proc/self/fd' ) }, createStandardStreams: () => { if (Module['stdin']) { FS.createDevice('/dev', 'stdin', Module['stdin']) } else { FS.symlink('/dev/tty', '/dev/stdin') } if (Module['stdout']) { FS.createDevice('/dev', 'stdout', null, Module['stdout']) } else { FS.symlink('/dev/tty', '/dev/stdout') } if (Module['stderr']) { FS.createDevice('/dev', 'stderr', null, Module['stderr']) } else { FS.symlink('/dev/tty1', '/dev/stderr') } var stdin = FS.open('/dev/stdin', 0) var stdout = FS.open('/dev/stdout', 1) var stderr = FS.open('/dev/stderr', 1) }, ensureErrnoError: () => { if (FS.ErrnoError) return FS.ErrnoError = function ErrnoError(errno, node) { this.node = node this.setErrno = function(errno) { this.errno = errno } this.setErrno(errno) this.message = 'FS error' } FS.ErrnoError.prototype = new Error() FS.ErrnoError.prototype.constructor = FS.ErrnoError ;[44].forEach(code => { FS.genericErrors[code] = new FS.ErrnoError(code) FS.genericErrors[code].stack = '' }) }, staticInit: () => { FS.ensureErrnoError() FS.nameTable = new Array(4096) FS.mount(MEMFS, {}, '/') FS.createDefaultDirectories() FS.createDefaultDevices() FS.createSpecialDirectories() FS.filesystems = { MEMFS: MEMFS, NODEFS: NODEFS } }, init: (input, output, error) => { FS.init.initialized = true FS.ensureErrnoError() Module['stdin'] = input || Module['stdin'] Module['stdout'] = output || Module['stdout'] Module['stderr'] = error || Module['stderr'] FS.createStandardStreams() }, quit: () => { FS.init.initialized = false for (var i = 0; i < FS.streams.length; i++) { var stream = FS.streams[i] if (!stream) { continue } FS.close(stream) } }, getMode: (canRead, canWrite) => { var mode = 0 if (canRead) mode |= 292 | 73 if (canWrite) mode |= 146 return mode }, findObject: (path, dontResolveLastLink) => { var ret = FS.analyzePath(path, dontResolveLastLink) if (!ret.exists) { return null } return ret.object }, analyzePath: (path, dontResolveLastLink) => { try { var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) path = lookup.path } catch (e) {} var ret = { isRoot: false, exists: false, error: 0, name: null, path: null, object: null, parentExists: false, parentPath: null, parentObject: null, } try { var lookup = FS.lookupPath(path, { parent: true }) ret.parentExists = true ret.parentPath = lookup.path ret.parentObject = lookup.node ret.name = PATH.basename(path) lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) ret.exists = true ret.path = lookup.path ret.object = lookup.node ret.name = lookup.node.name ret.isRoot = lookup.path === '/' } catch (e) { ret.error = e.errno } return ret }, createPath: (parent, path, canRead, canWrite) => { parent = typeof parent == 'string' ? parent : FS.getPath(parent) var parts = path.split('/').reverse() while (parts.length) { var part = parts.pop() if (!part) continue var current = PATH.join2(parent, part) try { FS.mkdir(current) } catch (e) {} parent = current } return current }, createFile: (parent, name, properties, canRead, canWrite) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(canRead, canWrite) return FS.create(path, mode) }, createDataFile: (parent, name, data, canRead, canWrite, canOwn) => { var path = name if (parent) { parent = typeof parent == 'string' ? parent : FS.getPath(parent) path = name ? PATH.join2(parent, name) : parent } var mode = FS.getMode(canRead, canWrite) var node = FS.create(path, mode) if (data) { if (typeof data == 'string') { var arr = new Array(data.length) for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i) data = arr } FS.chmod(node, mode | 146) var stream = FS.open(node, 577) FS.write(stream, data, 0, data.length, 0, canOwn) FS.close(stream) FS.chmod(node, mode) } return node }, createDevice: (parent, name, input, output) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(!!input, !!output) if (!FS.createDevice.major) FS.createDevice.major = 64 var dev = FS.makedev(FS.createDevice.major++, 0) FS.registerDevice(dev, { open: stream => { stream.seekable = false }, close: stream => { if (output && output.buffer && output.buffer.length) { output(10) } }, read: (stream, buffer, offset, length, pos) => { var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = input() } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: (stream, buffer, offset, length, pos) => { for (var i = 0; i < length; i++) { try { output(buffer[offset + i]) } catch (e) { throw new FS.ErrnoError(29) } } if (length) { stream.node.timestamp = Date.now() } return i }, }) return FS.mkdev(path, mode, dev) }, forceLoadFile: obj => { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true if (typeof XMLHttpRequest != 'undefined') { throw new Error( 'Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.' ) } else if (read_) { try { obj.contents = intArrayFromString(read_(obj.url), true) obj.usedBytes = obj.contents.length } catch (e) { throw new FS.ErrnoError(29) } } else { throw new Error('Cannot load without read() or XMLHttpRequest.') } }, createLazyFile: (parent, name, url, canRead, canWrite) => { function LazyUint8Array() { this.lengthKnown = false this.chunks = [] } LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) { if (idx > this.length - 1 || idx < 0) { return undefined } var chunkOffset = idx % this.chunkSize var chunkNum = (idx / this.chunkSize) | 0 return this.getter(chunkNum)[chunkOffset] } LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter( getter ) { this.getter = getter } LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { var xhr = new XMLHttpRequest() xhr.open('HEAD', url, false) xhr.send(null) if (!((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) throw new Error("Couldn't load " + url + '. Status: ' + xhr.status) var datalength = Number(xhr.getResponseHeader('Content-length')) var header var hasByteServing = (header = xhr.getResponseHeader('Accept-Ranges')) && header === 'bytes' var usesGzip = (header = xhr.getResponseHeader('Content-Encoding')) && header === 'gzip' var chunkSize = 1024 * 1024 if (!hasByteServing) chunkSize = datalength var doXHR = (from, to) => { if (from > to) throw new Error( 'invalid range (' + from + ', ' + to + ') or no bytes requested!' ) if (to > datalength - 1) throw new Error( 'only ' + datalength + ' bytes available! programmer error!' ) var xhr = new XMLHttpRequest() xhr.open('GET', url, false) if (datalength !== chunkSize) xhr.setRequestHeader('Range', 'bytes=' + from + '-' + to) xhr.responseType = 'arraybuffer' if (xhr.overrideMimeType) { xhr.overrideMimeType('text/plain; charset=x-user-defined') } xhr.send(null) if ( !((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ) throw new Error( "Couldn't load " + url + '. Status: ' + xhr.status ) if (xhr.response !== undefined) { return new Uint8Array(xhr.response || []) } return intArrayFromString(xhr.responseText || '', true) } var lazyArray = this lazyArray.setDataGetter(chunkNum => { var start = chunkNum * chunkSize var end = (chunkNum + 1) * chunkSize - 1 end = Math.min(end, datalength - 1) if (typeof lazyArray.chunks[chunkNum] == 'undefined') { lazyArray.chunks[chunkNum] = doXHR(start, end) } if (typeof lazyArray.chunks[chunkNum] == 'undefined') throw new Error('doXHR failed!') return lazyArray.chunks[chunkNum] }) if (usesGzip || !datalength) { chunkSize = datalength = 1 datalength = this.getter(0).length chunkSize = datalength out( 'LazyFiles on gzip forces download of the whole file when length is accessed' ) } this._length = datalength this._chunkSize = chunkSize this.lengthKnown = true } if (typeof XMLHttpRequest != 'undefined') { if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc' var lazyArray = new LazyUint8Array() Object.defineProperties(lazyArray, { length: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._length }, }, chunkSize: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._chunkSize }, }, }) var properties = { isDevice: false, contents: lazyArray } } else { var properties = { isDevice: false, url: url } } var node = FS.createFile(parent, name, properties, canRead, canWrite) if (properties.contents) { node.contents = properties.contents } else if (properties.url) { node.contents = null node.url = properties.url } Object.defineProperties(node, { usedBytes: { get: function() { return this.contents.length }, }, }) var stream_ops = {} var keys = Object.keys(node.stream_ops) keys.forEach(key => { var fn = node.stream_ops[key] stream_ops[key] = function forceLoadLazyFile() { FS.forceLoadFile(node) return fn.apply(null, arguments) } }) function writeChunks(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= contents.length) return 0 var size = Math.min(contents.length - position, length) if (contents.slice) { for (var i = 0; i < size; i++) { buffer[offset + i] = contents[position + i] } } else { for (var i = 0; i < size; i++) { buffer[offset + i] = contents.get(position + i) } } return size } stream_ops.read = (stream, buffer, offset, length, position) => { FS.forceLoadFile(node) return writeChunks(stream, buffer, offset, length, position) } stream_ops.mmap = (stream, length, position, prot, flags) => { FS.forceLoadFile(node) var ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } writeChunks(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } } node.stream_ops = stream_ops return node }, createPreloadedFile: ( parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish ) => { var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent var dep = getUniqueRunDependency('cp ' + fullname) function processData(byteArray) { function finish(byteArray) { if (preFinish) preFinish() if (!dontCreateFile) { FS.createDataFile( parent, name, byteArray, canRead, canWrite, canOwn ) } if (onload) onload() removeRunDependency(dep) } if ( Browser.handledByPreloadPlugin(byteArray, fullname, finish, () => { if (onerror) onerror() removeRunDependency(dep) }) ) { return } finish(byteArray) } addRunDependency(dep) if (typeof url == 'string') { asyncLoad(url, byteArray => processData(byteArray), onerror) } else { processData(url) } }, indexedDB: () => { return ( window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB ) }, DB_NAME: () => { return 'EM_FS_' + window.location.pathname }, DB_VERSION: 20, DB_STORE_NAME: 'FILE_DATA', saveFilesToDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = () => { out('creating db') var db = openRequest.result db.createObjectStore(FS.DB_STORE_NAME) } openRequest.onsuccess = () => { var db = openRequest.result var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite') var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var putRequest = files.put( FS.analyzePath(path).object.contents, path ) putRequest.onsuccess = () => { ok++ if (ok + fail == total) finish() } putRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, loadFilesFromDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = onerror openRequest.onsuccess = () => { var db = openRequest.result try { var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly') } catch (e) { onerror(e) return } var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var getRequest = files.get(path) getRequest.onsuccess = () => { if (FS.analyzePath(path).exists) { FS.unlink(path) } FS.createDataFile( PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true ) ok++ if (ok + fail == total) finish() } getRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, } var SYSCALLS = { DEFAULT_POLLMASK: 5, calculateAt: function(dirfd, path, allowEmpty) { if (PATH.isAbs(path)) { return path } var dir if (dirfd === -100) { dir = FS.cwd() } else { var dirstream = SYSCALLS.getStreamFromFD(dirfd) dir = dirstream.path } if (path.length == 0) { if (!allowEmpty) { throw new FS.ErrnoError(44) } return dir } return PATH.join2(dir, path) }, doStat: function(func, path, buf) { try { var stat = func(path) } catch (e) { if ( e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node)) ) { return -54 } throw e } HEAP32[buf >> 2] = stat.dev HEAP32[(buf + 8) >> 2] = stat.ino HEAP32[(buf + 12) >> 2] = stat.mode HEAPU32[(buf + 16) >> 2] = stat.nlink HEAP32[(buf + 20) >> 2] = stat.uid HEAP32[(buf + 24) >> 2] = stat.gid HEAP32[(buf + 28) >> 2] = stat.rdev ;(tempI64 = [ stat.size >>> 0, ((tempDouble = stat.size), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 40) >> 2] = tempI64[0]), (HEAP32[(buf + 44) >> 2] = tempI64[1]) HEAP32[(buf + 48) >> 2] = 4096 HEAP32[(buf + 52) >> 2] = stat.blocks var atime = stat.atime.getTime() var mtime = stat.mtime.getTime() var ctime = stat.ctime.getTime() ;(tempI64 = [ Math.floor(atime / 1e3) >>> 0, ((tempDouble = Math.floor(atime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 56) >> 2] = tempI64[0]), (HEAP32[(buf + 60) >> 2] = tempI64[1]) HEAPU32[(buf + 64) >> 2] = (atime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(mtime / 1e3) >>> 0, ((tempDouble = Math.floor(mtime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 72) >> 2] = tempI64[0]), (HEAP32[(buf + 76) >> 2] = tempI64[1]) HEAPU32[(buf + 80) >> 2] = (mtime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(ctime / 1e3) >>> 0, ((tempDouble = Math.floor(ctime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 88) >> 2] = tempI64[0]), (HEAP32[(buf + 92) >> 2] = tempI64[1]) HEAPU32[(buf + 96) >> 2] = (ctime % 1e3) * 1e3 ;(tempI64 = [ stat.ino >>> 0, ((tempDouble = stat.ino), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 104) >> 2] = tempI64[0]), (HEAP32[(buf + 108) >> 2] = tempI64[1]) return 0 }, doMsync: function(addr, stream, len, flags, offset) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } if (flags & 2) { return 0 } var buffer = HEAPU8.slice(addr, addr + len) FS.msync(stream, buffer, offset, len, flags) }, varargs: undefined, get: function() { SYSCALLS.varargs += 4 var ret = HEAP32[(SYSCALLS.varargs - 4) >> 2] return ret }, getStr: function(ptr) { var ret = UTF8ToString(ptr) return ret }, getStreamFromFD: function(fd) { var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) return stream }, } function ___syscall_fcntl64(fd, cmd, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (cmd) { case 0: { var arg = SYSCALLS.get() if (arg < 0) { return -28 } var newStream newStream = FS.createStream(stream, arg) return newStream.fd } case 1: case 2: return 0 case 3: return stream.flags case 4: { var arg = SYSCALLS.get() stream.flags |= arg return 0 } case 5: { var arg = SYSCALLS.get() var offset = 0 HEAP16[(arg + offset) >> 1] = 2 return 0 } case 6: case 7: return 0 case 16: case 8: return -28 case 9: setErrNo(28) return -1 default: { return -28 } } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_getcwd(buf, size) { try { if (size === 0) return -28 var cwd = FS.cwd() var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1 if (size < cwdLengthInBytes) return -68 stringToUTF8(cwd, buf, size) return cwdLengthInBytes } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_ioctl(fd, op, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (op) { case 21509: case 21505: { if (!stream.tty) return -59 return 0 } case 21510: case 21511: case 21512: case 21506: case 21507: case 21508: { if (!stream.tty) return -59 return 0 } case 21519: { if (!stream.tty) return -59 var argp = SYSCALLS.get() HEAP32[argp >> 2] = 0 return 0 } case 21520: { if (!stream.tty) return -59 return -28 } case 21531: { var argp = SYSCALLS.get() return FS.ioctl(stream, op, argp) } case 21523: { if (!stream.tty) return -59 return 0 } case 21524: { if (!stream.tty) return -59 return 0 } default: return -28 } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_openat(dirfd, path, flags, varargs) { SYSCALLS.varargs = varargs try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) var mode = varargs ? SYSCALLS.get() : 0 return FS.open(path, flags, mode).fd } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_readlinkat(dirfd, path, buf, bufsize) { try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) if (bufsize <= 0) return -28 var ret = FS.readlink(path) var len = Math.min(bufsize, lengthBytesUTF8(ret)) var endChar = HEAP8[buf + len] stringToUTF8(ret, buf, bufsize + 1) HEAP8[buf + len] = endChar return len } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_stat64(path, buf) { try { path = SYSCALLS.getStr(path) return SYSCALLS.doStat(FS.stat, path, buf) } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function _abort() { abort('') } function _emscripten_memcpy_big(dest, src, num) { HEAPU8.copyWithin(dest, src, src + num) } function getHeapMax() { return 2147483648 } function emscripten_realloc_buffer(size) { try { wasmMemory.grow((size - buffer.byteLength + 65535) >>> 16) updateGlobalBufferAndViews(wasmMemory.buffer) return 1 } catch (e) {} } function _emscripten_resize_heap(requestedSize) { var oldSize = HEAPU8.length requestedSize = requestedSize >>> 0 var maxHeapSize = getHeapMax() if (requestedSize > maxHeapSize) { return false } let alignUp = (x, multiple) => x + ((multiple - (x % multiple)) % multiple) for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown) overGrownHeapSize = Math.min( overGrownHeapSize, requestedSize + 100663296 ) var newSize = Math.min( maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536) ) var replacement = emscripten_realloc_buffer(newSize) if (replacement) { return true } } return false } var ENV = {} function getExecutableName() { return thisProgram || './this.program' } function getEnvStrings() { if (!getEnvStrings.strings) { var lang = ( (typeof navigator == 'object' && navigator.languages && navigator.languages[0]) || 'C' ).replace('-', '_') + '.UTF-8' var env = { USER: 'web_user', LOGNAME: 'web_user', PATH: '/', PWD: '/', HOME: '/home/web_user', LANG: lang, _: getExecutableName(), } for (var x in ENV) { if (ENV[x] === undefined) delete env[x] else env[x] = ENV[x] } var strings = [] for (var x in env) { strings.push(x + '=' + env[x]) } getEnvStrings.strings = strings } return getEnvStrings.strings } function writeAsciiToMemory(str, buffer, dontAddNull) { for (var i = 0; i < str.length; ++i) { HEAP8[buffer++ >> 0] = str.charCodeAt(i) } if (!dontAddNull) HEAP8[buffer >> 0] = 0 } function _environ_get(__environ, environ_buf) { var bufSize = 0 getEnvStrings().forEach(function(string, i) { var ptr = environ_buf + bufSize HEAPU32[(__environ + i * 4) >> 2] = ptr writeAsciiToMemory(string, ptr) bufSize += string.length + 1 }) return 0 } function _environ_sizes_get(penviron_count, penviron_buf_size) { var strings = getEnvStrings() HEAPU32[penviron_count >> 2] = strings.length var bufSize = 0 strings.forEach(function(string) { bufSize += string.length + 1 }) HEAPU32[penviron_buf_size >> 2] = bufSize return 0 } function _proc_exit(code) { EXITSTATUS = code if (!keepRuntimeAlive()) { if (Module['onExit']) Module['onExit'](code) ABORT = true } quit_(code, new ExitStatus(code)) } function exitJS(status, implicit) { EXITSTATUS = status _proc_exit(status) } var _exit = exitJS function _fd_close(fd) { try { var stream = SYSCALLS.getStreamFromFD(fd) FS.close(stream) return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doReadv(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.read(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr if (curr < len) break } return ret } function _fd_read(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doReadv(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function convertI32PairToI53Checked(lo, hi) { return (hi + 2097152) >>> 0 < 4194305 - !!lo ? (lo >>> 0) + hi * 4294967296 : NaN } function _fd_seek(fd, offset_low, offset_high, whence, newOffset) { try { var offset = convertI32PairToI53Checked(offset_low, offset_high) if (isNaN(offset)) return 61 var stream = SYSCALLS.getStreamFromFD(fd) FS.llseek(stream, offset, whence) ;(tempI64 = [ stream.position >>> 0, ((tempDouble = stream.position), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[newOffset >> 2] = tempI64[0]), (HEAP32[(newOffset + 4) >> 2] = tempI64[1]) if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doWritev(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.write(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr } return ret } function _fd_write(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doWritev(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function __isLeapYear(year) { return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) } function __arraySum(array, index) { var sum = 0 for (var i = 0; i <= index; sum += array[i++]) {} return sum } var __MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] var __MONTH_DAYS_REGULAR = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] function __addDays(date, days) { var newDate = new Date(date.getTime()) while (days > 0) { var leap = __isLeapYear(newDate.getFullYear()) var currentMonth = newDate.getMonth() var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth] if (days > daysInCurrentMonth - newDate.getDate()) { days -= daysInCurrentMonth - newDate.getDate() + 1 newDate.setDate(1) if (currentMonth < 11) { newDate.setMonth(currentMonth + 1) } else { newDate.setMonth(0) newDate.setFullYear(newDate.getFullYear() + 1) } } else { newDate.setDate(newDate.getDate() + days) return newDate } } return newDate } function writeArrayToMemory(array, buffer) { HEAP8.set(array, buffer) } function _strftime(s, maxsize, format, tm) { var tm_zone = HEAP32[(tm + 40) >> 2] var date = { tm_sec: HEAP32[tm >> 2], tm_min: HEAP32[(tm + 4) >> 2], tm_hour: HEAP32[(tm + 8) >> 2], tm_mday: HEAP32[(tm + 12) >> 2], tm_mon: HEAP32[(tm + 16) >> 2], tm_year: HEAP32[(tm + 20) >> 2], tm_wday: HEAP32[(tm + 24) >> 2], tm_yday: HEAP32[(tm + 28) >> 2], tm_isdst: HEAP32[(tm + 32) >> 2], tm_gmtoff: HEAP32[(tm + 36) >> 2], tm_zone: tm_zone ? UTF8ToString(tm_zone) : '', } var pattern = UTF8ToString(format) var EXPANSION_RULES_1 = { '%c': '%a %b %d %H:%M:%S %Y', '%D': '%m/%d/%y', '%F': '%Y-%m-%d', '%h': '%b', '%r': '%I:%M:%S %p', '%R': '%H:%M', '%T': '%H:%M:%S', '%x': '%m/%d/%y', '%X': '%H:%M:%S', '%Ec': '%c', '%EC': '%C', '%Ex': '%m/%d/%y', '%EX': '%H:%M:%S', '%Ey': '%y', '%EY': '%Y', '%Od': '%d', '%Oe': '%e', '%OH': '%H', '%OI': '%I', '%Om': '%m', '%OM': '%M', '%OS': '%S', '%Ou': '%u', '%OU': '%U', '%OV': '%V', '%Ow': '%w', '%OW': '%W', '%Oy': '%y', } for (var rule in EXPANSION_RULES_1) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_1[rule] ) } var WEEKDAYS = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', ] var MONTHS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ] function leadingSomething(value, digits, character) { var str = typeof value == 'number' ? value.toString() : value || '' while (str.length < digits) { str = character[0] + str } return str } function leadingNulls(value, digits) { return leadingSomething(value, digits, '0') } function compareByDay(date1, date2) { function sgn(value) { return value < 0 ? -1 : value > 0 ? 1 : 0 } var compare if ((compare = sgn(date1.getFullYear() - date2.getFullYear())) === 0) { if ((compare = sgn(date1.getMonth() - date2.getMonth())) === 0) { compare = sgn(date1.getDate() - date2.getDate()) } } return compare } function getFirstWeekStartDate(janFourth) { switch (janFourth.getDay()) { case 0: return new Date(janFourth.getFullYear() - 1, 11, 29) case 1: return janFourth case 2: return new Date(janFourth.getFullYear(), 0, 3) case 3: return new Date(janFourth.getFullYear(), 0, 2) case 4: return new Date(janFourth.getFullYear(), 0, 1) case 5: return new Date(janFourth.getFullYear() - 1, 11, 31) case 6: return new Date(janFourth.getFullYear() - 1, 11, 30) } } function getWeekBasedYear(date) { var thisDate = __addDays( new Date(date.tm_year + 1900, 0, 1), date.tm_yday ) var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4) var janFourthNextYear = new Date(thisDate.getFullYear() + 1, 0, 4) var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear) var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear) if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { return thisDate.getFullYear() + 1 } return thisDate.getFullYear() } return thisDate.getFullYear() - 1 } var EXPANSION_RULES_2 = { '%a': function(date) { return WEEKDAYS[date.tm_wday].substring(0, 3) }, '%A': function(date) { return WEEKDAYS[date.tm_wday] }, '%b': function(date) { return MONTHS[date.tm_mon].substring(0, 3) }, '%B': function(date) { return MONTHS[date.tm_mon] }, '%C': function(date) { var year = date.tm_year + 1900 return leadingNulls((year / 100) | 0, 2) }, '%d': function(date) { return leadingNulls(date.tm_mday, 2) }, '%e': function(date) { return leadingSomething(date.tm_mday, 2, ' ') }, '%g': function(date) { return getWeekBasedYear(date) .toString() .substring(2) }, '%G': function(date) { return getWeekBasedYear(date) }, '%H': function(date) { return leadingNulls(date.tm_hour, 2) }, '%I': function(date) { var twelveHour = date.tm_hour if (twelveHour == 0) twelveHour = 12 else if (twelveHour > 12) twelveHour -= 12 return leadingNulls(twelveHour, 2) }, '%j': function(date) { return leadingNulls( date.tm_mday + __arraySum( __isLeapYear(date.tm_year + 1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon - 1 ), 3 ) }, '%m': function(date) { return leadingNulls(date.tm_mon + 1, 2) }, '%M': function(date) { return leadingNulls(date.tm_min, 2) }, '%n': function() { return '\n' }, '%p': function(date) { if (date.tm_hour >= 0 && date.tm_hour < 12) { return 'AM' } return 'PM' }, '%S': function(date) { return leadingNulls(date.tm_sec, 2) }, '%t': function() { return '\t' }, '%u': function(date) { return date.tm_wday || 7 }, '%U': function(date) { var days = date.tm_yday + 7 - date.tm_wday return leadingNulls(Math.floor(days / 7), 2) }, '%V': function(date) { var val = Math.floor( (date.tm_yday + 7 - ((date.tm_wday + 6) % 7)) / 7 ) if ((date.tm_wday + 371 - date.tm_yday - 2) % 7 <= 2) { val++ } if (!val) { val = 52 var dec31 = (date.tm_wday + 7 - date.tm_yday - 1) % 7 if ( dec31 == 4 || (dec31 == 5 && __isLeapYear((date.tm_year % 400) - 1)) ) { val++ } } else if (val == 53) { var jan1 = (date.tm_wday + 371 - date.tm_yday) % 7 if (jan1 != 4 && (jan1 != 3 || !__isLeapYear(date.tm_year))) val = 1 } return leadingNulls(val, 2) }, '%w': function(date) { return date.tm_wday }, '%W': function(date) { var days = date.tm_yday + 7 - ((date.tm_wday + 6) % 7) return leadingNulls(Math.floor(days / 7), 2) }, '%y': function(date) { return (date.tm_year + 1900).toString().substring(2) }, '%Y': function(date) { return date.tm_year + 1900 }, '%z': function(date) { var off = date.tm_gmtoff var ahead = off >= 0 off = Math.abs(off) / 60 off = (off / 60) * 100 + (off % 60) return (ahead ? '+' : '-') + String('0000' + off).slice(-4) }, '%Z': function(date) { return date.tm_zone }, '%%': function() { return '%' }, } pattern = pattern.replace(/%%/g, '\0\0') for (var rule in EXPANSION_RULES_2) { if (pattern.includes(rule)) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date) ) } } pattern = pattern.replace(/\0\0/g, '%') var bytes = intArrayFromString(pattern, false) if (bytes.length > maxsize) { return 0 } writeArrayToMemory(bytes, s) return bytes.length - 1 } function _strftime_l(s, maxsize, format, tm, loc) { return _strftime(s, maxsize, format, tm) } function handleException(e) { if (e instanceof ExitStatus || e == 'unwind') { return EXITSTATUS } quit_(1, e) } function allocateUTF8OnStack(str) { var size = lengthBytesUTF8(str) + 1 var ret = stackAlloc(size) stringToUTF8Array(str, HEAP8, ret, size) return ret } function getCFunc(ident) { var func = Module['_' + ident] return func } function ccall(ident, returnType, argTypes, args, opts) { var toC = { string: str => { var ret = 0 if (str !== null && str !== undefined && str !== 0) { var len = (str.length << 2) + 1 ret = stackAlloc(len) stringToUTF8(str, ret, len) } return ret }, array: arr => { var ret = stackAlloc(arr.length) writeArrayToMemory(arr, ret) return ret }, } function convertReturnValue(ret) { if (returnType === 'string') { return UTF8ToString(ret) } if (returnType === 'boolean') return Boolean(ret) return ret } var func = getCFunc(ident) var cArgs = [] var stack = 0 if (args) { for (var i = 0; i < args.length; i++) { var converter = toC[argTypes[i]] if (converter) { if (stack === 0) stack = stackSave() cArgs[i] = converter(args[i]) } else { cArgs[i] = args[i] } } } var ret = func.apply(null, cArgs) function onDone(ret) { if (stack !== 0) stackRestore(stack) return convertReturnValue(ret) } ret = onDone(ret) return ret } function cwrap(ident, returnType, argTypes, opts) { argTypes = argTypes || [] var numericArgs = argTypes.every( type => type === 'number' || type === 'boolean' ) var numericRet = returnType !== 'string' if (numericRet && numericArgs && !opts) { return getCFunc(ident) } return function() { return ccall(ident, returnType, argTypes, arguments, opts) } } function AsciiToString(ptr) { var str = '' while (1) { var ch = HEAPU8[ptr++ >> 0] if (!ch) return str str += String.fromCharCode(ch) } } var FSNode = function(parent, name, mode, rdev) { if (!parent) { parent = this } this.parent = parent this.mount = parent.mount this.mounted = null this.id = FS.nextInode++ this.name = name this.mode = mode this.node_ops = {} this.stream_ops = {} this.rdev = rdev } var readMode = 292 | 73 var writeMode = 146 Object.defineProperties(FSNode.prototype, { read: { get: function() { return (this.mode & readMode) === readMode }, set: function(val) { val ? (this.mode |= readMode) : (this.mode &= ~readMode) }, }, write: { get: function() { return (this.mode & writeMode) === writeMode }, set: function(val) { val ? (this.mode |= writeMode) : (this.mode &= ~writeMode) }, }, isFolder: { get: function() { return FS.isDir(this.mode) }, }, isDevice: { get: function() { return FS.isChrdev(this.mode) }, }, }) FS.FSNode = FSNode FS.staticInit() Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_unlink'] = FS.unlink Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice if (ENVIRONMENT_IS_NODE) { NODEFS.staticInit() } ERRNO_CODES = { EPERM: 63, ENOENT: 44, ESRCH: 71, EINTR: 27, EIO: 29, ENXIO: 60, E2BIG: 1, ENOEXEC: 45, EBADF: 8, ECHILD: 12, EAGAIN: 6, EWOULDBLOCK: 6, ENOMEM: 48, EACCES: 2, EFAULT: 21, ENOTBLK: 105, EBUSY: 10, EEXIST: 20, EXDEV: 75, ENODEV: 43, ENOTDIR: 54, EISDIR: 31, EINVAL: 28, ENFILE: 41, EMFILE: 33, ENOTTY: 59, ETXTBSY: 74, EFBIG: 22, ENOSPC: 51, ESPIPE: 70, EROFS: 69, EMLINK: 34, EPIPE: 64, EDOM: 18, ERANGE: 68, ENOMSG: 49, EIDRM: 24, ECHRNG: 106, EL2NSYNC: 156, EL3HLT: 107, EL3RST: 108, ELNRNG: 109, EUNATCH: 110, ENOCSI: 111, EL2HLT: 112, EDEADLK: 16, ENOLCK: 46, EBADE: 113, EBADR: 114, EXFULL: 115, ENOANO: 104, EBADRQC: 103, EBADSLT: 102, EDEADLOCK: 16, EBFONT: 101, ENOSTR: 100, ENODATA: 116, ETIME: 117, ENOSR: 118, ENONET: 119, ENOPKG: 120, EREMOTE: 121, ENOLINK: 47, EADV: 122, ESRMNT: 123, ECOMM: 124, EPROTO: 65, EMULTIHOP: 36, EDOTDOT: 125, EBADMSG: 9, ENOTUNIQ: 126, EBADFD: 127, EREMCHG: 128, ELIBACC: 129, ELIBBAD: 130, ELIBSCN: 131, ELIBMAX: 132, ELIBEXEC: 133, ENOSYS: 52, ENOTEMPTY: 55, ENAMETOOLONG: 37, ELOOP: 32, EOPNOTSUPP: 138, EPFNOSUPPORT: 139, ECONNRESET: 15, ENOBUFS: 42, EAFNOSUPPORT: 5, EPROTOTYPE: 67, ENOTSOCK: 57, ENOPROTOOPT: 50, ESHUTDOWN: 140, ECONNREFUSED: 14, EADDRINUSE: 3, ECONNABORTED: 13, ENETUNREACH: 40, ENETDOWN: 38, ETIMEDOUT: 73, EHOSTDOWN: 142, EHOSTUNREACH: 23, EINPROGRESS: 26, EALREADY: 7, EDESTADDRREQ: 17, EMSGSIZE: 35, EPROTONOSUPPORT: 66, ESOCKTNOSUPPORT: 137, EADDRNOTAVAIL: 4, ENETRESET: 39, EISCONN: 30, ENOTCONN: 53, ETOOMANYREFS: 141, EUSERS: 136, EDQUOT: 19, ESTALE: 72, ENOTSUP: 138, ENOMEDIUM: 148, EILSEQ: 25, EOVERFLOW: 61, ECANCELED: 11, ENOTRECOVERABLE: 56, EOWNERDEAD: 62, ESTRPIPE: 135, } var asmLibraryArg = { a: ___cxa_throw, d: ___syscall_fcntl64, r: ___syscall_getcwd, i: ___syscall_ioctl, j: ___syscall_openat, n: ___syscall_readlinkat, o: ___syscall_stat64, b: _abort, f: _emscripten_memcpy_big, m: _emscripten_resize_heap, p: _environ_get, q: _environ_sizes_get, c: _exit, e: _fd_close, h: _fd_read, k: _fd_seek, g: _fd_write, l: _strftime_l, } var asm = createWasm() var ___wasm_call_ctors = (Module['___wasm_call_ctors'] = function() { return (___wasm_call_ctors = Module['___wasm_call_ctors'] = Module['asm']['t']).apply(null, arguments) }) var _main = (Module['_main'] = function() { return (_main = Module['_main'] = Module['asm']['u']).apply( null, arguments ) }) var ___errno_location = (Module['___errno_location'] = function() { return (___errno_location = Module['___errno_location'] = Module['asm']['v']).apply(null, arguments) }) var _itk_wasm_input_array_alloc = (Module[ '_itk_wasm_input_array_alloc' ] = function() { return (_itk_wasm_input_array_alloc = Module[ '_itk_wasm_input_array_alloc' ] = Module['asm']['w']).apply(null, arguments) }) var _itk_wasm_input_json_alloc = (Module[ '_itk_wasm_input_json_alloc' ] = function() { return (_itk_wasm_input_json_alloc = Module[ '_itk_wasm_input_json_alloc' ] = Module['asm']['x']).apply(null, arguments) }) var _itk_wasm_output_json_address = (Module[ '_itk_wasm_output_json_address' ] = function() { return (_itk_wasm_output_json_address = Module[ '_itk_wasm_output_json_address' ] = Module['asm']['y']).apply(null, arguments) }) var _itk_wasm_output_json_size = (Module[ '_itk_wasm_output_json_size' ] = function() { return (_itk_wasm_output_json_size = Module[ '_itk_wasm_output_json_size' ] = Module['asm']['z']).apply(null, arguments) }) var _itk_wasm_output_array_address = (Module[ '_itk_wasm_output_array_address' ] = function() { return (_itk_wasm_output_array_address = Module[ '_itk_wasm_output_array_address' ] = Module['asm']['A']).apply(null, arguments) }) var _itk_wasm_output_array_size = (Module[ '_itk_wasm_output_array_size' ] = function() { return (_itk_wasm_output_array_size = Module[ '_itk_wasm_output_array_size' ] = Module['asm']['B']).apply(null, arguments) }) var _itk_wasm_free_all = (Module['_itk_wasm_free_all'] = function() { return (_itk_wasm_free_all = Module['_itk_wasm_free_all'] = Module['asm']['C']).apply(null, arguments) }) var stackSave = (Module['stackSave'] = function() { return (stackSave = Module['stackSave'] = Module['asm']['E']).apply( null, arguments ) }) var stackRestore = (Module['stackRestore'] = function() { return (stackRestore = Module['stackRestore'] = Module['asm']['F']).apply( null, arguments ) }) var stackAlloc = (Module['stackAlloc'] = function() { return (stackAlloc = Module['stackAlloc'] = Module['asm']['G']).apply( null, arguments ) }) var ___cxa_is_pointer_type = (Module[ '___cxa_is_pointer_type' ] = function() { return (___cxa_is_pointer_type = Module['___cxa_is_pointer_type'] = Module['asm']['H']).apply(null, arguments) }) Module['addRunDependency'] = addRunDependency Module['removeRunDependency'] = removeRunDependency Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice Module['FS_unlink'] = FS.unlink Module['callMain'] = callMain Module['ccall'] = ccall Module['cwrap'] = cwrap Module['AsciiToString'] = AsciiToString Module['writeArrayToMemory'] = writeArrayToMemory Module['writeAsciiToMemory'] = writeAsciiToMemory var calledRun dependenciesFulfilled = function runCaller() { if (!calledRun) run() if (!calledRun) dependenciesFulfilled = runCaller } function callMain(args) { var entryFunction = Module['_main'] args = args || [] args.unshift(thisProgram) var argc = args.length var argv = stackAlloc((argc + 1) * 4) var argv_ptr = argv >> 2 args.forEach(arg => { HEAP32[argv_ptr++] = allocateUTF8OnStack(arg) }) HEAP32[argv_ptr] = 0 try { var ret = entryFunction(argc, argv) exitJS(ret, true) return ret } catch (e) { return handleException(e) } } function run(args) { args = args || arguments_ if (runDependencies > 0) { return } preRun() if (runDependencies > 0) { return } function doRun() { if (calledRun) return calledRun = true Module['calledRun'] = true if (ABORT) return initRuntime() preMain() readyPromiseResolve(Module) if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized']() if (shouldRunNow) callMain(args) postRun() } if (Module['setStatus']) { Module['setStatus']('Running...') setTimeout(function() { setTimeout(function() { Module['setStatus']('') }, 1) doRun() }, 1) } else { doRun() } } if (Module['preInit']) { if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']] while (Module['preInit'].length > 0) { Module['preInit'].pop()() } } var shouldRunNow = false if (Module['noInitialRun']) shouldRunNow = false run() Module.mountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) if (FS.isDir(containingDir) || containingDir === '/') { return } var currentDir = '/' var splitContainingDir = containingDir.split(path.sep) for (var ii = 1; ii < splitContainingDir.length; ii++) { currentDir += splitContainingDir[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } FS.mount(NODEFS, { root: containingDir }, currentDir) return currentDir + path.basename(filePath) } Module.unmountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) FS.unmount(containingDir) } Module.fs_mkdirs = function(dirs) { var currentDir = '/' var splitDirs = dirs.split('/') for (var ii = 1; ii < splitDirs.length; ++ii) { currentDir += splitDirs[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } } Module.fs_readFile = function(path, opts) { return FS.readFile(path, opts) } Module.fs_writeFile = function(path, data, opts) { return FS.writeFile(path, data, opts) } Module.fs_unlink = function(path) { return FS.unlink(path) } Module.fs_open = function(path, flags, mode) { return FS.open(path, flags, mode) } Module.fs_stat = function(path) { return FS.stat(path) } Module.fs_read = function(stream, buffer, offset, length, position) { return FS.read(stream, buffer, offset, length, position) } Module.fs_close = function(stream) { return FS.close(stream) } return DownsampleLabelImage.ready } })() export default DownsampleLabelImage ================================================ FILE: src/IO/Downsample/emscripten-build/DownsampleLabelImage.umd.js ================================================ var DownsampleLabelImage = (() => { var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename return function(DownsampleLabelImage) { DownsampleLabelImage = DownsampleLabelImage || {} var Module = typeof DownsampleLabelImage != 'undefined' ? DownsampleLabelImage : {} var readyPromiseResolve, readyPromiseReject Module['ready'] = new Promise(function(resolve, reject) { readyPromiseResolve = resolve readyPromiseReject = reject }) var mStdout = null var mStderr = null Module['resetModuleStdout'] = function() { mStdout = '' } Module['resetModuleStderr'] = function() { mStderr = '' } Module['print'] = function(text) { console.log(text) mStdout += text + '\n' } Module['printErr'] = function(text) { console.error(text) mStderr += text + '\n' } Module['getModuleStdout'] = function() { return mStdout } Module['getModuleStderr'] = function() { return mStderr } var moduleOverrides = Object.assign({}, Module) var arguments_ = [] var thisProgram = './this.program' var quit_ = (status, toThrow) => { throw toThrow } var ENVIRONMENT_IS_WEB = typeof window == 'object' var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function' var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string' var scriptDirectory = '' function locateFile(path) { if (Module['locateFile']) { return Module['locateFile'](path, scriptDirectory) } return scriptDirectory + path } var read_, readAsync, readBinary, setWindowTitle function logExceptionOnExit(e) { if (e instanceof ExitStatus) return let toLog = e err('exiting due to exception: ' + toLog) } if (ENVIRONMENT_IS_NODE) { var fs = require('fs') var nodePath = require('path') if (ENVIRONMENT_IS_WORKER) { scriptDirectory = nodePath.dirname(scriptDirectory) + '/' } else { scriptDirectory = __dirname + '/' } read_ = (filename, binary) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) return fs.readFileSync(filename, binary ? undefined : 'utf8') } readBinary = filename => { var ret = read_(filename, true) if (!ret.buffer) { ret = new Uint8Array(ret) } return ret } readAsync = (filename, onload, onerror) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) fs.readFile(filename, function(err, data) { if (err) onerror(err) else onload(data.buffer) }) } if (process['argv'].length > 1) { thisProgram = process['argv'][1].replace(/\\/g, '/') } arguments_ = process['argv'].slice(2) process['on']('uncaughtException', function(ex) { if (!(ex instanceof ExitStatus)) { throw ex } }) process['on']('unhandledRejection', function(reason) { throw reason }) quit_ = (status, toThrow) => { if (keepRuntimeAlive()) { process['exitCode'] = status throw toThrow } logExceptionOnExit(toThrow) process['exit'](status) } Module['inspect'] = function() { return '[Emscripten Module object]' } } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = self.location.href } else if (typeof document != 'undefined' && document.currentScript) { scriptDirectory = document.currentScript.src } if (_scriptDir) { scriptDirectory = _scriptDir } if (scriptDirectory.indexOf('blob:') !== 0) { scriptDirectory = scriptDirectory.substr( 0, scriptDirectory.replace(/[?#].*/, '').lastIndexOf('/') + 1 ) } else { scriptDirectory = '' } { read_ = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.send(null) return xhr.responseText } if (ENVIRONMENT_IS_WORKER) { readBinary = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.responseType = 'arraybuffer' xhr.send(null) return new Uint8Array(xhr.response) } } readAsync = (url, onload, onerror) => { var xhr = new XMLHttpRequest() xhr.open('GET', url, true) xhr.responseType = 'arraybuffer' xhr.onload = () => { if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { onload(xhr.response) return } onerror() } xhr.onerror = onerror xhr.send(null) } } setWindowTitle = title => (document.title = title) } else { } var out = Module['print'] || console.log.bind(console) var err = Module['printErr'] || console.warn.bind(console) Object.assign(Module, moduleOverrides) moduleOverrides = null if (Module['arguments']) arguments_ = Module['arguments'] if (Module['thisProgram']) thisProgram = Module['thisProgram'] if (Module['quit']) quit_ = Module['quit'] var wasmBinary if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'] var noExitRuntime = Module['noExitRuntime'] || true if (typeof WebAssembly != 'object') { abort('no native wasm support detected') } var wasmMemory var ABORT = false var EXITSTATUS function assert(condition, text) { if (!condition) { abort(text) } } var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { var endIdx = idx + maxBytesToRead var endPtr = idx while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)) } var str = '' while (idx < endPtr) { var u0 = heapOrArray[idx++] if (!(u0 & 128)) { str += String.fromCharCode(u0) continue } var u1 = heapOrArray[idx++] & 63 if ((u0 & 224) == 192) { str += String.fromCharCode(((u0 & 31) << 6) | u1) continue } var u2 = heapOrArray[idx++] & 63 if ((u0 & 240) == 224) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2 } else { u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63) } if (u0 < 65536) { str += String.fromCharCode(u0) } else { var ch = u0 - 65536 str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)) } } return str } function UTF8ToString(ptr, maxBytesToRead) { return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '' } function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { if (!(maxBytesToWrite > 0)) return 0 var startIdx = outIdx var endIdx = outIdx + maxBytesToWrite - 1 for (var i = 0; i < str.length; ++i) { var u = str.charCodeAt(i) if (u >= 55296 && u <= 57343) { var u1 = str.charCodeAt(++i) u = (65536 + ((u & 1023) << 10)) | (u1 & 1023) } if (u <= 127) { if (outIdx >= endIdx) break heap[outIdx++] = u } else if (u <= 2047) { if (outIdx + 1 >= endIdx) break heap[outIdx++] = 192 | (u >> 6) heap[outIdx++] = 128 | (u & 63) } else if (u <= 65535) { if (outIdx + 2 >= endIdx) break heap[outIdx++] = 224 | (u >> 12) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } else { if (outIdx + 3 >= endIdx) break heap[outIdx++] = 240 | (u >> 18) heap[outIdx++] = 128 | ((u >> 12) & 63) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } } heap[outIdx] = 0 return outIdx - startIdx } function stringToUTF8(str, outPtr, maxBytesToWrite) { return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite) } function lengthBytesUTF8(str) { var len = 0 for (var i = 0; i < str.length; ++i) { var c = str.charCodeAt(i) if (c <= 127) { len++ } else if (c <= 2047) { len += 2 } else if (c >= 55296 && c <= 57343) { len += 4 ++i } else { len += 3 } } return len } var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64 function updateGlobalBufferAndViews(buf) { buffer = buf Module['HEAP8'] = HEAP8 = new Int8Array(buf) Module['HEAP16'] = HEAP16 = new Int16Array(buf) Module['HEAP32'] = HEAP32 = new Int32Array(buf) Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf) Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf) Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf) Module['HEAPF32'] = HEAPF32 = new Float32Array(buf) Module['HEAPF64'] = HEAPF64 = new Float64Array(buf) } var INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 16777216 var wasmTable var __ATPRERUN__ = [] var __ATINIT__ = [] var __ATMAIN__ = [] var __ATPOSTRUN__ = [] var runtimeInitialized = false function keepRuntimeAlive() { return noExitRuntime } function preRun() { if (Module['preRun']) { if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']] while (Module['preRun'].length) { addOnPreRun(Module['preRun'].shift()) } } callRuntimeCallbacks(__ATPRERUN__) } function initRuntime() { runtimeInitialized = true if (!Module['noFSInit'] && !FS.init.initialized) FS.init() FS.ignorePermissions = false TTY.init() callRuntimeCallbacks(__ATINIT__) } function preMain() { callRuntimeCallbacks(__ATMAIN__) } function postRun() { if (Module['postRun']) { if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']] while (Module['postRun'].length) { addOnPostRun(Module['postRun'].shift()) } } callRuntimeCallbacks(__ATPOSTRUN__) } function addOnPreRun(cb) { __ATPRERUN__.unshift(cb) } function addOnInit(cb) { __ATINIT__.unshift(cb) } function addOnPostRun(cb) { __ATPOSTRUN__.unshift(cb) } var runDependencies = 0 var runDependencyWatcher = null var dependenciesFulfilled = null function getUniqueRunDependency(id) { return id } function addRunDependency(id) { runDependencies++ if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } } function removeRunDependency(id) { runDependencies-- if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } if (runDependencies == 0) { if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher) runDependencyWatcher = null } if (dependenciesFulfilled) { var callback = dependenciesFulfilled dependenciesFulfilled = null callback() } } } function abort(what) { if (Module['onAbort']) { Module['onAbort'](what) } what = 'Aborted(' + what + ')' err(what) ABORT = true EXITSTATUS = 1 what += '. Build with -sASSERTIONS for more info.' var e = new WebAssembly.RuntimeError(what) readyPromiseReject(e) throw e } var dataURIPrefix = 'data:application/octet-stream;base64,' function isDataURI(filename) { return filename.startsWith(dataURIPrefix) } function isFileURI(filename) { return filename.startsWith('file://') } var wasmBinaryFile wasmBinaryFile = 'DownsampleLabelImage.umd.wasm' if (!isDataURI(wasmBinaryFile)) { wasmBinaryFile = locateFile(wasmBinaryFile) } function getBinary(file) { try { if (file == wasmBinaryFile && wasmBinary) { return new Uint8Array(wasmBinary) } if (readBinary) { return readBinary(file) } throw 'both async and sync fetching of the wasm failed' } catch (err) { abort(err) } } function getBinaryPromise() { if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { if (typeof fetch == 'function' && !isFileURI(wasmBinaryFile)) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }) .then(function(response) { if (!response['ok']) { throw "failed to load wasm binary file at '" + wasmBinaryFile + "'" } return response['arrayBuffer']() }) .catch(function() { return getBinary(wasmBinaryFile) }) } else { if (readAsync) { return new Promise(function(resolve, reject) { readAsync( wasmBinaryFile, function(response) { resolve(new Uint8Array(response)) }, reject ) }) } } } return Promise.resolve().then(function() { return getBinary(wasmBinaryFile) }) } function createWasm() { var info = { a: asmLibraryArg } function receiveInstance(instance, module) { var exports = instance.exports Module['asm'] = exports wasmMemory = Module['asm']['s'] updateGlobalBufferAndViews(wasmMemory.buffer) wasmTable = Module['asm']['D'] addOnInit(Module['asm']['t']) removeRunDependency('wasm-instantiate') } addRunDependency('wasm-instantiate') function receiveInstantiationResult(result) { receiveInstance(result['instance']) } function instantiateArrayBuffer(receiver) { return getBinaryPromise() .then(function(binary) { return WebAssembly.instantiate(binary, info) }) .then(function(instance) { return instance }) .then(receiver, function(reason) { err('failed to asynchronously prepare wasm: ' + reason) abort(reason) }) } function instantiateAsync() { if ( !wasmBinary && typeof WebAssembly.instantiateStreaming == 'function' && !isDataURI(wasmBinaryFile) && !isFileURI(wasmBinaryFile) && !ENVIRONMENT_IS_NODE && typeof fetch == 'function' ) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then( function(response) { var result = WebAssembly.instantiateStreaming(response, info) return result.then(receiveInstantiationResult, function(reason) { err('wasm streaming compile failed: ' + reason) err('falling back to ArrayBuffer instantiation') return instantiateArrayBuffer(receiveInstantiationResult) }) } ) } else { return instantiateArrayBuffer(receiveInstantiationResult) } } if (Module['instantiateWasm']) { try { var exports = Module['instantiateWasm'](info, receiveInstance) return exports } catch (e) { err('Module.instantiateWasm callback failed with error: ' + e) readyPromiseReject(e) } } instantiateAsync().catch(readyPromiseReject) return {} } var tempDouble var tempI64 function ExitStatus(status) { this.name = 'ExitStatus' this.message = 'Program terminated with exit(' + status + ')' this.status = status } function callRuntimeCallbacks(callbacks) { while (callbacks.length > 0) { callbacks.shift()(Module) } } function ExceptionInfo(excPtr) { this.excPtr = excPtr this.ptr = excPtr - 24 this.set_type = function(type) { HEAPU32[(this.ptr + 4) >> 2] = type } this.get_type = function() { return HEAPU32[(this.ptr + 4) >> 2] } this.set_destructor = function(destructor) { HEAPU32[(this.ptr + 8) >> 2] = destructor } this.get_destructor = function() { return HEAPU32[(this.ptr + 8) >> 2] } this.set_refcount = function(refcount) { HEAP32[this.ptr >> 2] = refcount } this.set_caught = function(caught) { caught = caught ? 1 : 0 HEAP8[(this.ptr + 12) >> 0] = caught } this.get_caught = function() { return HEAP8[(this.ptr + 12) >> 0] != 0 } this.set_rethrown = function(rethrown) { rethrown = rethrown ? 1 : 0 HEAP8[(this.ptr + 13) >> 0] = rethrown } this.get_rethrown = function() { return HEAP8[(this.ptr + 13) >> 0] != 0 } this.init = function(type, destructor) { this.set_adjusted_ptr(0) this.set_type(type) this.set_destructor(destructor) this.set_refcount(0) this.set_caught(false) this.set_rethrown(false) } this.add_ref = function() { var value = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = value + 1 } this.release_ref = function() { var prev = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = prev - 1 return prev === 1 } this.set_adjusted_ptr = function(adjustedPtr) { HEAPU32[(this.ptr + 16) >> 2] = adjustedPtr } this.get_adjusted_ptr = function() { return HEAPU32[(this.ptr + 16) >> 2] } this.get_exception_ptr = function() { var isPointer = ___cxa_is_pointer_type(this.get_type()) if (isPointer) { return HEAPU32[this.excPtr >> 2] } var adjusted = this.get_adjusted_ptr() if (adjusted !== 0) return adjusted return this.excPtr } } var exceptionLast = 0 var uncaughtExceptionCount = 0 function ___cxa_throw(ptr, type, destructor) { var info = new ExceptionInfo(ptr) info.init(type, destructor) exceptionLast = ptr uncaughtExceptionCount++ throw ptr } function setErrNo(value) { HEAP32[___errno_location() >> 2] = value return value } var PATH = { isAbs: path => path.charAt(0) === '/', splitPath: filename => { var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/ return splitPathRe.exec(filename).slice(1) }, normalizeArray: (parts, allowAboveRoot) => { var up = 0 for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i] if (last === '.') { parts.splice(i, 1) } else if (last === '..') { parts.splice(i, 1) up++ } else if (up) { parts.splice(i, 1) up-- } } if (allowAboveRoot) { for (; up; up--) { parts.unshift('..') } } return parts }, normalize: path => { var isAbsolute = PATH.isAbs(path), trailingSlash = path.substr(-1) === '/' path = PATH.normalizeArray( path.split('/').filter(p => !!p), !isAbsolute ).join('/') if (!path && !isAbsolute) { path = '.' } if (path && trailingSlash) { path += '/' } return (isAbsolute ? '/' : '') + path }, dirname: path => { var result = PATH.splitPath(path), root = result[0], dir = result[1] if (!root && !dir) { return '.' } if (dir) { dir = dir.substr(0, dir.length - 1) } return root + dir }, basename: path => { if (path === '/') return '/' path = PATH.normalize(path) path = path.replace(/\/$/, '') var lastSlash = path.lastIndexOf('/') if (lastSlash === -1) return path return path.substr(lastSlash + 1) }, join: function() { var paths = Array.prototype.slice.call(arguments) return PATH.normalize(paths.join('/')) }, join2: (l, r) => { return PATH.normalize(l + '/' + r) }, } function getRandomDevice() { if ( typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function' ) { var randomBuffer = new Uint8Array(1) return () => { crypto.getRandomValues(randomBuffer) return randomBuffer[0] } } else if (ENVIRONMENT_IS_NODE) { try { var crypto_module = require('crypto') return () => crypto_module['randomBytes'](1)[0] } catch (e) {} } return () => abort('randomDevice') } var PATH_FS = { resolve: function() { var resolvedPath = '', resolvedAbsolute = false for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = i >= 0 ? arguments[i] : FS.cwd() if (typeof path != 'string') { throw new TypeError('Arguments to path.resolve must be strings') } else if (!path) { return '' } resolvedPath = path + '/' + resolvedPath resolvedAbsolute = PATH.isAbs(path) } resolvedPath = PATH.normalizeArray( resolvedPath.split('/').filter(p => !!p), !resolvedAbsolute ).join('/') return (resolvedAbsolute ? '/' : '') + resolvedPath || '.' }, relative: (from, to) => { from = PATH_FS.resolve(from).substr(1) to = PATH_FS.resolve(to).substr(1) function trim(arr) { var start = 0 for (; start < arr.length; start++) { if (arr[start] !== '') break } var end = arr.length - 1 for (; end >= 0; end--) { if (arr[end] !== '') break } if (start > end) return [] return arr.slice(start, end - start + 1) } var fromParts = trim(from.split('/')) var toParts = trim(to.split('/')) var length = Math.min(fromParts.length, toParts.length) var samePartsLength = length for (var i = 0; i < length; i++) { if (fromParts[i] !== toParts[i]) { samePartsLength = i break } } var outputParts = [] for (var i = samePartsLength; i < fromParts.length; i++) { outputParts.push('..') } outputParts = outputParts.concat(toParts.slice(samePartsLength)) return outputParts.join('/') }, } function intArrayFromString(stringy, dontAddNull, length) { var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1 var u8array = new Array(len) var numBytesWritten = stringToUTF8Array( stringy, u8array, 0, u8array.length ) if (dontAddNull) u8array.length = numBytesWritten return u8array } var TTY = { ttys: [], init: function() {}, shutdown: function() {}, register: function(dev, ops) { TTY.ttys[dev] = { input: [], output: [], ops: ops } FS.registerDevice(dev, TTY.stream_ops) }, stream_ops: { open: function(stream) { var tty = TTY.ttys[stream.node.rdev] if (!tty) { throw new FS.ErrnoError(43) } stream.tty = tty stream.seekable = false }, close: function(stream) { stream.tty.ops.fsync(stream.tty) }, fsync: function(stream) { stream.tty.ops.fsync(stream.tty) }, read: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.get_char) { throw new FS.ErrnoError(60) } var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = stream.tty.ops.get_char(stream.tty) } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.put_char) { throw new FS.ErrnoError(60) } try { for (var i = 0; i < length; i++) { stream.tty.ops.put_char(stream.tty, buffer[offset + i]) } } catch (e) { throw new FS.ErrnoError(29) } if (length) { stream.node.timestamp = Date.now() } return i }, }, default_tty_ops: { get_char: function(tty) { if (!tty.input.length) { var result = null if (ENVIRONMENT_IS_NODE) { var BUFSIZE = 256 var buf = Buffer.alloc(BUFSIZE) var bytesRead = 0 try { bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE, -1) } catch (e) { if (e.toString().includes('EOF')) bytesRead = 0 else throw e } if (bytesRead > 0) { result = buf.slice(0, bytesRead).toString('utf-8') } else { result = null } } else if ( typeof window != 'undefined' && typeof window.prompt == 'function' ) { result = window.prompt('Input: ') if (result !== null) { result += '\n' } } else if (typeof readline == 'function') { result = readline() if (result !== null) { result += '\n' } } if (!result) { return null } tty.input = intArrayFromString(result, true) } return tty.input.shift() }, put_char: function(tty, val) { if (val === null || val === 10) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, default_tty1_ops: { put_char: function(tty, val) { if (val === null || val === 10) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, } function mmapAlloc(size) { abort() } var MEMFS = { ops_table: null, mount: function(mount) { return MEMFS.createNode(null, '/', 16384 | 511, 0) }, createNode: function(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { throw new FS.ErrnoError(63) } if (!MEMFS.ops_table) { MEMFS.ops_table = { dir: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, lookup: MEMFS.node_ops.lookup, mknod: MEMFS.node_ops.mknod, rename: MEMFS.node_ops.rename, unlink: MEMFS.node_ops.unlink, rmdir: MEMFS.node_ops.rmdir, readdir: MEMFS.node_ops.readdir, symlink: MEMFS.node_ops.symlink, }, stream: { llseek: MEMFS.stream_ops.llseek }, }, file: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: { llseek: MEMFS.stream_ops.llseek, read: MEMFS.stream_ops.read, write: MEMFS.stream_ops.write, allocate: MEMFS.stream_ops.allocate, mmap: MEMFS.stream_ops.mmap, msync: MEMFS.stream_ops.msync, }, }, link: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, readlink: MEMFS.node_ops.readlink, }, stream: {}, }, chrdev: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: FS.chrdev_stream_ops, }, } } var node = FS.createNode(parent, name, mode, dev) if (FS.isDir(node.mode)) { node.node_ops = MEMFS.ops_table.dir.node node.stream_ops = MEMFS.ops_table.dir.stream node.contents = {} } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node node.stream_ops = MEMFS.ops_table.file.stream node.usedBytes = 0 node.contents = null } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node node.stream_ops = MEMFS.ops_table.link.stream } else if (FS.isChrdev(node.mode)) { node.node_ops = MEMFS.ops_table.chrdev.node node.stream_ops = MEMFS.ops_table.chrdev.stream } node.timestamp = Date.now() if (parent) { parent.contents[name] = node parent.timestamp = node.timestamp } return node }, getFileDataAsTypedArray: function(node) { if (!node.contents) return new Uint8Array(0) if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes) return new Uint8Array(node.contents) }, expandFileStorage: function(node, newCapacity) { var prevCapacity = node.contents ? node.contents.length : 0 if (prevCapacity >= newCapacity) return var CAPACITY_DOUBLING_MAX = 1024 * 1024 newCapacity = Math.max( newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2 : 1.125)) >>> 0 ) if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256) var oldContents = node.contents node.contents = new Uint8Array(newCapacity) if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0) }, resizeFileStorage: function(node, newSize) { if (node.usedBytes == newSize) return if (newSize == 0) { node.contents = null node.usedBytes = 0 } else { var oldContents = node.contents node.contents = new Uint8Array(newSize) if (oldContents) { node.contents.set( oldContents.subarray(0, Math.min(newSize, node.usedBytes)) ) } node.usedBytes = newSize } }, node_ops: { getattr: function(node) { var attr = {} attr.dev = FS.isChrdev(node.mode) ? node.id : 1 attr.ino = node.id attr.mode = node.mode attr.nlink = 1 attr.uid = 0 attr.gid = 0 attr.rdev = node.rdev if (FS.isDir(node.mode)) { attr.size = 4096 } else if (FS.isFile(node.mode)) { attr.size = node.usedBytes } else if (FS.isLink(node.mode)) { attr.size = node.link.length } else { attr.size = 0 } attr.atime = new Date(node.timestamp) attr.mtime = new Date(node.timestamp) attr.ctime = new Date(node.timestamp) attr.blksize = 4096 attr.blocks = Math.ceil(attr.size / attr.blksize) return attr }, setattr: function(node, attr) { if (attr.mode !== undefined) { node.mode = attr.mode } if (attr.timestamp !== undefined) { node.timestamp = attr.timestamp } if (attr.size !== undefined) { MEMFS.resizeFileStorage(node, attr.size) } }, lookup: function(parent, name) { throw FS.genericErrors[44] }, mknod: function(parent, name, mode, dev) { return MEMFS.createNode(parent, name, mode, dev) }, rename: function(old_node, new_dir, new_name) { if (FS.isDir(old_node.mode)) { var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (new_node) { for (var i in new_node.contents) { throw new FS.ErrnoError(55) } } } delete old_node.parent.contents[old_node.name] old_node.parent.timestamp = Date.now() old_node.name = new_name new_dir.contents[new_name] = old_node new_dir.timestamp = old_node.parent.timestamp old_node.parent = new_dir }, unlink: function(parent, name) { delete parent.contents[name] parent.timestamp = Date.now() }, rmdir: function(parent, name) { var node = FS.lookupNode(parent, name) for (var i in node.contents) { throw new FS.ErrnoError(55) } delete parent.contents[name] parent.timestamp = Date.now() }, readdir: function(node) { var entries = ['.', '..'] for (var key in node.contents) { if (!node.contents.hasOwnProperty(key)) { continue } entries.push(key) } return entries }, symlink: function(parent, newname, oldpath) { var node = MEMFS.createNode(parent, newname, 511 | 40960, 0) node.link = oldpath return node }, readlink: function(node) { if (!FS.isLink(node.mode)) { throw new FS.ErrnoError(28) } return node.link }, }, stream_ops: { read: function(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= stream.node.usedBytes) return 0 var size = Math.min(stream.node.usedBytes - position, length) if (size > 8 && contents.subarray) { buffer.set(contents.subarray(position, position + size), offset) } else { for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i] } return size }, write: function(stream, buffer, offset, length, position, canOwn) { if (buffer.buffer === HEAP8.buffer) { canOwn = false } if (!length) return 0 var node = stream.node node.timestamp = Date.now() if (buffer.subarray && (!node.contents || node.contents.subarray)) { if (canOwn) { node.contents = buffer.subarray(offset, offset + length) node.usedBytes = length return length } else if (node.usedBytes === 0 && position === 0) { node.contents = buffer.slice(offset, offset + length) node.usedBytes = length return length } else if (position + length <= node.usedBytes) { node.contents.set( buffer.subarray(offset, offset + length), position ) return length } } MEMFS.expandFileStorage(node, position + length) if (node.contents.subarray && buffer.subarray) { node.contents.set( buffer.subarray(offset, offset + length), position ) } else { for (var i = 0; i < length; i++) { node.contents[position + i] = buffer[offset + i] } } node.usedBytes = Math.max(node.usedBytes, position + length) return length }, llseek: function(stream, offset, whence) { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { position += stream.node.usedBytes } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, allocate: function(stream, offset, length) { MEMFS.expandFileStorage(stream.node, offset + length) stream.node.usedBytes = Math.max( stream.node.usedBytes, offset + length ) }, mmap: function(stream, length, position, prot, flags) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr var allocated var contents = stream.node.contents if (!(flags & 2) && contents.buffer === buffer) { allocated = false ptr = contents.byteOffset } else { if (position > 0 || position + length < contents.length) { if (contents.subarray) { contents = contents.subarray(position, position + length) } else { contents = Array.prototype.slice.call( contents, position, position + length ) } } allocated = true ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } HEAP8.set(contents, ptr) } return { ptr: ptr, allocated: allocated } }, msync: function(stream, buffer, offset, length, mmapFlags) { MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } function asyncLoad(url, onload, onerror, noRunDep) { var dep = !noRunDep ? getUniqueRunDependency('al ' + url) : '' readAsync( url, arrayBuffer => { assert( arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).' ) onload(new Uint8Array(arrayBuffer)) if (dep) removeRunDependency(dep) }, event => { if (onerror) { onerror() } else { throw 'Loading data file "' + url + '" failed.' } } ) if (dep) addRunDependency(dep) } var ERRNO_CODES = {} var NODEFS = { isWindows: false, staticInit: () => { NODEFS.isWindows = !!process.platform.match(/^win/) var flags = process['binding']('constants') if (flags['fs']) { flags = flags['fs'] } NODEFS.flagsForNodeMap = { 1024: flags['O_APPEND'], 64: flags['O_CREAT'], 128: flags['O_EXCL'], 256: flags['O_NOCTTY'], 0: flags['O_RDONLY'], 2: flags['O_RDWR'], 4096: flags['O_SYNC'], 512: flags['O_TRUNC'], 1: flags['O_WRONLY'], 131072: flags['O_NOFOLLOW'], } }, convertNodeCode: e => { var code = e.code return ERRNO_CODES[code] }, mount: mount => { return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0) }, createNode: (parent, name, mode, dev) => { if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { throw new FS.ErrnoError(28) } var node = FS.createNode(parent, name, mode) node.node_ops = NODEFS.node_ops node.stream_ops = NODEFS.stream_ops return node }, getMode: path => { var stat try { stat = fs.lstatSync(path) if (NODEFS.isWindows) { stat.mode = stat.mode | ((stat.mode & 292) >> 2) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return stat.mode }, realPath: node => { var parts = [] while (node.parent !== node) { parts.push(node.name) node = node.parent } parts.push(node.mount.opts.root) parts.reverse() return PATH.join.apply(null, parts) }, flagsForNode: flags => { flags &= ~2097152 flags &= ~2048 flags &= ~32768 flags &= ~524288 flags &= ~65536 var newFlags = 0 for (var k in NODEFS.flagsForNodeMap) { if (flags & k) { newFlags |= NODEFS.flagsForNodeMap[k] flags ^= k } } if (flags) { throw new FS.ErrnoError(28) } return newFlags }, node_ops: { getattr: node => { var path = NODEFS.realPath(node) var stat try { stat = fs.lstatSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } if (NODEFS.isWindows && !stat.blksize) { stat.blksize = 4096 } if (NODEFS.isWindows && !stat.blocks) { stat.blocks = ((stat.size + stat.blksize - 1) / stat.blksize) | 0 } return { dev: stat.dev, ino: stat.ino, mode: stat.mode, nlink: stat.nlink, uid: stat.uid, gid: stat.gid, rdev: stat.rdev, size: stat.size, atime: stat.atime, mtime: stat.mtime, ctime: stat.ctime, blksize: stat.blksize, blocks: stat.blocks, } }, setattr: (node, attr) => { var path = NODEFS.realPath(node) try { if (attr.mode !== undefined) { fs.chmodSync(path, attr.mode) node.mode = attr.mode } if (attr.timestamp !== undefined) { var date = new Date(attr.timestamp) fs.utimesSync(path, date, date) } if (attr.size !== undefined) { fs.truncateSync(path, attr.size) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, lookup: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) var mode = NODEFS.getMode(path) return NODEFS.createNode(parent, name, mode) }, mknod: (parent, name, mode, dev) => { var node = NODEFS.createNode(parent, name, mode, dev) var path = NODEFS.realPath(node) try { if (FS.isDir(node.mode)) { fs.mkdirSync(path, node.mode) } else { fs.writeFileSync(path, '', { mode: node.mode }) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return node }, rename: (oldNode, newDir, newName) => { var oldPath = NODEFS.realPath(oldNode) var newPath = PATH.join2(NODEFS.realPath(newDir), newName) try { fs.renameSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } oldNode.name = newName }, unlink: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.unlinkSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, rmdir: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.rmdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readdir: node => { var path = NODEFS.realPath(node) try { return fs.readdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, symlink: (parent, newName, oldPath) => { var newPath = PATH.join2(NODEFS.realPath(parent), newName) try { fs.symlinkSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readlink: node => { var path = NODEFS.realPath(node) try { path = fs.readlinkSync(path) path = nodePath.relative( nodePath.resolve(node.mount.opts.root), path ) return path } catch (e) { if (!e.code) throw e if (e.code === 'UNKNOWN') throw new FS.ErrnoError(28) throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, }, stream_ops: { open: stream => { var path = NODEFS.realPath(stream.node) try { if (FS.isFile(stream.node.mode)) { stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags)) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, close: stream => { try { if (FS.isFile(stream.node.mode) && stream.nfd) { fs.closeSync(stream.nfd) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, read: (stream, buffer, offset, length, position) => { if (length === 0) return 0 try { return fs.readSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, write: (stream, buffer, offset, length, position) => { try { return fs.writeSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, llseek: (stream, offset, whence) => { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { try { var stat = fs.fstatSync(stream.nfd) position += stat.size } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, mmap: (stream, length, position, prot, flags) => { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr = mmapAlloc(length) NODEFS.stream_ops.read(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } }, msync: (stream, buffer, offset, length, mmapFlags) => { NODEFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } var FS = { root: null, mounts: [], devices: {}, streams: [], nextInode: 1, nameTable: null, currentPath: '/', initialized: false, ignorePermissions: true, ErrnoError: null, genericErrors: {}, filesystems: null, syncFSRequests: 0, lookupPath: (path, opts = {}) => { path = PATH_FS.resolve(path) if (!path) return { path: '', node: null } var defaults = { follow_mount: true, recurse_count: 0 } opts = Object.assign(defaults, opts) if (opts.recurse_count > 8) { throw new FS.ErrnoError(32) } var parts = path.split('/').filter(p => !!p) var current = FS.root var current_path = '/' for (var i = 0; i < parts.length; i++) { var islast = i === parts.length - 1 if (islast && opts.parent) { break } current = FS.lookupNode(current, parts[i]) current_path = PATH.join2(current_path, parts[i]) if (FS.isMountpoint(current)) { if (!islast || (islast && opts.follow_mount)) { current = current.mounted.root } } if (!islast || opts.follow) { var count = 0 while (FS.isLink(current.mode)) { var link = FS.readlink(current_path) current_path = PATH_FS.resolve(PATH.dirname(current_path), link) var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count + 1, }) current = lookup.node if (count++ > 40) { throw new FS.ErrnoError(32) } } } } return { path: current_path, node: current } }, getPath: node => { var path while (true) { if (FS.isRoot(node)) { var mount = node.mount.mountpoint if (!path) return mount return mount[mount.length - 1] !== '/' ? mount + '/' + path : mount + path } path = path ? node.name + '/' + path : node.name node = node.parent } }, hashName: (parentid, name) => { var hash = 0 for (var i = 0; i < name.length; i++) { hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0 } return ((parentid + hash) >>> 0) % FS.nameTable.length }, hashAddNode: node => { var hash = FS.hashName(node.parent.id, node.name) node.name_next = FS.nameTable[hash] FS.nameTable[hash] = node }, hashRemoveNode: node => { var hash = FS.hashName(node.parent.id, node.name) if (FS.nameTable[hash] === node) { FS.nameTable[hash] = node.name_next } else { var current = FS.nameTable[hash] while (current) { if (current.name_next === node) { current.name_next = node.name_next break } current = current.name_next } } }, lookupNode: (parent, name) => { var errCode = FS.mayLookup(parent) if (errCode) { throw new FS.ErrnoError(errCode, parent) } var hash = FS.hashName(parent.id, name) for (var node = FS.nameTable[hash]; node; node = node.name_next) { var nodeName = node.name if (node.parent.id === parent.id && nodeName === name) { return node } } return FS.lookup(parent, name) }, createNode: (parent, name, mode, rdev) => { var node = new FS.FSNode(parent, name, mode, rdev) FS.hashAddNode(node) return node }, destroyNode: node => { FS.hashRemoveNode(node) }, isRoot: node => { return node === node.parent }, isMountpoint: node => { return !!node.mounted }, isFile: mode => { return (mode & 61440) === 32768 }, isDir: mode => { return (mode & 61440) === 16384 }, isLink: mode => { return (mode & 61440) === 40960 }, isChrdev: mode => { return (mode & 61440) === 8192 }, isBlkdev: mode => { return (mode & 61440) === 24576 }, isFIFO: mode => { return (mode & 61440) === 4096 }, isSocket: mode => { return (mode & 49152) === 49152 }, flagModes: { r: 0, 'r+': 2, w: 577, 'w+': 578, a: 1089, 'a+': 1090 }, modeStringToFlags: str => { var flags = FS.flagModes[str] if (typeof flags == 'undefined') { throw new Error('Unknown file open mode: ' + str) } return flags }, flagsToPermissionString: flag => { var perms = ['r', 'w', 'rw'][flag & 3] if (flag & 512) { perms += 'w' } return perms }, nodePermissions: (node, perms) => { if (FS.ignorePermissions) { return 0 } if (perms.includes('r') && !(node.mode & 292)) { return 2 } else if (perms.includes('w') && !(node.mode & 146)) { return 2 } else if (perms.includes('x') && !(node.mode & 73)) { return 2 } return 0 }, mayLookup: dir => { var errCode = FS.nodePermissions(dir, 'x') if (errCode) return errCode if (!dir.node_ops.lookup) return 2 return 0 }, mayCreate: (dir, name) => { try { var node = FS.lookupNode(dir, name) return 20 } catch (e) {} return FS.nodePermissions(dir, 'wx') }, mayDelete: (dir, name, isdir) => { var node try { node = FS.lookupNode(dir, name) } catch (e) { return e.errno } var errCode = FS.nodePermissions(dir, 'wx') if (errCode) { return errCode } if (isdir) { if (!FS.isDir(node.mode)) { return 54 } if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return 10 } } else { if (FS.isDir(node.mode)) { return 31 } } return 0 }, mayOpen: (node, flags) => { if (!node) { return 44 } if (FS.isLink(node.mode)) { return 32 } else if (FS.isDir(node.mode)) { if (FS.flagsToPermissionString(flags) !== 'r' || flags & 512) { return 31 } } return FS.nodePermissions(node, FS.flagsToPermissionString(flags)) }, MAX_OPEN_FDS: 4096, nextfd: (fd_start = 0, fd_end = FS.MAX_OPEN_FDS) => { for (var fd = fd_start; fd <= fd_end; fd++) { if (!FS.streams[fd]) { return fd } } throw new FS.ErrnoError(33) }, getStream: fd => FS.streams[fd], createStream: (stream, fd_start, fd_end) => { if (!FS.FSStream) { FS.FSStream = function() { this.shared = {} } FS.FSStream.prototype = {} Object.defineProperties(FS.FSStream.prototype, { object: { get: function() { return this.node }, set: function(val) { this.node = val }, }, isRead: { get: function() { return (this.flags & 2097155) !== 1 }, }, isWrite: { get: function() { return (this.flags & 2097155) !== 0 }, }, isAppend: { get: function() { return this.flags & 1024 }, }, flags: { get: function() { return this.shared.flags }, set: function(val) { this.shared.flags = val }, }, position: { get: function() { return this.shared.position }, set: function(val) { this.shared.position = val }, }, }) } stream = Object.assign(new FS.FSStream(), stream) var fd = FS.nextfd(fd_start, fd_end) stream.fd = fd FS.streams[fd] = stream return stream }, closeStream: fd => { FS.streams[fd] = null }, chrdev_stream_ops: { open: stream => { var device = FS.getDevice(stream.node.rdev) stream.stream_ops = device.stream_ops if (stream.stream_ops.open) { stream.stream_ops.open(stream) } }, llseek: () => { throw new FS.ErrnoError(70) }, }, major: dev => dev >> 8, minor: dev => dev & 255, makedev: (ma, mi) => (ma << 8) | mi, registerDevice: (dev, ops) => { FS.devices[dev] = { stream_ops: ops } }, getDevice: dev => FS.devices[dev], getMounts: mount => { var mounts = [] var check = [mount] while (check.length) { var m = check.pop() mounts.push(m) check.push.apply(check, m.mounts) } return mounts }, syncfs: (populate, callback) => { if (typeof populate == 'function') { callback = populate populate = false } FS.syncFSRequests++ if (FS.syncFSRequests > 1) { err( 'warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work' ) } var mounts = FS.getMounts(FS.root.mount) var completed = 0 function doCallback(errCode) { FS.syncFSRequests-- return callback(errCode) } function done(errCode) { if (errCode) { if (!done.errored) { done.errored = true return doCallback(errCode) } return } if (++completed >= mounts.length) { doCallback(null) } } mounts.forEach(mount => { if (!mount.type.syncfs) { return done(null) } mount.type.syncfs(mount, populate, done) }) }, mount: (type, opts, mountpoint) => { var root = mountpoint === '/' var pseudo = !mountpoint var node if (root && FS.root) { throw new FS.ErrnoError(10) } else if (!root && !pseudo) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) mountpoint = lookup.path node = lookup.node if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } if (!FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } } var mount = { type: type, opts: opts, mountpoint: mountpoint, mounts: [], } var mountRoot = type.mount(mount) mountRoot.mount = mount mount.root = mountRoot if (root) { FS.root = mountRoot } else if (node) { node.mounted = mount if (node.mount) { node.mount.mounts.push(mount) } } return mountRoot }, unmount: mountpoint => { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) if (!FS.isMountpoint(lookup.node)) { throw new FS.ErrnoError(28) } var node = lookup.node var mount = node.mounted var mounts = FS.getMounts(mount) Object.keys(FS.nameTable).forEach(hash => { var current = FS.nameTable[hash] while (current) { var next = current.name_next if (mounts.includes(current.mount)) { FS.destroyNode(current) } current = next } }) node.mounted = null var idx = node.mount.mounts.indexOf(mount) node.mount.mounts.splice(idx, 1) }, lookup: (parent, name) => { return parent.node_ops.lookup(parent, name) }, mknod: (path, mode, dev) => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) if (!name || name === '.' || name === '..') { throw new FS.ErrnoError(28) } var errCode = FS.mayCreate(parent, name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.mknod) { throw new FS.ErrnoError(63) } return parent.node_ops.mknod(parent, name, mode, dev) }, create: (path, mode) => { mode = mode !== undefined ? mode : 438 mode &= 4095 mode |= 32768 return FS.mknod(path, mode, 0) }, mkdir: (path, mode) => { mode = mode !== undefined ? mode : 511 mode &= 511 | 512 mode |= 16384 return FS.mknod(path, mode, 0) }, mkdirTree: (path, mode) => { var dirs = path.split('/') var d = '' for (var i = 0; i < dirs.length; ++i) { if (!dirs[i]) continue d += '/' + dirs[i] try { FS.mkdir(d, mode) } catch (e) { if (e.errno != 20) throw e } } }, mkdev: (path, mode, dev) => { if (typeof dev == 'undefined') { dev = mode mode = 438 } mode |= 8192 return FS.mknod(path, mode, dev) }, symlink: (oldpath, newpath) => { if (!PATH_FS.resolve(oldpath)) { throw new FS.ErrnoError(44) } var lookup = FS.lookupPath(newpath, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var newname = PATH.basename(newpath) var errCode = FS.mayCreate(parent, newname) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.symlink) { throw new FS.ErrnoError(63) } return parent.node_ops.symlink(parent, newname, oldpath) }, rename: (old_path, new_path) => { var old_dirname = PATH.dirname(old_path) var new_dirname = PATH.dirname(new_path) var old_name = PATH.basename(old_path) var new_name = PATH.basename(new_path) var lookup, old_dir, new_dir lookup = FS.lookupPath(old_path, { parent: true }) old_dir = lookup.node lookup = FS.lookupPath(new_path, { parent: true }) new_dir = lookup.node if (!old_dir || !new_dir) throw new FS.ErrnoError(44) if (old_dir.mount !== new_dir.mount) { throw new FS.ErrnoError(75) } var old_node = FS.lookupNode(old_dir, old_name) var relative = PATH_FS.relative(old_path, new_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(28) } relative = PATH_FS.relative(new_path, old_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(55) } var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (old_node === new_node) { return } var isdir = FS.isDir(old_node.mode) var errCode = FS.mayDelete(old_dir, old_name, isdir) if (errCode) { throw new FS.ErrnoError(errCode) } errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!old_dir.node_ops.rename) { throw new FS.ErrnoError(63) } if ( FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node)) ) { throw new FS.ErrnoError(10) } if (new_dir !== old_dir) { errCode = FS.nodePermissions(old_dir, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } } FS.hashRemoveNode(old_node) try { old_dir.node_ops.rename(old_node, new_dir, new_name) } catch (e) { throw e } finally { FS.hashAddNode(old_node) } }, rmdir: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, true) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.rmdir) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.rmdir(parent, name) FS.destroyNode(node) }, readdir: path => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node if (!node.node_ops.readdir) { throw new FS.ErrnoError(54) } return node.node_ops.readdir(node) }, unlink: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, false) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.unlink) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.unlink(parent, name) FS.destroyNode(node) }, readlink: path => { var lookup = FS.lookupPath(path) var link = lookup.node if (!link) { throw new FS.ErrnoError(44) } if (!link.node_ops.readlink) { throw new FS.ErrnoError(28) } return PATH_FS.resolve( FS.getPath(link.parent), link.node_ops.readlink(link) ) }, stat: (path, dontFollow) => { var lookup = FS.lookupPath(path, { follow: !dontFollow }) var node = lookup.node if (!node) { throw new FS.ErrnoError(44) } if (!node.node_ops.getattr) { throw new FS.ErrnoError(63) } return node.node_ops.getattr(node) }, lstat: path => { return FS.stat(path, true) }, chmod: (path, mode, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { mode: (mode & 4095) | (node.mode & ~4095), timestamp: Date.now(), }) }, lchmod: (path, mode) => { FS.chmod(path, mode, true) }, fchmod: (fd, mode) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chmod(stream.node, mode) }, chown: (path, uid, gid, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { timestamp: Date.now() }) }, lchown: (path, uid, gid) => { FS.chown(path, uid, gid, true) }, fchown: (fd, uid, gid) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chown(stream.node, uid, gid) }, truncate: (path, len) => { if (len < 0) { throw new FS.ErrnoError(28) } var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: true }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } if (FS.isDir(node.mode)) { throw new FS.ErrnoError(31) } if (!FS.isFile(node.mode)) { throw new FS.ErrnoError(28) } var errCode = FS.nodePermissions(node, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } node.node_ops.setattr(node, { size: len, timestamp: Date.now() }) }, ftruncate: (fd, len) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(28) } FS.truncate(stream.node, len) }, utime: (path, atime, mtime) => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node node.node_ops.setattr(node, { timestamp: Math.max(atime, mtime) }) }, open: (path, flags, mode) => { if (path === '') { throw new FS.ErrnoError(44) } flags = typeof flags == 'string' ? FS.modeStringToFlags(flags) : flags mode = typeof mode == 'undefined' ? 438 : mode if (flags & 64) { mode = (mode & 4095) | 32768 } else { mode = 0 } var node if (typeof path == 'object') { node = path } else { path = PATH.normalize(path) try { var lookup = FS.lookupPath(path, { follow: !(flags & 131072) }) node = lookup.node } catch (e) {} } var created = false if (flags & 64) { if (node) { if (flags & 128) { throw new FS.ErrnoError(20) } } else { node = FS.mknod(path, mode, 0) created = true } } if (!node) { throw new FS.ErrnoError(44) } if (FS.isChrdev(node.mode)) { flags &= ~512 } if (flags & 65536 && !FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } if (!created) { var errCode = FS.mayOpen(node, flags) if (errCode) { throw new FS.ErrnoError(errCode) } } if (flags & 512 && !created) { FS.truncate(node, 0) } flags &= ~(128 | 512 | 131072) var stream = FS.createStream({ node: node, path: FS.getPath(node), flags: flags, seekable: true, position: 0, stream_ops: node.stream_ops, ungotten: [], error: false, }) if (stream.stream_ops.open) { stream.stream_ops.open(stream) } if (Module['logReadFiles'] && !(flags & 1)) { if (!FS.readFiles) FS.readFiles = {} if (!(path in FS.readFiles)) { FS.readFiles[path] = 1 } } return stream }, close: stream => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (stream.getdents) stream.getdents = null try { if (stream.stream_ops.close) { stream.stream_ops.close(stream) } } catch (e) { throw e } finally { FS.closeStream(stream.fd) } stream.fd = null }, isClosed: stream => { return stream.fd === null }, llseek: (stream, offset, whence) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (!stream.seekable || !stream.stream_ops.llseek) { throw new FS.ErrnoError(70) } if (whence != 0 && whence != 1 && whence != 2) { throw new FS.ErrnoError(28) } stream.position = stream.stream_ops.llseek(stream, offset, whence) stream.ungotten = [] return stream.position }, read: (stream, buffer, offset, length, position) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.read) { throw new FS.ErrnoError(28) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesRead = stream.stream_ops.read( stream, buffer, offset, length, position ) if (!seeking) stream.position += bytesRead return bytesRead }, write: (stream, buffer, offset, length, position, canOwn) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.write) { throw new FS.ErrnoError(28) } if (stream.seekable && stream.flags & 1024) { FS.llseek(stream, 0, 2) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesWritten = stream.stream_ops.write( stream, buffer, offset, length, position, canOwn ) if (!seeking) stream.position += bytesWritten return bytesWritten }, allocate: (stream, offset, length) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (offset < 0 || length <= 0) { throw new FS.ErrnoError(28) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(43) } if (!stream.stream_ops.allocate) { throw new FS.ErrnoError(138) } stream.stream_ops.allocate(stream, offset, length) }, mmap: (stream, length, position, prot, flags) => { if ( (prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2 ) { throw new FS.ErrnoError(2) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(2) } if (!stream.stream_ops.mmap) { throw new FS.ErrnoError(43) } return stream.stream_ops.mmap(stream, length, position, prot, flags) }, msync: (stream, buffer, offset, length, mmapFlags) => { if (!stream.stream_ops.msync) { return 0 } return stream.stream_ops.msync( stream, buffer, offset, length, mmapFlags ) }, munmap: stream => 0, ioctl: (stream, cmd, arg) => { if (!stream.stream_ops.ioctl) { throw new FS.ErrnoError(59) } return stream.stream_ops.ioctl(stream, cmd, arg) }, readFile: (path, opts = {}) => { opts.flags = opts.flags || 0 opts.encoding = opts.encoding || 'binary' if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { throw new Error('Invalid encoding type "' + opts.encoding + '"') } var ret var stream = FS.open(path, opts.flags) var stat = FS.stat(path) var length = stat.size var buf = new Uint8Array(length) FS.read(stream, buf, 0, length, 0) if (opts.encoding === 'utf8') { ret = UTF8ArrayToString(buf, 0) } else if (opts.encoding === 'binary') { ret = buf } FS.close(stream) return ret }, writeFile: (path, data, opts = {}) => { opts.flags = opts.flags || 577 var stream = FS.open(path, opts.flags, opts.mode) if (typeof data == 'string') { var buf = new Uint8Array(lengthBytesUTF8(data) + 1) var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length) FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn) } else if (ArrayBuffer.isView(data)) { FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn) } else { throw new Error('Unsupported data type') } FS.close(stream) }, cwd: () => FS.currentPath, chdir: path => { var lookup = FS.lookupPath(path, { follow: true }) if (lookup.node === null) { throw new FS.ErrnoError(44) } if (!FS.isDir(lookup.node.mode)) { throw new FS.ErrnoError(54) } var errCode = FS.nodePermissions(lookup.node, 'x') if (errCode) { throw new FS.ErrnoError(errCode) } FS.currentPath = lookup.path }, createDefaultDirectories: () => { FS.mkdir('/tmp') FS.mkdir('/home') FS.mkdir('/home/web_user') }, createDefaultDevices: () => { FS.mkdir('/dev') FS.registerDevice(FS.makedev(1, 3), { read: () => 0, write: (stream, buffer, offset, length, pos) => length, }) FS.mkdev('/dev/null', FS.makedev(1, 3)) TTY.register(FS.makedev(5, 0), TTY.default_tty_ops) TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops) FS.mkdev('/dev/tty', FS.makedev(5, 0)) FS.mkdev('/dev/tty1', FS.makedev(6, 0)) var random_device = getRandomDevice() FS.createDevice('/dev', 'random', random_device) FS.createDevice('/dev', 'urandom', random_device) FS.mkdir('/dev/shm') FS.mkdir('/dev/shm/tmp') }, createSpecialDirectories: () => { FS.mkdir('/proc') var proc_self = FS.mkdir('/proc/self') FS.mkdir('/proc/self/fd') FS.mount( { mount: () => { var node = FS.createNode(proc_self, 'fd', 16384 | 511, 73) node.node_ops = { lookup: (parent, name) => { var fd = +name var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) var ret = { parent: null, mount: { mountpoint: 'fake' }, node_ops: { readlink: () => stream.path }, } ret.parent = ret return ret }, } return node }, }, {}, '/proc/self/fd' ) }, createStandardStreams: () => { if (Module['stdin']) { FS.createDevice('/dev', 'stdin', Module['stdin']) } else { FS.symlink('/dev/tty', '/dev/stdin') } if (Module['stdout']) { FS.createDevice('/dev', 'stdout', null, Module['stdout']) } else { FS.symlink('/dev/tty', '/dev/stdout') } if (Module['stderr']) { FS.createDevice('/dev', 'stderr', null, Module['stderr']) } else { FS.symlink('/dev/tty1', '/dev/stderr') } var stdin = FS.open('/dev/stdin', 0) var stdout = FS.open('/dev/stdout', 1) var stderr = FS.open('/dev/stderr', 1) }, ensureErrnoError: () => { if (FS.ErrnoError) return FS.ErrnoError = function ErrnoError(errno, node) { this.node = node this.setErrno = function(errno) { this.errno = errno } this.setErrno(errno) this.message = 'FS error' } FS.ErrnoError.prototype = new Error() FS.ErrnoError.prototype.constructor = FS.ErrnoError ;[44].forEach(code => { FS.genericErrors[code] = new FS.ErrnoError(code) FS.genericErrors[code].stack = '' }) }, staticInit: () => { FS.ensureErrnoError() FS.nameTable = new Array(4096) FS.mount(MEMFS, {}, '/') FS.createDefaultDirectories() FS.createDefaultDevices() FS.createSpecialDirectories() FS.filesystems = { MEMFS: MEMFS, NODEFS: NODEFS } }, init: (input, output, error) => { FS.init.initialized = true FS.ensureErrnoError() Module['stdin'] = input || Module['stdin'] Module['stdout'] = output || Module['stdout'] Module['stderr'] = error || Module['stderr'] FS.createStandardStreams() }, quit: () => { FS.init.initialized = false for (var i = 0; i < FS.streams.length; i++) { var stream = FS.streams[i] if (!stream) { continue } FS.close(stream) } }, getMode: (canRead, canWrite) => { var mode = 0 if (canRead) mode |= 292 | 73 if (canWrite) mode |= 146 return mode }, findObject: (path, dontResolveLastLink) => { var ret = FS.analyzePath(path, dontResolveLastLink) if (!ret.exists) { return null } return ret.object }, analyzePath: (path, dontResolveLastLink) => { try { var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) path = lookup.path } catch (e) {} var ret = { isRoot: false, exists: false, error: 0, name: null, path: null, object: null, parentExists: false, parentPath: null, parentObject: null, } try { var lookup = FS.lookupPath(path, { parent: true }) ret.parentExists = true ret.parentPath = lookup.path ret.parentObject = lookup.node ret.name = PATH.basename(path) lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) ret.exists = true ret.path = lookup.path ret.object = lookup.node ret.name = lookup.node.name ret.isRoot = lookup.path === '/' } catch (e) { ret.error = e.errno } return ret }, createPath: (parent, path, canRead, canWrite) => { parent = typeof parent == 'string' ? parent : FS.getPath(parent) var parts = path.split('/').reverse() while (parts.length) { var part = parts.pop() if (!part) continue var current = PATH.join2(parent, part) try { FS.mkdir(current) } catch (e) {} parent = current } return current }, createFile: (parent, name, properties, canRead, canWrite) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(canRead, canWrite) return FS.create(path, mode) }, createDataFile: (parent, name, data, canRead, canWrite, canOwn) => { var path = name if (parent) { parent = typeof parent == 'string' ? parent : FS.getPath(parent) path = name ? PATH.join2(parent, name) : parent } var mode = FS.getMode(canRead, canWrite) var node = FS.create(path, mode) if (data) { if (typeof data == 'string') { var arr = new Array(data.length) for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i) data = arr } FS.chmod(node, mode | 146) var stream = FS.open(node, 577) FS.write(stream, data, 0, data.length, 0, canOwn) FS.close(stream) FS.chmod(node, mode) } return node }, createDevice: (parent, name, input, output) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(!!input, !!output) if (!FS.createDevice.major) FS.createDevice.major = 64 var dev = FS.makedev(FS.createDevice.major++, 0) FS.registerDevice(dev, { open: stream => { stream.seekable = false }, close: stream => { if (output && output.buffer && output.buffer.length) { output(10) } }, read: (stream, buffer, offset, length, pos) => { var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = input() } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: (stream, buffer, offset, length, pos) => { for (var i = 0; i < length; i++) { try { output(buffer[offset + i]) } catch (e) { throw new FS.ErrnoError(29) } } if (length) { stream.node.timestamp = Date.now() } return i }, }) return FS.mkdev(path, mode, dev) }, forceLoadFile: obj => { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true if (typeof XMLHttpRequest != 'undefined') { throw new Error( 'Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.' ) } else if (read_) { try { obj.contents = intArrayFromString(read_(obj.url), true) obj.usedBytes = obj.contents.length } catch (e) { throw new FS.ErrnoError(29) } } else { throw new Error('Cannot load without read() or XMLHttpRequest.') } }, createLazyFile: (parent, name, url, canRead, canWrite) => { function LazyUint8Array() { this.lengthKnown = false this.chunks = [] } LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) { if (idx > this.length - 1 || idx < 0) { return undefined } var chunkOffset = idx % this.chunkSize var chunkNum = (idx / this.chunkSize) | 0 return this.getter(chunkNum)[chunkOffset] } LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter( getter ) { this.getter = getter } LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { var xhr = new XMLHttpRequest() xhr.open('HEAD', url, false) xhr.send(null) if (!((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) throw new Error("Couldn't load " + url + '. Status: ' + xhr.status) var datalength = Number(xhr.getResponseHeader('Content-length')) var header var hasByteServing = (header = xhr.getResponseHeader('Accept-Ranges')) && header === 'bytes' var usesGzip = (header = xhr.getResponseHeader('Content-Encoding')) && header === 'gzip' var chunkSize = 1024 * 1024 if (!hasByteServing) chunkSize = datalength var doXHR = (from, to) => { if (from > to) throw new Error( 'invalid range (' + from + ', ' + to + ') or no bytes requested!' ) if (to > datalength - 1) throw new Error( 'only ' + datalength + ' bytes available! programmer error!' ) var xhr = new XMLHttpRequest() xhr.open('GET', url, false) if (datalength !== chunkSize) xhr.setRequestHeader('Range', 'bytes=' + from + '-' + to) xhr.responseType = 'arraybuffer' if (xhr.overrideMimeType) { xhr.overrideMimeType('text/plain; charset=x-user-defined') } xhr.send(null) if ( !((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ) throw new Error( "Couldn't load " + url + '. Status: ' + xhr.status ) if (xhr.response !== undefined) { return new Uint8Array(xhr.response || []) } return intArrayFromString(xhr.responseText || '', true) } var lazyArray = this lazyArray.setDataGetter(chunkNum => { var start = chunkNum * chunkSize var end = (chunkNum + 1) * chunkSize - 1 end = Math.min(end, datalength - 1) if (typeof lazyArray.chunks[chunkNum] == 'undefined') { lazyArray.chunks[chunkNum] = doXHR(start, end) } if (typeof lazyArray.chunks[chunkNum] == 'undefined') throw new Error('doXHR failed!') return lazyArray.chunks[chunkNum] }) if (usesGzip || !datalength) { chunkSize = datalength = 1 datalength = this.getter(0).length chunkSize = datalength out( 'LazyFiles on gzip forces download of the whole file when length is accessed' ) } this._length = datalength this._chunkSize = chunkSize this.lengthKnown = true } if (typeof XMLHttpRequest != 'undefined') { if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc' var lazyArray = new LazyUint8Array() Object.defineProperties(lazyArray, { length: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._length }, }, chunkSize: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._chunkSize }, }, }) var properties = { isDevice: false, contents: lazyArray } } else { var properties = { isDevice: false, url: url } } var node = FS.createFile(parent, name, properties, canRead, canWrite) if (properties.contents) { node.contents = properties.contents } else if (properties.url) { node.contents = null node.url = properties.url } Object.defineProperties(node, { usedBytes: { get: function() { return this.contents.length }, }, }) var stream_ops = {} var keys = Object.keys(node.stream_ops) keys.forEach(key => { var fn = node.stream_ops[key] stream_ops[key] = function forceLoadLazyFile() { FS.forceLoadFile(node) return fn.apply(null, arguments) } }) function writeChunks(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= contents.length) return 0 var size = Math.min(contents.length - position, length) if (contents.slice) { for (var i = 0; i < size; i++) { buffer[offset + i] = contents[position + i] } } else { for (var i = 0; i < size; i++) { buffer[offset + i] = contents.get(position + i) } } return size } stream_ops.read = (stream, buffer, offset, length, position) => { FS.forceLoadFile(node) return writeChunks(stream, buffer, offset, length, position) } stream_ops.mmap = (stream, length, position, prot, flags) => { FS.forceLoadFile(node) var ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } writeChunks(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } } node.stream_ops = stream_ops return node }, createPreloadedFile: ( parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish ) => { var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent var dep = getUniqueRunDependency('cp ' + fullname) function processData(byteArray) { function finish(byteArray) { if (preFinish) preFinish() if (!dontCreateFile) { FS.createDataFile( parent, name, byteArray, canRead, canWrite, canOwn ) } if (onload) onload() removeRunDependency(dep) } if ( Browser.handledByPreloadPlugin(byteArray, fullname, finish, () => { if (onerror) onerror() removeRunDependency(dep) }) ) { return } finish(byteArray) } addRunDependency(dep) if (typeof url == 'string') { asyncLoad(url, byteArray => processData(byteArray), onerror) } else { processData(url) } }, indexedDB: () => { return ( window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB ) }, DB_NAME: () => { return 'EM_FS_' + window.location.pathname }, DB_VERSION: 20, DB_STORE_NAME: 'FILE_DATA', saveFilesToDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = () => { out('creating db') var db = openRequest.result db.createObjectStore(FS.DB_STORE_NAME) } openRequest.onsuccess = () => { var db = openRequest.result var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite') var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var putRequest = files.put( FS.analyzePath(path).object.contents, path ) putRequest.onsuccess = () => { ok++ if (ok + fail == total) finish() } putRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, loadFilesFromDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = onerror openRequest.onsuccess = () => { var db = openRequest.result try { var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly') } catch (e) { onerror(e) return } var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var getRequest = files.get(path) getRequest.onsuccess = () => { if (FS.analyzePath(path).exists) { FS.unlink(path) } FS.createDataFile( PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true ) ok++ if (ok + fail == total) finish() } getRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, } var SYSCALLS = { DEFAULT_POLLMASK: 5, calculateAt: function(dirfd, path, allowEmpty) { if (PATH.isAbs(path)) { return path } var dir if (dirfd === -100) { dir = FS.cwd() } else { var dirstream = SYSCALLS.getStreamFromFD(dirfd) dir = dirstream.path } if (path.length == 0) { if (!allowEmpty) { throw new FS.ErrnoError(44) } return dir } return PATH.join2(dir, path) }, doStat: function(func, path, buf) { try { var stat = func(path) } catch (e) { if ( e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node)) ) { return -54 } throw e } HEAP32[buf >> 2] = stat.dev HEAP32[(buf + 8) >> 2] = stat.ino HEAP32[(buf + 12) >> 2] = stat.mode HEAPU32[(buf + 16) >> 2] = stat.nlink HEAP32[(buf + 20) >> 2] = stat.uid HEAP32[(buf + 24) >> 2] = stat.gid HEAP32[(buf + 28) >> 2] = stat.rdev ;(tempI64 = [ stat.size >>> 0, ((tempDouble = stat.size), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 40) >> 2] = tempI64[0]), (HEAP32[(buf + 44) >> 2] = tempI64[1]) HEAP32[(buf + 48) >> 2] = 4096 HEAP32[(buf + 52) >> 2] = stat.blocks var atime = stat.atime.getTime() var mtime = stat.mtime.getTime() var ctime = stat.ctime.getTime() ;(tempI64 = [ Math.floor(atime / 1e3) >>> 0, ((tempDouble = Math.floor(atime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 56) >> 2] = tempI64[0]), (HEAP32[(buf + 60) >> 2] = tempI64[1]) HEAPU32[(buf + 64) >> 2] = (atime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(mtime / 1e3) >>> 0, ((tempDouble = Math.floor(mtime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 72) >> 2] = tempI64[0]), (HEAP32[(buf + 76) >> 2] = tempI64[1]) HEAPU32[(buf + 80) >> 2] = (mtime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(ctime / 1e3) >>> 0, ((tempDouble = Math.floor(ctime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 88) >> 2] = tempI64[0]), (HEAP32[(buf + 92) >> 2] = tempI64[1]) HEAPU32[(buf + 96) >> 2] = (ctime % 1e3) * 1e3 ;(tempI64 = [ stat.ino >>> 0, ((tempDouble = stat.ino), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 104) >> 2] = tempI64[0]), (HEAP32[(buf + 108) >> 2] = tempI64[1]) return 0 }, doMsync: function(addr, stream, len, flags, offset) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } if (flags & 2) { return 0 } var buffer = HEAPU8.slice(addr, addr + len) FS.msync(stream, buffer, offset, len, flags) }, varargs: undefined, get: function() { SYSCALLS.varargs += 4 var ret = HEAP32[(SYSCALLS.varargs - 4) >> 2] return ret }, getStr: function(ptr) { var ret = UTF8ToString(ptr) return ret }, getStreamFromFD: function(fd) { var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) return stream }, } function ___syscall_fcntl64(fd, cmd, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (cmd) { case 0: { var arg = SYSCALLS.get() if (arg < 0) { return -28 } var newStream newStream = FS.createStream(stream, arg) return newStream.fd } case 1: case 2: return 0 case 3: return stream.flags case 4: { var arg = SYSCALLS.get() stream.flags |= arg return 0 } case 5: { var arg = SYSCALLS.get() var offset = 0 HEAP16[(arg + offset) >> 1] = 2 return 0 } case 6: case 7: return 0 case 16: case 8: return -28 case 9: setErrNo(28) return -1 default: { return -28 } } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_getcwd(buf, size) { try { if (size === 0) return -28 var cwd = FS.cwd() var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1 if (size < cwdLengthInBytes) return -68 stringToUTF8(cwd, buf, size) return cwdLengthInBytes } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_ioctl(fd, op, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (op) { case 21509: case 21505: { if (!stream.tty) return -59 return 0 } case 21510: case 21511: case 21512: case 21506: case 21507: case 21508: { if (!stream.tty) return -59 return 0 } case 21519: { if (!stream.tty) return -59 var argp = SYSCALLS.get() HEAP32[argp >> 2] = 0 return 0 } case 21520: { if (!stream.tty) return -59 return -28 } case 21531: { var argp = SYSCALLS.get() return FS.ioctl(stream, op, argp) } case 21523: { if (!stream.tty) return -59 return 0 } case 21524: { if (!stream.tty) return -59 return 0 } default: return -28 } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_openat(dirfd, path, flags, varargs) { SYSCALLS.varargs = varargs try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) var mode = varargs ? SYSCALLS.get() : 0 return FS.open(path, flags, mode).fd } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_readlinkat(dirfd, path, buf, bufsize) { try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) if (bufsize <= 0) return -28 var ret = FS.readlink(path) var len = Math.min(bufsize, lengthBytesUTF8(ret)) var endChar = HEAP8[buf + len] stringToUTF8(ret, buf, bufsize + 1) HEAP8[buf + len] = endChar return len } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_stat64(path, buf) { try { path = SYSCALLS.getStr(path) return SYSCALLS.doStat(FS.stat, path, buf) } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function _abort() { abort('') } function _emscripten_memcpy_big(dest, src, num) { HEAPU8.copyWithin(dest, src, src + num) } function getHeapMax() { return 2147483648 } function emscripten_realloc_buffer(size) { try { wasmMemory.grow((size - buffer.byteLength + 65535) >>> 16) updateGlobalBufferAndViews(wasmMemory.buffer) return 1 } catch (e) {} } function _emscripten_resize_heap(requestedSize) { var oldSize = HEAPU8.length requestedSize = requestedSize >>> 0 var maxHeapSize = getHeapMax() if (requestedSize > maxHeapSize) { return false } let alignUp = (x, multiple) => x + ((multiple - (x % multiple)) % multiple) for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown) overGrownHeapSize = Math.min( overGrownHeapSize, requestedSize + 100663296 ) var newSize = Math.min( maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536) ) var replacement = emscripten_realloc_buffer(newSize) if (replacement) { return true } } return false } var ENV = {} function getExecutableName() { return thisProgram || './this.program' } function getEnvStrings() { if (!getEnvStrings.strings) { var lang = ( (typeof navigator == 'object' && navigator.languages && navigator.languages[0]) || 'C' ).replace('-', '_') + '.UTF-8' var env = { USER: 'web_user', LOGNAME: 'web_user', PATH: '/', PWD: '/', HOME: '/home/web_user', LANG: lang, _: getExecutableName(), } for (var x in ENV) { if (ENV[x] === undefined) delete env[x] else env[x] = ENV[x] } var strings = [] for (var x in env) { strings.push(x + '=' + env[x]) } getEnvStrings.strings = strings } return getEnvStrings.strings } function writeAsciiToMemory(str, buffer, dontAddNull) { for (var i = 0; i < str.length; ++i) { HEAP8[buffer++ >> 0] = str.charCodeAt(i) } if (!dontAddNull) HEAP8[buffer >> 0] = 0 } function _environ_get(__environ, environ_buf) { var bufSize = 0 getEnvStrings().forEach(function(string, i) { var ptr = environ_buf + bufSize HEAPU32[(__environ + i * 4) >> 2] = ptr writeAsciiToMemory(string, ptr) bufSize += string.length + 1 }) return 0 } function _environ_sizes_get(penviron_count, penviron_buf_size) { var strings = getEnvStrings() HEAPU32[penviron_count >> 2] = strings.length var bufSize = 0 strings.forEach(function(string) { bufSize += string.length + 1 }) HEAPU32[penviron_buf_size >> 2] = bufSize return 0 } function _proc_exit(code) { EXITSTATUS = code if (!keepRuntimeAlive()) { if (Module['onExit']) Module['onExit'](code) ABORT = true } quit_(code, new ExitStatus(code)) } function exitJS(status, implicit) { EXITSTATUS = status _proc_exit(status) } var _exit = exitJS function _fd_close(fd) { try { var stream = SYSCALLS.getStreamFromFD(fd) FS.close(stream) return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doReadv(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.read(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr if (curr < len) break } return ret } function _fd_read(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doReadv(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function convertI32PairToI53Checked(lo, hi) { return (hi + 2097152) >>> 0 < 4194305 - !!lo ? (lo >>> 0) + hi * 4294967296 : NaN } function _fd_seek(fd, offset_low, offset_high, whence, newOffset) { try { var offset = convertI32PairToI53Checked(offset_low, offset_high) if (isNaN(offset)) return 61 var stream = SYSCALLS.getStreamFromFD(fd) FS.llseek(stream, offset, whence) ;(tempI64 = [ stream.position >>> 0, ((tempDouble = stream.position), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[newOffset >> 2] = tempI64[0]), (HEAP32[(newOffset + 4) >> 2] = tempI64[1]) if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doWritev(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.write(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr } return ret } function _fd_write(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doWritev(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function __isLeapYear(year) { return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) } function __arraySum(array, index) { var sum = 0 for (var i = 0; i <= index; sum += array[i++]) {} return sum } var __MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] var __MONTH_DAYS_REGULAR = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] function __addDays(date, days) { var newDate = new Date(date.getTime()) while (days > 0) { var leap = __isLeapYear(newDate.getFullYear()) var currentMonth = newDate.getMonth() var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth] if (days > daysInCurrentMonth - newDate.getDate()) { days -= daysInCurrentMonth - newDate.getDate() + 1 newDate.setDate(1) if (currentMonth < 11) { newDate.setMonth(currentMonth + 1) } else { newDate.setMonth(0) newDate.setFullYear(newDate.getFullYear() + 1) } } else { newDate.setDate(newDate.getDate() + days) return newDate } } return newDate } function writeArrayToMemory(array, buffer) { HEAP8.set(array, buffer) } function _strftime(s, maxsize, format, tm) { var tm_zone = HEAP32[(tm + 40) >> 2] var date = { tm_sec: HEAP32[tm >> 2], tm_min: HEAP32[(tm + 4) >> 2], tm_hour: HEAP32[(tm + 8) >> 2], tm_mday: HEAP32[(tm + 12) >> 2], tm_mon: HEAP32[(tm + 16) >> 2], tm_year: HEAP32[(tm + 20) >> 2], tm_wday: HEAP32[(tm + 24) >> 2], tm_yday: HEAP32[(tm + 28) >> 2], tm_isdst: HEAP32[(tm + 32) >> 2], tm_gmtoff: HEAP32[(tm + 36) >> 2], tm_zone: tm_zone ? UTF8ToString(tm_zone) : '', } var pattern = UTF8ToString(format) var EXPANSION_RULES_1 = { '%c': '%a %b %d %H:%M:%S %Y', '%D': '%m/%d/%y', '%F': '%Y-%m-%d', '%h': '%b', '%r': '%I:%M:%S %p', '%R': '%H:%M', '%T': '%H:%M:%S', '%x': '%m/%d/%y', '%X': '%H:%M:%S', '%Ec': '%c', '%EC': '%C', '%Ex': '%m/%d/%y', '%EX': '%H:%M:%S', '%Ey': '%y', '%EY': '%Y', '%Od': '%d', '%Oe': '%e', '%OH': '%H', '%OI': '%I', '%Om': '%m', '%OM': '%M', '%OS': '%S', '%Ou': '%u', '%OU': '%U', '%OV': '%V', '%Ow': '%w', '%OW': '%W', '%Oy': '%y', } for (var rule in EXPANSION_RULES_1) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_1[rule] ) } var WEEKDAYS = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', ] var MONTHS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ] function leadingSomething(value, digits, character) { var str = typeof value == 'number' ? value.toString() : value || '' while (str.length < digits) { str = character[0] + str } return str } function leadingNulls(value, digits) { return leadingSomething(value, digits, '0') } function compareByDay(date1, date2) { function sgn(value) { return value < 0 ? -1 : value > 0 ? 1 : 0 } var compare if ((compare = sgn(date1.getFullYear() - date2.getFullYear())) === 0) { if ((compare = sgn(date1.getMonth() - date2.getMonth())) === 0) { compare = sgn(date1.getDate() - date2.getDate()) } } return compare } function getFirstWeekStartDate(janFourth) { switch (janFourth.getDay()) { case 0: return new Date(janFourth.getFullYear() - 1, 11, 29) case 1: return janFourth case 2: return new Date(janFourth.getFullYear(), 0, 3) case 3: return new Date(janFourth.getFullYear(), 0, 2) case 4: return new Date(janFourth.getFullYear(), 0, 1) case 5: return new Date(janFourth.getFullYear() - 1, 11, 31) case 6: return new Date(janFourth.getFullYear() - 1, 11, 30) } } function getWeekBasedYear(date) { var thisDate = __addDays( new Date(date.tm_year + 1900, 0, 1), date.tm_yday ) var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4) var janFourthNextYear = new Date(thisDate.getFullYear() + 1, 0, 4) var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear) var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear) if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { return thisDate.getFullYear() + 1 } return thisDate.getFullYear() } return thisDate.getFullYear() - 1 } var EXPANSION_RULES_2 = { '%a': function(date) { return WEEKDAYS[date.tm_wday].substring(0, 3) }, '%A': function(date) { return WEEKDAYS[date.tm_wday] }, '%b': function(date) { return MONTHS[date.tm_mon].substring(0, 3) }, '%B': function(date) { return MONTHS[date.tm_mon] }, '%C': function(date) { var year = date.tm_year + 1900 return leadingNulls((year / 100) | 0, 2) }, '%d': function(date) { return leadingNulls(date.tm_mday, 2) }, '%e': function(date) { return leadingSomething(date.tm_mday, 2, ' ') }, '%g': function(date) { return getWeekBasedYear(date) .toString() .substring(2) }, '%G': function(date) { return getWeekBasedYear(date) }, '%H': function(date) { return leadingNulls(date.tm_hour, 2) }, '%I': function(date) { var twelveHour = date.tm_hour if (twelveHour == 0) twelveHour = 12 else if (twelveHour > 12) twelveHour -= 12 return leadingNulls(twelveHour, 2) }, '%j': function(date) { return leadingNulls( date.tm_mday + __arraySum( __isLeapYear(date.tm_year + 1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon - 1 ), 3 ) }, '%m': function(date) { return leadingNulls(date.tm_mon + 1, 2) }, '%M': function(date) { return leadingNulls(date.tm_min, 2) }, '%n': function() { return '\n' }, '%p': function(date) { if (date.tm_hour >= 0 && date.tm_hour < 12) { return 'AM' } return 'PM' }, '%S': function(date) { return leadingNulls(date.tm_sec, 2) }, '%t': function() { return '\t' }, '%u': function(date) { return date.tm_wday || 7 }, '%U': function(date) { var days = date.tm_yday + 7 - date.tm_wday return leadingNulls(Math.floor(days / 7), 2) }, '%V': function(date) { var val = Math.floor( (date.tm_yday + 7 - ((date.tm_wday + 6) % 7)) / 7 ) if ((date.tm_wday + 371 - date.tm_yday - 2) % 7 <= 2) { val++ } if (!val) { val = 52 var dec31 = (date.tm_wday + 7 - date.tm_yday - 1) % 7 if ( dec31 == 4 || (dec31 == 5 && __isLeapYear((date.tm_year % 400) - 1)) ) { val++ } } else if (val == 53) { var jan1 = (date.tm_wday + 371 - date.tm_yday) % 7 if (jan1 != 4 && (jan1 != 3 || !__isLeapYear(date.tm_year))) val = 1 } return leadingNulls(val, 2) }, '%w': function(date) { return date.tm_wday }, '%W': function(date) { var days = date.tm_yday + 7 - ((date.tm_wday + 6) % 7) return leadingNulls(Math.floor(days / 7), 2) }, '%y': function(date) { return (date.tm_year + 1900).toString().substring(2) }, '%Y': function(date) { return date.tm_year + 1900 }, '%z': function(date) { var off = date.tm_gmtoff var ahead = off >= 0 off = Math.abs(off) / 60 off = (off / 60) * 100 + (off % 60) return (ahead ? '+' : '-') + String('0000' + off).slice(-4) }, '%Z': function(date) { return date.tm_zone }, '%%': function() { return '%' }, } pattern = pattern.replace(/%%/g, '\0\0') for (var rule in EXPANSION_RULES_2) { if (pattern.includes(rule)) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date) ) } } pattern = pattern.replace(/\0\0/g, '%') var bytes = intArrayFromString(pattern, false) if (bytes.length > maxsize) { return 0 } writeArrayToMemory(bytes, s) return bytes.length - 1 } function _strftime_l(s, maxsize, format, tm, loc) { return _strftime(s, maxsize, format, tm) } function handleException(e) { if (e instanceof ExitStatus || e == 'unwind') { return EXITSTATUS } quit_(1, e) } function allocateUTF8OnStack(str) { var size = lengthBytesUTF8(str) + 1 var ret = stackAlloc(size) stringToUTF8Array(str, HEAP8, ret, size) return ret } function getCFunc(ident) { var func = Module['_' + ident] return func } function ccall(ident, returnType, argTypes, args, opts) { var toC = { string: str => { var ret = 0 if (str !== null && str !== undefined && str !== 0) { var len = (str.length << 2) + 1 ret = stackAlloc(len) stringToUTF8(str, ret, len) } return ret }, array: arr => { var ret = stackAlloc(arr.length) writeArrayToMemory(arr, ret) return ret }, } function convertReturnValue(ret) { if (returnType === 'string') { return UTF8ToString(ret) } if (returnType === 'boolean') return Boolean(ret) return ret } var func = getCFunc(ident) var cArgs = [] var stack = 0 if (args) { for (var i = 0; i < args.length; i++) { var converter = toC[argTypes[i]] if (converter) { if (stack === 0) stack = stackSave() cArgs[i] = converter(args[i]) } else { cArgs[i] = args[i] } } } var ret = func.apply(null, cArgs) function onDone(ret) { if (stack !== 0) stackRestore(stack) return convertReturnValue(ret) } ret = onDone(ret) return ret } function cwrap(ident, returnType, argTypes, opts) { argTypes = argTypes || [] var numericArgs = argTypes.every( type => type === 'number' || type === 'boolean' ) var numericRet = returnType !== 'string' if (numericRet && numericArgs && !opts) { return getCFunc(ident) } return function() { return ccall(ident, returnType, argTypes, arguments, opts) } } function AsciiToString(ptr) { var str = '' while (1) { var ch = HEAPU8[ptr++ >> 0] if (!ch) return str str += String.fromCharCode(ch) } } var FSNode = function(parent, name, mode, rdev) { if (!parent) { parent = this } this.parent = parent this.mount = parent.mount this.mounted = null this.id = FS.nextInode++ this.name = name this.mode = mode this.node_ops = {} this.stream_ops = {} this.rdev = rdev } var readMode = 292 | 73 var writeMode = 146 Object.defineProperties(FSNode.prototype, { read: { get: function() { return (this.mode & readMode) === readMode }, set: function(val) { val ? (this.mode |= readMode) : (this.mode &= ~readMode) }, }, write: { get: function() { return (this.mode & writeMode) === writeMode }, set: function(val) { val ? (this.mode |= writeMode) : (this.mode &= ~writeMode) }, }, isFolder: { get: function() { return FS.isDir(this.mode) }, }, isDevice: { get: function() { return FS.isChrdev(this.mode) }, }, }) FS.FSNode = FSNode FS.staticInit() Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_unlink'] = FS.unlink Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice if (ENVIRONMENT_IS_NODE) { NODEFS.staticInit() } ERRNO_CODES = { EPERM: 63, ENOENT: 44, ESRCH: 71, EINTR: 27, EIO: 29, ENXIO: 60, E2BIG: 1, ENOEXEC: 45, EBADF: 8, ECHILD: 12, EAGAIN: 6, EWOULDBLOCK: 6, ENOMEM: 48, EACCES: 2, EFAULT: 21, ENOTBLK: 105, EBUSY: 10, EEXIST: 20, EXDEV: 75, ENODEV: 43, ENOTDIR: 54, EISDIR: 31, EINVAL: 28, ENFILE: 41, EMFILE: 33, ENOTTY: 59, ETXTBSY: 74, EFBIG: 22, ENOSPC: 51, ESPIPE: 70, EROFS: 69, EMLINK: 34, EPIPE: 64, EDOM: 18, ERANGE: 68, ENOMSG: 49, EIDRM: 24, ECHRNG: 106, EL2NSYNC: 156, EL3HLT: 107, EL3RST: 108, ELNRNG: 109, EUNATCH: 110, ENOCSI: 111, EL2HLT: 112, EDEADLK: 16, ENOLCK: 46, EBADE: 113, EBADR: 114, EXFULL: 115, ENOANO: 104, EBADRQC: 103, EBADSLT: 102, EDEADLOCK: 16, EBFONT: 101, ENOSTR: 100, ENODATA: 116, ETIME: 117, ENOSR: 118, ENONET: 119, ENOPKG: 120, EREMOTE: 121, ENOLINK: 47, EADV: 122, ESRMNT: 123, ECOMM: 124, EPROTO: 65, EMULTIHOP: 36, EDOTDOT: 125, EBADMSG: 9, ENOTUNIQ: 126, EBADFD: 127, EREMCHG: 128, ELIBACC: 129, ELIBBAD: 130, ELIBSCN: 131, ELIBMAX: 132, ELIBEXEC: 133, ENOSYS: 52, ENOTEMPTY: 55, ENAMETOOLONG: 37, ELOOP: 32, EOPNOTSUPP: 138, EPFNOSUPPORT: 139, ECONNRESET: 15, ENOBUFS: 42, EAFNOSUPPORT: 5, EPROTOTYPE: 67, ENOTSOCK: 57, ENOPROTOOPT: 50, ESHUTDOWN: 140, ECONNREFUSED: 14, EADDRINUSE: 3, ECONNABORTED: 13, ENETUNREACH: 40, ENETDOWN: 38, ETIMEDOUT: 73, EHOSTDOWN: 142, EHOSTUNREACH: 23, EINPROGRESS: 26, EALREADY: 7, EDESTADDRREQ: 17, EMSGSIZE: 35, EPROTONOSUPPORT: 66, ESOCKTNOSUPPORT: 137, EADDRNOTAVAIL: 4, ENETRESET: 39, EISCONN: 30, ENOTCONN: 53, ETOOMANYREFS: 141, EUSERS: 136, EDQUOT: 19, ESTALE: 72, ENOTSUP: 138, ENOMEDIUM: 148, EILSEQ: 25, EOVERFLOW: 61, ECANCELED: 11, ENOTRECOVERABLE: 56, EOWNERDEAD: 62, ESTRPIPE: 135, } var asmLibraryArg = { a: ___cxa_throw, d: ___syscall_fcntl64, r: ___syscall_getcwd, i: ___syscall_ioctl, j: ___syscall_openat, n: ___syscall_readlinkat, o: ___syscall_stat64, b: _abort, f: _emscripten_memcpy_big, m: _emscripten_resize_heap, p: _environ_get, q: _environ_sizes_get, c: _exit, e: _fd_close, h: _fd_read, k: _fd_seek, g: _fd_write, l: _strftime_l, } var asm = createWasm() var ___wasm_call_ctors = (Module['___wasm_call_ctors'] = function() { return (___wasm_call_ctors = Module['___wasm_call_ctors'] = Module['asm']['t']).apply(null, arguments) }) var _main = (Module['_main'] = function() { return (_main = Module['_main'] = Module['asm']['u']).apply( null, arguments ) }) var ___errno_location = (Module['___errno_location'] = function() { return (___errno_location = Module['___errno_location'] = Module['asm']['v']).apply(null, arguments) }) var _itk_wasm_input_array_alloc = (Module[ '_itk_wasm_input_array_alloc' ] = function() { return (_itk_wasm_input_array_alloc = Module[ '_itk_wasm_input_array_alloc' ] = Module['asm']['w']).apply(null, arguments) }) var _itk_wasm_input_json_alloc = (Module[ '_itk_wasm_input_json_alloc' ] = function() { return (_itk_wasm_input_json_alloc = Module[ '_itk_wasm_input_json_alloc' ] = Module['asm']['x']).apply(null, arguments) }) var _itk_wasm_output_json_address = (Module[ '_itk_wasm_output_json_address' ] = function() { return (_itk_wasm_output_json_address = Module[ '_itk_wasm_output_json_address' ] = Module['asm']['y']).apply(null, arguments) }) var _itk_wasm_output_json_size = (Module[ '_itk_wasm_output_json_size' ] = function() { return (_itk_wasm_output_json_size = Module[ '_itk_wasm_output_json_size' ] = Module['asm']['z']).apply(null, arguments) }) var _itk_wasm_output_array_address = (Module[ '_itk_wasm_output_array_address' ] = function() { return (_itk_wasm_output_array_address = Module[ '_itk_wasm_output_array_address' ] = Module['asm']['A']).apply(null, arguments) }) var _itk_wasm_output_array_size = (Module[ '_itk_wasm_output_array_size' ] = function() { return (_itk_wasm_output_array_size = Module[ '_itk_wasm_output_array_size' ] = Module['asm']['B']).apply(null, arguments) }) var _itk_wasm_free_all = (Module['_itk_wasm_free_all'] = function() { return (_itk_wasm_free_all = Module['_itk_wasm_free_all'] = Module['asm']['C']).apply(null, arguments) }) var stackSave = (Module['stackSave'] = function() { return (stackSave = Module['stackSave'] = Module['asm']['E']).apply( null, arguments ) }) var stackRestore = (Module['stackRestore'] = function() { return (stackRestore = Module['stackRestore'] = Module['asm']['F']).apply( null, arguments ) }) var stackAlloc = (Module['stackAlloc'] = function() { return (stackAlloc = Module['stackAlloc'] = Module['asm']['G']).apply( null, arguments ) }) var ___cxa_is_pointer_type = (Module[ '___cxa_is_pointer_type' ] = function() { return (___cxa_is_pointer_type = Module['___cxa_is_pointer_type'] = Module['asm']['H']).apply(null, arguments) }) Module['addRunDependency'] = addRunDependency Module['removeRunDependency'] = removeRunDependency Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice Module['FS_unlink'] = FS.unlink Module['callMain'] = callMain Module['ccall'] = ccall Module['cwrap'] = cwrap Module['AsciiToString'] = AsciiToString Module['writeArrayToMemory'] = writeArrayToMemory Module['writeAsciiToMemory'] = writeAsciiToMemory var calledRun dependenciesFulfilled = function runCaller() { if (!calledRun) run() if (!calledRun) dependenciesFulfilled = runCaller } function callMain(args) { var entryFunction = Module['_main'] args = args || [] args.unshift(thisProgram) var argc = args.length var argv = stackAlloc((argc + 1) * 4) var argv_ptr = argv >> 2 args.forEach(arg => { HEAP32[argv_ptr++] = allocateUTF8OnStack(arg) }) HEAP32[argv_ptr] = 0 try { var ret = entryFunction(argc, argv) exitJS(ret, true) return ret } catch (e) { return handleException(e) } } function run(args) { args = args || arguments_ if (runDependencies > 0) { return } preRun() if (runDependencies > 0) { return } function doRun() { if (calledRun) return calledRun = true Module['calledRun'] = true if (ABORT) return initRuntime() preMain() readyPromiseResolve(Module) if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized']() if (shouldRunNow) callMain(args) postRun() } if (Module['setStatus']) { Module['setStatus']('Running...') setTimeout(function() { setTimeout(function() { Module['setStatus']('') }, 1) doRun() }, 1) } else { doRun() } } if (Module['preInit']) { if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']] while (Module['preInit'].length > 0) { Module['preInit'].pop()() } } var shouldRunNow = false if (Module['noInitialRun']) shouldRunNow = false run() Module.mountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) if (FS.isDir(containingDir) || containingDir === '/') { return } var currentDir = '/' var splitContainingDir = containingDir.split(path.sep) for (var ii = 1; ii < splitContainingDir.length; ii++) { currentDir += splitContainingDir[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } FS.mount(NODEFS, { root: containingDir }, currentDir) return currentDir + path.basename(filePath) } Module.unmountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) FS.unmount(containingDir) } Module.fs_mkdirs = function(dirs) { var currentDir = '/' var splitDirs = dirs.split('/') for (var ii = 1; ii < splitDirs.length; ++ii) { currentDir += splitDirs[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } } Module.fs_readFile = function(path, opts) { return FS.readFile(path, opts) } Module.fs_writeFile = function(path, data, opts) { return FS.writeFile(path, data, opts) } Module.fs_unlink = function(path) { return FS.unlink(path) } Module.fs_open = function(path, flags, mode) { return FS.open(path, flags, mode) } Module.fs_stat = function(path) { return FS.stat(path) } Module.fs_read = function(stream, buffer, offset, length, position) { return FS.read(stream, buffer, offset, length, position) } Module.fs_close = function(stream) { return FS.close(stream) } return DownsampleLabelImage.ready } })() if (typeof exports === 'object' && typeof module === 'object') module.exports = DownsampleLabelImage else if (typeof define === 'function' && define['amd']) define([], function() { return DownsampleLabelImage }) else if (typeof exports === 'object') exports['DownsampleLabelImage'] = DownsampleLabelImage ================================================ FILE: src/IO/Downsample/index.html ================================================ itk-wasm UMD Example
================================================ FILE: src/IO/Downsample/package.json ================================================ { "name": "itk-downsample", "version": "1.0.1", "description": "Downsample a 2D or 3D image", "main": "downsample.js", "type": "module", "scripts": { "build": "itk-wasm build", "start": "http-server .", "cypress:open": "npx cypress open", "cypress:run": "npx cypress run --config defaultCommandTimeout=8000", "browser:debug": "start-server-and-test start http://localhost:8080 cypress:open", "test:browser": "start-server-and-test start http://localhost:8080 cypress:run", "test": "npm run test:node && npm run test:nodeLabelImage && npm run test:browser", "test:node": "node downsample.js ./cthead1.png downsample.png", "test:nodeLabelImage": "node downsample.js -l ./cthead1-bin.png downsample-bin.png" }, "repository": { "type": "git", "url": "git+https://github.com/InsightSoftwareConsortium/itk-wasm.git" }, "keywords": [ "multi-scale" ], "author": "Matt McCormick", "license": "Apache-2.0", "bugs": { "url": "https://github.com/InsightSoftwareConsortium/itk-wasm/issues" }, "homepage": "https://github.com/InsightSoftwareConsortium/itk-wasm#readme", "dependencies": { "commander": "^9.1.0", "itk-image-io": "^1.0.0-b.84", "itk-wasm": "^1.0.0-b.84" }, "devDependencies": { "cypress": "^13.2.0", "http-server": "^14.1.0", "start-server-and-test": "^2.0.3" } } ================================================ FILE: src/IO/Downsample/styles.css ================================================ html, body { height: 90%; } textarea { resize: none; overflow-y: scroll; position: absolute; box-sizing: border-box; width: 600px; height: 400px; bottom: 0px; left: 0px; top: 50px; } #outputViewer { position: relative; box-sizing: border-box; height: 500px; width: 600px; left: 5px; top: 450px; } ================================================ FILE: src/IO/HttpStore.js ================================================ import axios from 'axios' class HttpStore { constructor(url) { this.href = url.href } async getItem(item) { const url = `${this.href}/${item}` return (await axios.get(url, { responseType: 'arraybuffer' })).data } } export default HttpStore ================================================ FILE: src/IO/ImageDataFromChunks.worker.js ================================================ import registerWebworker from 'webworker-promise/lib/register' import { computeRanges } from './Analyze/computeRanges' import componentTypeToTypedArray from './componentTypeToTypedArray' import { CXYZT, ensuredDims } from './dimensionUtils' import { getTypedArray, ElementGetter, getSize, testLittleEndian, } from './dtypeUtils' const haveSharedArrayBuffer = typeof self.SharedArrayBuffer === 'function' const validateIndices = ({ chunkStart, chunkEnd, roiStart, roiEnd }) => { if ( ['x', 'y', 'z'].some( dim => chunkStart[dim] > roiEnd[dim] || chunkEnd[dim] < roiStart[dim] ) ) { // We should never get here... console.error('Requested a chunk outside the region of interest!') } } const IS_SYSTEM_LITTLE_ENDIAN = (function() { const buffer = new ArrayBuffer(2) new DataView(buffer).setInt16(0, 256, true /* littleEndian */) // Int16Array uses the platform's endianness. return new Int16Array(buffer)[0] === 256 })() const makeImageDataFromChunks = ({ scaleInfo: info, imageType, chunkIndices, chunks, indexStart, indexEnd, }) => { const pixelArrayType = componentTypeToTypedArray.get(imageType.componentType) let pixelArray = null const pixelArrayElements = Array.from(info.arrayShape.values()).reduce( (a, b) => a * b ) if (haveSharedArrayBuffer) { const pixelArrayBytes = pixelArrayElements * pixelArrayType.BYTES_PER_ELEMENT const sharedArrayBuffer = new SharedArrayBuffer(pixelArrayBytes) pixelArray = new pixelArrayType(sharedArrayBuffer) } else { pixelArray = new pixelArrayType(pixelArrayElements) } const arrayShape = Object.fromEntries(ensuredDims(1, CXYZT, info.arrayShape)) const pixelStrides = { z: arrayShape.c * arrayShape.x * arrayShape.y, y: arrayShape.c * arrayShape.x, x: arrayShape.c, } const chunkSizeDefault1 = ensuredDims(1, CXYZT, info.chunkSize) const chunkSize = Object.fromEntries(chunkSizeDefault1) // stride is the number of elements between elements in a dimension const [chunkStrides] = Array.from(chunkSizeDefault1) .reverse() .reduce( ([strides, size], [dim, dimSize]) => [ { [dim]: size, ...strides }, size * dimSize, ], [{}, 1] ) for (let index = 0; index < chunkIndices.length; index++) { const [c, x, y, z /*t*/] = chunkIndices[index] const chunkStart = { c: c * chunkSize.c, z: z * chunkSize.z, y: y * chunkSize.y, x: x * chunkSize.x, } const chunkEnd = { c: (c + 1) * chunkSize.c, z: (z + 1) * chunkSize.z, y: (y + 1) * chunkSize.y, x: (x + 1) * chunkSize.x, } const roiStart = Object.fromEntries(ensuredDims(0, CXYZT, indexStart)) const roiEnd = Object.fromEntries(ensuredDims(1, CXYZT, indexEnd)) validateIndices({ chunkStart, chunkEnd, roiStart, roiEnd }) // iterate on image from chunk or ROI start const itStart = { c: Math.max(chunkStart.c, roiStart.c), z: Math.max(chunkStart.z, roiStart.z), y: Math.max(chunkStart.y, roiStart.y), x: Math.max(chunkStart.x, roiStart.x), } const itEnd = { c: Math.min(chunkEnd.c, roiEnd.c), z: Math.min(chunkEnd.z, roiEnd.z), y: Math.min(chunkEnd.y, roiEnd.y), x: Math.min(chunkEnd.x, roiEnd.x), } // write pixels at the start of the output image, not where they start in source image const roiStartPixelOffset = Object.keys(pixelStrides).reduce( (offset, dim) => offset + pixelStrides[dim] * roiStart[dim], 0 ) // Does input data group component(s) with each pixel? const dims = Array.from(info.arrayShape.keys()).join('') const areComponentsInterleaved = dims.endsWith('xc') || (arrayShape.c === 1 && dims.endsWith('x')) // if one component, can end with just 'x' // Input data endiennes matches system or just 1 byte? const dataEndiennesOK = getSize(info.dtype) === 1 || IS_SYSTEM_LITTLE_ENDIAN === testLittleEndian(info.dtype) if (areComponentsInterleaved && dataEndiennesOK) { // copy by row TURBO MODE const TypedArray = getTypedArray(info.dtype) const typedChunk = new TypedArray(chunks[index]) const offsetInChunkRow = (itStart.x - x * chunkSize.x) * chunkStrides.x for (let zz = itStart.z; zz < itEnd.z; zz++) { const zChunkOffset = (zz - z * chunkSize.z) * chunkStrides.z const zPixelOffset = zz * pixelStrides.z - roiStartPixelOffset for (let yy = itStart.y; yy < itEnd.y; yy++) { const yChunkOffset = offsetInChunkRow + (yy - y * chunkSize.y) * chunkStrides.y + zChunkOffset const subarray = typedChunk.subarray( yChunkOffset, yChunkOffset + itEnd.c * (itEnd.x - itStart.x) ) const pixelOffset = itStart.x * pixelStrides.x + // chunk's x index mapped to image's x index yy * pixelStrides.y + zPixelOffset pixelArray.set(subarray, pixelOffset) } // row } // slice } else { // copy by element tortoise mode const getChunkElement = ElementGetter(info.dtype, chunks[index]) for (let cc = itStart.c; cc < itEnd.c; cc++) { // subtract c * chunkSize.c from cc to start at beginning of chunk despite itStart.c const cChunkOffset = (cc - c * chunkSize.c) * chunkStrides.c for (let zz = itStart.z; zz < itEnd.z; zz++) { const zChunkOffset = (zz - z * chunkSize.z) * chunkStrides.z + cChunkOffset const zPixelOffset = zz * pixelStrides.z + cc - roiStartPixelOffset for (let yy = itStart.y; yy < itEnd.y; yy++) { const yChunkOffset = (yy - y * chunkSize.y) * chunkStrides.y + zChunkOffset const yPixelOffset = yy * pixelStrides.y + zPixelOffset for (let xx = itStart.x; xx < itEnd.x; xx++) { pixelArray[xx * pixelStrides.x + yPixelOffset] = getChunkElement( (xx - x * chunkSize.x) * chunkStrides.x + yChunkOffset ) } // column } // row } // slice } // component } // copy by row or element } // chunk return pixelArray } registerWebworker().operation('imageDataFromChunks', async chunkInfo => { const pixelArray = makeImageDataFromChunks(chunkInfo) const ranges = chunkInfo.areRangesNeeded ? ( await computeRanges( pixelArray, chunkInfo.scaleInfo.arrayShape.get('c') ?? 1 ) ).map(({ min, max }) => [min, max]) : undefined const response = { pixelArray, ranges } return haveSharedArrayBuffer ? response : new registerWebworker.TransferableResponse(response, [pixelArray.buffer]) }) ================================================ FILE: src/IO/InMemoryMultiscaleSpatialImage.js ================================================ import MultiscaleSpatialImage, { ensure3dDirection, storeImage, } from './MultiscaleSpatialImage' import componentTypeToTypedArray from './componentTypeToTypedArray' import { WorkerPool, runPipeline, InterfaceTypes, imageSharedBufferOrCopy, stackImages, } from 'itk-wasm' import { chunkArray, CXYZT, orderBy, toDimensionMap } from './dimensionUtils' import { computeRanges } from './Analyze/computeRanges' const numberOfWorkers = navigator.hardwareConcurrency ? navigator.hardwareConcurrency : 6 const downsampleWorkerPool = new WorkerPool(numberOfWorkers, runPipeline) class Coords { constructor(image, dims) { this.coords = new Map() let spatialDimIndex = 0 if (dims[0] == 'c') { spatialDimIndex = 1 const coord = new Uint16Array(image.imageType.components) for (let c = 0; c < image.imageType.components; c++) { coord[c] = c } this.coords.set('c', coord) } let spatialIndex = 0 for (let d = spatialDimIndex; d < dims.length; d++) { const size = image.size[spatialIndex] const origin = image.origin[spatialIndex] const spacing = image.spacing[spatialIndex] const coord = new Float64Array(size) for (let i = 0; i < size; i++) { coord[i] = origin + i * spacing } this.coords.set(dims[d], coord) spatialIndex++ } } async get(coord) { return this.coords.get(coord) } has(coord) { return this.coords.has(coord) } } async function chunkImage(image, chunkSize) { const imageType = image.imageType const componentType = imageType.componentType const dims = ['y', 'x'] const sizeCXYZTElements = [ imageType.components, image.size[0], image.size[1], 1, 1, ] const sizeCXYZTChunks = [ imageType.components, chunkSize[0], chunkSize[1], 1, 1, ] if (imageType.components > 1) { dims.push('c') } if (imageType.dimension == 3) { dims.unshift('z') sizeCXYZTElements[3] = image.size[2] sizeCXYZTChunks[3] = chunkSize[2] } const numberOfCXYZTChunks = [1, 1, 1, 1, 1] for (let i = 0; i < numberOfCXYZTChunks.length; i++) { numberOfCXYZTChunks[i] = Math.ceil( sizeCXYZTElements[i] / sizeCXYZTChunks[i] ) } const chunksStride = new Array(5) chunksStride[0] = 1 chunksStride[1] = 1 * numberOfCXYZTChunks[0] chunksStride[2] = 1 * numberOfCXYZTChunks[0] * numberOfCXYZTChunks[1] chunksStride[3] = 1 * numberOfCXYZTChunks[0] * numberOfCXYZTChunks[1] * numberOfCXYZTChunks[2] chunksStride[4] = 1 * numberOfCXYZTChunks[0] * numberOfCXYZTChunks[1] * numberOfCXYZTChunks[2] * numberOfCXYZTChunks[3] let chunks = new Array( numberOfCXYZTChunks[0] * numberOfCXYZTChunks[1] * numberOfCXYZTChunks[2] * numberOfCXYZTChunks[3] * numberOfCXYZTChunks[4] ) const dataStride = new Array(5) dataStride[0] = 1 dataStride[1] = 1 * imageType.components dataStride[2] = 1 * imageType.components * image.size[0] dataStride[3] = 1 * imageType.components * image.size[0] * image.size[1] dataStride[4] = dataStride[3] const chunkType = componentTypeToTypedArray.get(componentType) const chunkElements = sizeCXYZTChunks[0] * sizeCXYZTChunks[1] * sizeCXYZTChunks[2] * sizeCXYZTChunks[3] * sizeCXYZTChunks[4] const data = image.data //const haveSharedArrayBuffer = typeof window.SharedArrayBuffer === 'function' //if (haveSharedArrayBuffer && !data.buffer instanceof SharedArrayBuffer) { //const sharedBuffer = new SharedArrayBuffer(data.buffer.byteLength) // eslint-disable-line //const sharedTypedArray = new data.constructor(sharedBuffer) //sharedTypedArray.set(data, 0) //data = sharedTypedArray //} let offset = 0 const cxElements = sizeCXYZTChunks[0] * sizeCXYZTChunks[1] const singleChunk = numberOfCXYZTChunks.every(e => e === 1) if (singleChunk) { chunks[0] = data } else { //if (haveSharedArrayBuffer) { // Poorer performance //if (false) { //const taskArgs = new Array(chunks.length) //for (let k = 0; k < numberOfCXYZTChunks[3]; k++) { //const kOffset = k * sizeCXYZTChunks[3] //for (let j = 0; j < numberOfCXYZTChunks[2]; j++) { //const jOffset = j * sizeCXYZTChunks[2] //for (let i = 0; i < numberOfCXYZTChunks[1]; i++) { //const iOffset = i * sizeCXYZTChunks[1] //taskArgs[offset] = [ //{ //data, //componentType, //chunkElements, //cxElements, //sizeCXYZTChunks, //dataStride, //kOffset, //jOffset, //iOffset, //}, //] //offset++ //} // for every x chunk //} // for every y chunk //} // for every z chunk //// const result = await chunkerWorkerPool.runTasks(taskArgs).promise //// chunks = result.map(e => e.chunk) //} else { for (let k = 0; k < numberOfCXYZTChunks[3]; k++) { const kOffset = k * sizeCXYZTChunks[3] for (let j = 0; j < numberOfCXYZTChunks[2]; j++) { const jOffset = j * sizeCXYZTChunks[2] for (let i = 0; i < numberOfCXYZTChunks[1]; i++) { const iOffset = i * sizeCXYZTChunks[1] const chunk = new chunkType(chunkElements) let cxOffset = 0 for (let kk = 0; kk < sizeCXYZTChunks[3]; kk++) { const kaOffset = dataStride[3] * (kOffset + kk) for (let jj = 0; jj < sizeCXYZTChunks[2]; jj++) { const jaOffset = kaOffset + dataStride[2] * (jOffset + jj) const iaOffset = jaOffset + dataStride[1] * iOffset const dataSlice = data.subarray(iaOffset, iaOffset + cxElements) chunk.set(dataSlice, Math.min(cxOffset, chunk.length)) cxOffset += cxElements } } chunks[offset] = chunk offset++ } // for every x chunk } // for every y chunk } // for every z chunk //} } const ranges = ( await computeRanges(image.data, sizeCXYZTElements[0]) ).map(({ min, max }) => [min, max]) const orderByDims = orderBy(dims) const direction3d = ensure3dDirection(image.direction) const scaleInfo = { dims, coords: new Coords(image, [...dims].reverse()), // Coords assumes xyz chunkCount: orderByDims(toDimensionMap(CXYZT, numberOfCXYZTChunks)), chunkSize: orderByDims(toDimensionMap(CXYZT, sizeCXYZTChunks)), arrayShape: orderByDims(toDimensionMap(CXYZT, sizeCXYZTElements)), ranges, direction: chunkArray(3, [...direction3d].reverse()), // reverse to cast xyz to zyx } return { scaleInfo, chunksStride, chunks } } class InMemoryMultiscaleSpatialImage extends MultiscaleSpatialImage { static async buildPyramid( image, chunkSize = [64, 64, 64], isLabelImage = false ) { const scale0 = await chunkImage(image, chunkSize) const scaleInfo = [scale0.scaleInfo] const pyramid = [ { chunksStride: scale0.chunksStride, chunks: scale0.chunks, largestImage: image, }, ] let currentImage = image const maxTotalSplits = Math.min( parseInt(numberOfWorkers / 2), Math.max(currentImage.size[currentImage.size.length - 1], 1) ) const pipelinePath = isLabelImage ? 'DownsampleLabelImage' : 'Downsample' while ( currentImage.size.reduce((a, c, i) => a || c / chunkSize[i] >= 2.0, false) ) { const factors = currentImage.size.map((s, i) => { const n = Math.ceil(s / 2) const factor = n >= chunkSize[i] ? 2 : 1 return factor }) const downsampleTaskArgs = [] for (let index = 0; index < maxTotalSplits; index++) { const data = imageSharedBufferOrCopy(currentImage) const inputs = [ { type: InterfaceTypes.Image, data: data, }, ] const desiredOutputs = [ { type: InterfaceTypes.Image }, { type: InterfaceTypes.TextStream }, ] const args = [ '0', '0', factors.join(','), '--max-total-splits', '' + maxTotalSplits, '--split', '' + index, '--number-of-splits', '' + maxTotalSplits, '--memory-io', ] downsampleTaskArgs.push([pipelinePath, args, desiredOutputs, inputs]) } const results = await downsampleWorkerPool.runTasks(downsampleTaskArgs) .promise const validResults = results.filter(r => r.returnValue === 0) const imageSplits = validResults.map(({ outputs }) => outputs[0].data) currentImage = stackImages(imageSplits) const scaleN = await chunkImage(currentImage, chunkSize) scaleInfo.push(scaleN.scaleInfo) pyramid.push({ chunksStride: scaleN.chunksStride, chunks: scaleN.chunks, largestImage: currentImage, }) } // scale const imageType = image.imageType return { scaleInfo, imageType, pyramid } } constructor(pyramid, scaleInfo, imageType, name = 'Image') { super(scaleInfo, imageType, name) this.pyramid = pyramid // cache whole images for getImage to retrieve pyramid.forEach((data, scale) => { storeImage({ cache: this.cachedImages, scale, bounds: this.getIndexBounds(scale), image: data.largestImage, }) }) } async getChunksImpl(scale, cxyztArray) { const result = new Array(cxyztArray.length) const strides = this.pyramid[scale].chunksStride const chunks = this.pyramid[scale].chunks for (let i = 0; i < result.length; i++) { const cxyzt = cxyztArray[i] result[i] = chunks[ cxyzt[0] * strides[0] + cxyzt[1] * strides[1] + cxyzt[2] * strides[2] + cxyzt[3] * strides[3] + cxyzt[4] * strides[4] ] } return result.map(a => a.buffer) } } export default InMemoryMultiscaleSpatialImage ================================================ FILE: src/IO/MultiscaleSpatialImage.js ================================================ import { mat4, vec3 } from 'gl-matrix' import { setMatrixElement } from 'itk-wasm' import componentTypeToTypedArray from './componentTypeToTypedArray' import WebworkerPromise from 'webworker-promise' import { chunkArray, CXYZT, ensuredDims, orderBy } from './dimensionUtils' import { getDtype } from './dtypeUtils' import { transformBounds } from '../transformBounds' import { makeIndexToWorld } from '../internalUtils' import ImageDataFromChunksWorker from './ImageDataFromChunks.worker' const imageDataFromChunksWorkerPromise = new WebworkerPromise( new ImageDataFromChunksWorker() ) /* Every element corresponds to a pyramid scale Lower scales, corresponds to a higher index, correspond to a lower resolution. scaleInfo = [{ // scale 0 information dims: ['x', 'y'], // Valid elements: 'c', 'x', 'y', 'z', or 't' coords: .get() Promise resolves a Map('x': Float64Array([0.0, 2.0, ...), 'y' ... chunkCount: Map('t': 1, 'c': 1, 'z': 10, 'y': 10, 'x': 10]), // array shape in chunks chunkSize: Map('t': 1, 'c': 1, 'z': 1, 'y': 64, 'x': 64]), // chunk shape in elements arrayShape: Map('t': 1, 'c': 1, 'z': 1, 'y': 64, 'x': 64]), // array shape in elements ranges: Map('1': [0, 140], '2': [3, 130]) // or null if unknown. Range of values for each component name: 'dataset_name' }, { scale 1 information }, { scale N information } ] */ function inflate(bounds, delta) { bounds[0] -= delta bounds[1] += delta bounds[2] -= delta bounds[3] += delta bounds[4] -= delta bounds[5] += delta return bounds } // code modfied from vtk.js/ImageData const extentToBounds = (ex, indexToWorld) => { // prettier-ignore const corners = [ ex[0], ex[2], ex[4], ex[1], ex[2], ex[4], ex[0], ex[3], ex[4], ex[1], ex[3], ex[4], ex[0], ex[2], ex[5], ex[1], ex[2], ex[5], ex[0], ex[3], ex[5], ex[1], ex[3], ex[5]]; const idx = new Float64Array([corners[0], corners[1], corners[2]]) const vout = new Float64Array(3) vec3.transformMat4(vout, idx, indexToWorld) const bounds = [vout[0], vout[0], vout[1], vout[1], vout[2], vout[2]] for (let i = 3; i < 24; i += 3) { vec3.set(idx, corners[i], corners[i + 1], corners[i + 2]) vec3.transformMat4(vout, idx, indexToWorld) if (vout[0] < bounds[0]) { bounds[0] = vout[0] } if (vout[1] < bounds[2]) { bounds[2] = vout[1] } if (vout[2] < bounds[4]) { bounds[4] = vout[2] } if (vout[0] > bounds[1]) { bounds[1] = vout[0] } if (vout[1] > bounds[3]) { bounds[3] = vout[1] } if (vout[2] > bounds[5]) { bounds[5] = vout[2] } } return bounds } export const ensure3dDirection = d => { if (d.length >= 9) { return d } // Pad 2D with Z dimension return [d[0], d[1], 0, d[2], d[3], 0, 0, 0, 1] } export const worldBoundsToIndexBounds = ({ bounds, fullIndexBounds, worldToIndex, }) => { const fullIndexBoundsWithZCT = ensuredDims([0, 1], CXYZT, fullIndexBounds) if (!bounds || bounds.length === 0) { // no bounds, return full image return fullIndexBoundsWithZCT } const imageBounds = transformBounds(worldToIndex, bounds) // clamp to existing integer indexes const imageBoundsByDim = chunkArray(2, imageBounds) const spaceBounds = ['x', 'y', 'z'].map((dim, idx) => { const [min, max] = fullIndexBoundsWithZCT.get(dim) const [bmin, bmax] = imageBoundsByDim[idx] return [ dim, [ Math.floor(Math.min(max, Math.max(min, bmin))), Math.ceil(Math.min(max, Math.max(min, bmax))), ], ] }) const ctBounds = ['c', 't'].map(dim => [dim, fullIndexBoundsWithZCT.get(dim)]) return new Map([...spaceBounds, ...ctBounds]) } function isContained(benchmarkBounds, testedBounds) { return Array.from(benchmarkBounds).every( ([dim, [benchmarkMin, benchmarkMax]]) => { const [testedMin, testedMax] = testedBounds.get(dim) return benchmarkMin <= testedMin && testedMax <= benchmarkMax } ) } function findImageInBounds({ cache, scale, bounds }) { const imagesAtScale = cache.get(scale) ?? [] return imagesAtScale.find(({ bounds: cachedBounds }) => isContained(cachedBounds, bounds) )?.image } export function storeImage({ cache, scale, bounds, image }) { cache.set(scale, [{ bounds, image }]) } class MultiscaleSpatialImage { scaleInfo = [] name = 'Image' constructor(scaleInfo, imageType, name = 'Image') { this.scaleInfo = scaleInfo this.name = name this.imageType = imageType this.pixelArrayType = componentTypeToTypedArray.get(imageType.componentType) this.spatialDims = ['x', 'y', 'z'].slice(0, imageType.dimension) this.cachedImages = new Map() } get coarsestScale() { return this.scaleInfo.length - 1 } async scaleOrigin(scale) { const info = this.scaleInfo[scale] if (info.origin) return info.origin const origin = new Array(this.spatialDims.length) for (let index = 0; index < this.spatialDims.length; index++) { const dim = this.spatialDims[index] if (info.coords.has(dim)) { const coords = await info.coords.get(dim) origin[index] = coords[0] } else { origin[index] = 0.0 } } info.origin = origin return origin } async scaleSpacing(scale) { const info = this.scaleInfo[scale] if (info.spacing) return info.spacing const spacing = new Array(this.spatialDims.length) for (let index = 0; index < this.spatialDims.length; index++) { const dim = this.spatialDims[index] const dimCoords = await info.coords.get(dim) if (dimCoords && dimCoords.length >= 2) { spacing[index] = dimCoords[1] - dimCoords[0] } else { spacing[index] = 1.0 } } info.spacing = spacing return spacing } get direction() { const dimension = this.imageType.dimension const direction = new Float64Array(dimension * dimension) // Direction should be consistent over scales const infoDirection = this.scaleInfo[0].direction if (infoDirection) { // Todo: verify this logic const dims = this.scaleInfo[0].dims for (let d1 = 0; d1 < dimension; d1++) { const sd1 = this.spatialDims[d1] const di1 = dims.indexOf(sd1) for (let d2 = 0; d2 < dimension; d2++) { const sd2 = this.spatialDims[d2] const di2 = dims.indexOf(sd2) setMatrixElement( direction, dimension, d1, d2, infoDirection[di1][di2] ) } } } else { direction.fill(0.0) for (let d = 0; d < dimension; d++) { setMatrixElement(direction, dimension, d, d, 1.0) } } return direction } /* Return a promise that provides the requested chunk at a given scale and * chunk index. */ async getChunks(scale, cxyztArray) { return this.getChunksImpl(scale, cxyztArray) } async getChunksImpl(/* scale, cxyztArray */) { console.error('Override me in a derived class') } async buildImage(scale, indexBounds) { const { chunkSize, chunkCount, pixelArrayMetadata } = this.scaleInfo[scale] const [indexToWorld, spacing] = await Promise.all([ this.scaleIndexToWorld(scale), this.scaleSpacing(scale), ]) const start = new Map( CXYZT.map(dim => [dim, indexBounds.get(dim)?.[0] ?? 0]) ) const end = new Map( CXYZT.map(dim => [dim, (indexBounds.get(dim)?.[1] ?? 0) + 1]) ) const arrayShape = new Map( CXYZT.map(dim => [dim, end.get(dim) - start.get(dim)]) ) const startXYZ = ['x', 'y', 'z'].map(dim => start.get(dim)) const origin = vec3 .transformMat4([], startXYZ, indexToWorld) .slice(0, this.imageType.dimension) const chunkSizeWith1 = ensuredDims(1, CXYZT, chunkSize) const l = 0 const zChunkStart = Math.floor(start.get('z') / chunkSizeWith1.get('z')) const zChunkEnd = Math.ceil(end.get('z') / chunkSizeWith1.get('z')) const yChunkStart = Math.floor(start.get('y') / chunkSizeWith1.get('y')) const yChunkEnd = Math.ceil(end.get('y') / chunkSizeWith1.get('y')) const xChunkStart = Math.floor(start.get('x') / chunkSizeWith1.get('x')) const xChunkEnd = Math.ceil(end.get('x') / chunkSizeWith1.get('x')) const cChunkStart = 0 const cChunkEnd = chunkCount.get('c') ?? 1 const chunkIndices = [] for (let k = zChunkStart; k < zChunkEnd; k++) { for (let j = yChunkStart; j < yChunkEnd; j++) { for (let i = xChunkStart; i < xChunkEnd; i++) { for (let h = cChunkStart; h < cChunkEnd; h++) { chunkIndices.push([h, i, j, k, l]) } // for every cChunk } // for every xChunk } // for every yChunk } // for every zChunk const chunks = await this.getChunks(scale, chunkIndices) const preComputedRanges = this.scaleInfo[scale].ranges const args = { scaleInfo: { chunkSize: chunkSizeWith1, arrayShape, dtype: pixelArrayMetadata?.dtype ?? getDtype(this.pixelArrayType), }, imageType: this.imageType, chunkIndices, chunks, indexStart: start, indexEnd: end, areRangesNeeded: !preComputedRanges, } const { pixelArray, ranges } = await imageDataFromChunksWorkerPromise.exec( 'imageDataFromChunks', args ) return { imageType: this.imageType, name: this.scaleInfo[scale].name, origin, spacing, direction: this.direction, size: ['x', 'y', 'z'] .slice(0, this.imageType.dimension) .map(dim => arrayShape.get(dim)), data: pixelArray, ranges: preComputedRanges ?? ranges, } } async scaleIndexToWorld(requestedScale) { const scale = Math.min(requestedScale, this.scaleInfo.length - 1) if (this.scaleInfo[scale].indexToWorld) return this.scaleInfo[scale].indexToWorld // compute and cache origin/scale on info await Promise.all([this.scaleOrigin(scale), this.scaleSpacing(scale)]) const { spacing, origin } = this.scaleInfo[scale] const direction = ensure3dDirection(this.direction) this.scaleInfo[scale].indexToWorld = makeIndexToWorld({ direction, origin, spacing, }) return this.scaleInfo[scale].indexToWorld } /* Retrieve bounded image at scale. */ async getImage(requestedScale, worldBounds = []) { const scale = Math.min(requestedScale, this.scaleInfo.length - 1) const indexToWorld = await this.scaleIndexToWorld(scale) const { dims } = this.scaleInfo[scale] const indexBounds = orderBy(dims)( worldBoundsToIndexBounds({ bounds: worldBounds, fullIndexBounds: this.getIndexBounds(scale), worldToIndex: mat4.invert([], indexToWorld), }) ) const cachedImage = findImageInBounds({ cache: this.cachedImages, scale, bounds: indexBounds, }) if (cachedImage) return cachedImage const image = await this.buildImage(scale, indexBounds) storeImage({ cache: this.cachedImages, scale, bounds: indexBounds, image }) return image } getIndexBounds(requestedScale) { const scale = Math.min(requestedScale, this.scaleInfo.length - 1) const { arrayShape } = this.scaleInfo[scale] return new Map( Array.from(arrayShape).map(([dim, size]) => [dim, [0, size - 1]]) ) } // indexToWorld will be undefined if getImage() not completed on scale first getWorldBounds(requestedScale) { // clap scale same as getImage for when comparing images const scale = Math.min(requestedScale, this.scaleInfo.length - 1) const { indexToWorld } = this.scaleInfo[scale] const imageBounds = ensuredDims( [0, 1], ['x', 'y', 'z'], this.getIndexBounds(scale) ) const bounds = ['x', 'y', 'z'].flatMap(dim => imageBounds.get(dim)) inflate(bounds, 0.5) return extentToBounds(bounds, indexToWorld) } } export default MultiscaleSpatialImage ================================================ FILE: src/IO/ResampleLabelImage/.gitignore ================================================ /emscripten-build/* !emscripten-build/ResampleLabelImage* resample.png ================================================ FILE: src/IO/ResampleLabelImage/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.16) project(ResampleLabelImage) set(CMAKE_CXX_STANDARD 17) set(io_components) if (NOT EMSCRIPTEN AND NOT WASI) set(io_components ITKIOPNG ITKIOMeta ITKIONRRD ) endif() find_package(ITK REQUIRED COMPONENTS ${io_components} WebAssemblyInterface ITKImageGrid ITKImageFunction GenericLabelInterpolator ) include(${ITK_USE_FILE}) add_executable(ResampleLabelImage ResampleLabelImage.cxx) target_link_libraries(ResampleLabelImage PUBLIC ${ITK_LIBRARIES}) ================================================ FILE: src/IO/ResampleLabelImage/ResampleLabelImage.cxx ================================================ /*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "itkBinShrinkImageFilter.h" #include "itkVectorImage.h" #include "itkResampleImageFilter.h" #include "itkLabelImageGenericInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkImageRegionSplitterSlowDimension.h" #include "itkExtractImageFilter.h" #include "itkRGBPixel.h" #include "itkRGBAPixel.h" #include "itkVectorImage.h" #include "itkOffset.h" #include "itkVector.h" #include "itkPoint.h" #include "itkCovariantVector.h" #include "itkSymmetricSecondRankTensor.h" #include "itkDiffusionTensor3D.h" #include #include "itkFixedArray.h" #include "itkArray.h" #include "itkMatrix.h" #include "itkVariableLengthVector.h" #include "itkVariableSizeMatrix.h" #include #include "itkPipeline.h" #include "itkInputImage.h" #include "itkOutputImage.h" #include "itkOutputTextStream.h" #include "itkSupportInputImageTypes.h" template int ResampleLabelImage(itk::wasm::Pipeline &pipeline, itk::wasm::InputImage &inputImage) { using ImageType = TImage; pipeline.get_option("InputImage")->required(); using OutputImageType = itk::wasm::OutputImage; OutputImageType outputImage; pipeline.add_option("OutputImage", outputImage, "Output image")->required(); std::vector outSize; pipeline.add_option("-z,--size", outSize, "New image size for each direction")->expected(2, 3)->delimiter(','); std::vector outSpacing; pipeline.add_option("-p,--spacing", outSpacing, "New image spacing for each direction")->expected(2, 3)->delimiter(','); std::vector outOrigin; pipeline.add_option("-o,--origin", outOrigin, "New image origin for each direction")->expected(2, 3)->delimiter(','); std::vector outDirection; pipeline.add_option("-d,--direction", outDirection, "New image direction")->expected(4, 9)->delimiter(','); // split args unsigned int maxTotalSplits = 1; pipeline.add_option("-m,--max-total-splits", maxTotalSplits, "Maximum total splits when processed in parallel"); unsigned int split = 0; pipeline.add_option("-s,--split", split, "Current processed split"); itk::wasm::OutputTextStream numberOfSplitsStream; auto numberOfSplitsStreamOption = pipeline.add_option("--number-of-splits", numberOfSplitsStream, "Number of splits"); ITK_WASM_PARSE(pipeline); auto inImage = inputImage.Get(); using ResampleFilterType = itk::ResampleImageFilter; auto resampleFilter = ResampleFilterType::New(); resampleFilter->SetInput(inImage); using InterpolatorType = itk::LabelImageGenericInterpolateImageFunction; resampleFilter->SetInterpolator(InterpolatorType::New()); typename ImageType::SizeType outputSize; typename ImageType::SpacingType outputSpacing; typename ImageType::PointType outputOrigin; const int dims = outputSize.size(); for (int i = 0; i < dims; ++i) { outputSize[i] = outSize[i]; outputSpacing[i] = outSpacing[i]; outputOrigin[i] = outOrigin[i]; } resampleFilter->SetSize(outputSize); resampleFilter->SetOutputSpacing(outputSpacing); resampleFilter->SetOutputOrigin(outputOrigin); typename ImageType::DirectionType outputDirection; for (int row = 0; row < dims; ++row) { for (int col = 0; col < dims; ++col) { outputDirection(row, col) = outDirection[row * dims + col]; } } resampleFilter->SetOutputDirection(outputDirection); // Split handling using ROIFilterType = itk::ExtractImageFilter; resampleFilter->UpdateOutputInformation(); using RegionType = typename ImageType::RegionType; const RegionType largestRegion(resampleFilter->GetOutput()->GetLargestPossibleRegion()); using SplitterType = itk::ImageRegionSplitterSlowDimension; auto splitter = SplitterType::New(); const unsigned int numberOfSplits = splitter->GetNumberOfSplits(largestRegion, maxTotalSplits); if (split >= numberOfSplits) { std::cerr << "Error: requested split: " << split << " is outside the number of splits: " << numberOfSplits << std::endl; return EXIT_FAILURE; } if (!numberOfSplitsStreamOption->empty()) { numberOfSplitsStream.Get() << numberOfSplits; } RegionType requestedRegion(largestRegion); splitter->GetSplit(split, numberOfSplits, requestedRegion); auto roiFilter = ROIFilterType::New(); roiFilter->SetExtractionRegion(requestedRegion); roiFilter->SetInput(resampleFilter->GetOutput()); try { roiFilter->Update(); } catch (std::exception &error) { std::cerr << "Error: " << error.what() << std::endl; return EXIT_FAILURE; } outputImage.Set(roiFilter->GetOutput()); return EXIT_SUCCESS; } template class PipelineFunctor { public: int operator()(itk::wasm::Pipeline &pipeline) { using ImageType = TImage; using InputImageType = itk::wasm::InputImage; InputImageType inputImage; pipeline.add_option("InputImage", inputImage, "Input image"); ITK_WASM_PRE_PARSE(pipeline); return ResampleLabelImage(pipeline, inputImage); } }; int main(int argc, char *argv[]) { itk::wasm::Pipeline pipeline("ResampleLabelImage", "Resample a label image", argc, argv); return itk::wasm::SupportInputImageTypes::Dimensions<2U, 3U>("InputImage", pipeline); } ================================================ FILE: src/IO/ResampleLabelImage/emscripten-build/ResampleLabelImage.js ================================================ var ResampleLabelImage = (() => { var _scriptDir = import.meta.url return async function(ResampleLabelImage) { ResampleLabelImage = ResampleLabelImage || {} var Module = typeof ResampleLabelImage != 'undefined' ? ResampleLabelImage : {} var readyPromiseResolve, readyPromiseReject Module['ready'] = new Promise(function(resolve, reject) { readyPromiseResolve = resolve readyPromiseReject = reject }) var mStdout = null var mStderr = null Module['resetModuleStdout'] = function() { mStdout = '' } Module['resetModuleStderr'] = function() { mStderr = '' } Module['print'] = function(text) { console.log(text) mStdout += text + '\n' } Module['printErr'] = function(text) { console.error(text) mStderr += text + '\n' } Module['getModuleStdout'] = function() { return mStdout } Module['getModuleStderr'] = function() { return mStderr } var moduleOverrides = Object.assign({}, Module) var arguments_ = [] var thisProgram = './this.program' var quit_ = (status, toThrow) => { throw toThrow } var ENVIRONMENT_IS_WEB = typeof window == 'object' var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function' var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string' var scriptDirectory = '' function locateFile(path) { if (Module['locateFile']) { return Module['locateFile'](path, scriptDirectory) } return scriptDirectory + path } var read_, readAsync, readBinary, setWindowTitle function logExceptionOnExit(e) { if (e instanceof ExitStatus) return let toLog = e err('exiting due to exception: ' + toLog) } if (ENVIRONMENT_IS_NODE) { const { createRequire: createRequire } = await import('module') var require = createRequire(import.meta.url) var fs = require('fs') var nodePath = require('path') if (ENVIRONMENT_IS_WORKER) { scriptDirectory = nodePath.dirname(scriptDirectory) + '/' } else { scriptDirectory = require('url').fileURLToPath( new URL('./', import.meta.url) ) } read_ = (filename, binary) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) return fs.readFileSync(filename, binary ? undefined : 'utf8') } readBinary = filename => { var ret = read_(filename, true) if (!ret.buffer) { ret = new Uint8Array(ret) } return ret } readAsync = (filename, onload, onerror) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) fs.readFile(filename, function(err, data) { if (err) onerror(err) else onload(data.buffer) }) } if (process['argv'].length > 1) { thisProgram = process['argv'][1].replace(/\\/g, '/') } arguments_ = process['argv'].slice(2) process['on']('uncaughtException', function(ex) { if (!(ex instanceof ExitStatus)) { throw ex } }) process['on']('unhandledRejection', function(reason) { throw reason }) quit_ = (status, toThrow) => { if (keepRuntimeAlive()) { process['exitCode'] = status throw toThrow } logExceptionOnExit(toThrow) process['exit'](status) } Module['inspect'] = function() { return '[Emscripten Module object]' } } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = self.location.href } else if (typeof document != 'undefined' && document.currentScript) { scriptDirectory = document.currentScript.src } if (_scriptDir) { scriptDirectory = _scriptDir } if (scriptDirectory.indexOf('blob:') !== 0) { scriptDirectory = scriptDirectory.substr( 0, scriptDirectory.replace(/[?#].*/, '').lastIndexOf('/') + 1 ) } else { scriptDirectory = '' } { read_ = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.send(null) return xhr.responseText } if (ENVIRONMENT_IS_WORKER) { readBinary = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.responseType = 'arraybuffer' xhr.send(null) return new Uint8Array(xhr.response) } } readAsync = (url, onload, onerror) => { var xhr = new XMLHttpRequest() xhr.open('GET', url, true) xhr.responseType = 'arraybuffer' xhr.onload = () => { if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { onload(xhr.response) return } onerror() } xhr.onerror = onerror xhr.send(null) } } setWindowTitle = title => (document.title = title) } else { } var out = Module['print'] || console.log.bind(console) var err = Module['printErr'] || console.warn.bind(console) Object.assign(Module, moduleOverrides) moduleOverrides = null if (Module['arguments']) arguments_ = Module['arguments'] if (Module['thisProgram']) thisProgram = Module['thisProgram'] if (Module['quit']) quit_ = Module['quit'] var wasmBinary if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'] var noExitRuntime = Module['noExitRuntime'] || true if (typeof WebAssembly != 'object') { abort('no native wasm support detected') } var wasmMemory var ABORT = false var EXITSTATUS function assert(condition, text) { if (!condition) { abort(text) } } var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { var endIdx = idx + maxBytesToRead var endPtr = idx while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)) } var str = '' while (idx < endPtr) { var u0 = heapOrArray[idx++] if (!(u0 & 128)) { str += String.fromCharCode(u0) continue } var u1 = heapOrArray[idx++] & 63 if ((u0 & 224) == 192) { str += String.fromCharCode(((u0 & 31) << 6) | u1) continue } var u2 = heapOrArray[idx++] & 63 if ((u0 & 240) == 224) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2 } else { u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63) } if (u0 < 65536) { str += String.fromCharCode(u0) } else { var ch = u0 - 65536 str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)) } } return str } function UTF8ToString(ptr, maxBytesToRead) { return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '' } function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { if (!(maxBytesToWrite > 0)) return 0 var startIdx = outIdx var endIdx = outIdx + maxBytesToWrite - 1 for (var i = 0; i < str.length; ++i) { var u = str.charCodeAt(i) if (u >= 55296 && u <= 57343) { var u1 = str.charCodeAt(++i) u = (65536 + ((u & 1023) << 10)) | (u1 & 1023) } if (u <= 127) { if (outIdx >= endIdx) break heap[outIdx++] = u } else if (u <= 2047) { if (outIdx + 1 >= endIdx) break heap[outIdx++] = 192 | (u >> 6) heap[outIdx++] = 128 | (u & 63) } else if (u <= 65535) { if (outIdx + 2 >= endIdx) break heap[outIdx++] = 224 | (u >> 12) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } else { if (outIdx + 3 >= endIdx) break heap[outIdx++] = 240 | (u >> 18) heap[outIdx++] = 128 | ((u >> 12) & 63) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } } heap[outIdx] = 0 return outIdx - startIdx } function stringToUTF8(str, outPtr, maxBytesToWrite) { return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite) } function lengthBytesUTF8(str) { var len = 0 for (var i = 0; i < str.length; ++i) { var c = str.charCodeAt(i) if (c <= 127) { len++ } else if (c <= 2047) { len += 2 } else if (c >= 55296 && c <= 57343) { len += 4 ++i } else { len += 3 } } return len } var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64 function updateGlobalBufferAndViews(buf) { buffer = buf Module['HEAP8'] = HEAP8 = new Int8Array(buf) Module['HEAP16'] = HEAP16 = new Int16Array(buf) Module['HEAP32'] = HEAP32 = new Int32Array(buf) Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf) Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf) Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf) Module['HEAPF32'] = HEAPF32 = new Float32Array(buf) Module['HEAPF64'] = HEAPF64 = new Float64Array(buf) } var INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 16777216 var wasmTable var __ATPRERUN__ = [] var __ATINIT__ = [] var __ATMAIN__ = [] var __ATPOSTRUN__ = [] var runtimeInitialized = false function keepRuntimeAlive() { return noExitRuntime } function preRun() { if (Module['preRun']) { if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']] while (Module['preRun'].length) { addOnPreRun(Module['preRun'].shift()) } } callRuntimeCallbacks(__ATPRERUN__) } function initRuntime() { runtimeInitialized = true if (!Module['noFSInit'] && !FS.init.initialized) FS.init() FS.ignorePermissions = false TTY.init() callRuntimeCallbacks(__ATINIT__) } function preMain() { callRuntimeCallbacks(__ATMAIN__) } function postRun() { if (Module['postRun']) { if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']] while (Module['postRun'].length) { addOnPostRun(Module['postRun'].shift()) } } callRuntimeCallbacks(__ATPOSTRUN__) } function addOnPreRun(cb) { __ATPRERUN__.unshift(cb) } function addOnInit(cb) { __ATINIT__.unshift(cb) } function addOnPostRun(cb) { __ATPOSTRUN__.unshift(cb) } var runDependencies = 0 var runDependencyWatcher = null var dependenciesFulfilled = null function getUniqueRunDependency(id) { return id } function addRunDependency(id) { runDependencies++ if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } } function removeRunDependency(id) { runDependencies-- if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } if (runDependencies == 0) { if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher) runDependencyWatcher = null } if (dependenciesFulfilled) { var callback = dependenciesFulfilled dependenciesFulfilled = null callback() } } } function abort(what) { if (Module['onAbort']) { Module['onAbort'](what) } what = 'Aborted(' + what + ')' err(what) ABORT = true EXITSTATUS = 1 what += '. Build with -sASSERTIONS for more info.' var e = new WebAssembly.RuntimeError(what) readyPromiseReject(e) throw e } var dataURIPrefix = 'data:application/octet-stream;base64,' function isDataURI(filename) { return filename.startsWith(dataURIPrefix) } function isFileURI(filename) { return filename.startsWith('file://') } var wasmBinaryFile if (Module['locateFile']) { wasmBinaryFile = 'ResampleLabelImage.wasm' if (!isDataURI(wasmBinaryFile)) { wasmBinaryFile = locateFile(wasmBinaryFile) } } else { wasmBinaryFile = new URL('ResampleLabelImage.wasm', import.meta.url).href } function getBinary(file) { try { if (file == wasmBinaryFile && wasmBinary) { return new Uint8Array(wasmBinary) } if (readBinary) { return readBinary(file) } throw 'both async and sync fetching of the wasm failed' } catch (err) { abort(err) } } function getBinaryPromise() { if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { if (typeof fetch == 'function' && !isFileURI(wasmBinaryFile)) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }) .then(function(response) { if (!response['ok']) { throw "failed to load wasm binary file at '" + wasmBinaryFile + "'" } return response['arrayBuffer']() }) .catch(function() { return getBinary(wasmBinaryFile) }) } else { if (readAsync) { return new Promise(function(resolve, reject) { readAsync( wasmBinaryFile, function(response) { resolve(new Uint8Array(response)) }, reject ) }) } } } return Promise.resolve().then(function() { return getBinary(wasmBinaryFile) }) } function createWasm() { var info = { a: asmLibraryArg } function receiveInstance(instance, module) { var exports = instance.exports Module['asm'] = exports wasmMemory = Module['asm']['s'] updateGlobalBufferAndViews(wasmMemory.buffer) wasmTable = Module['asm']['D'] addOnInit(Module['asm']['t']) removeRunDependency('wasm-instantiate') } addRunDependency('wasm-instantiate') function receiveInstantiationResult(result) { receiveInstance(result['instance']) } function instantiateArrayBuffer(receiver) { return getBinaryPromise() .then(function(binary) { return WebAssembly.instantiate(binary, info) }) .then(function(instance) { return instance }) .then(receiver, function(reason) { err('failed to asynchronously prepare wasm: ' + reason) abort(reason) }) } function instantiateAsync() { if ( !wasmBinary && typeof WebAssembly.instantiateStreaming == 'function' && !isDataURI(wasmBinaryFile) && !isFileURI(wasmBinaryFile) && !ENVIRONMENT_IS_NODE && typeof fetch == 'function' ) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then( function(response) { var result = WebAssembly.instantiateStreaming(response, info) return result.then(receiveInstantiationResult, function(reason) { err('wasm streaming compile failed: ' + reason) err('falling back to ArrayBuffer instantiation') return instantiateArrayBuffer(receiveInstantiationResult) }) } ) } else { return instantiateArrayBuffer(receiveInstantiationResult) } } if (Module['instantiateWasm']) { try { var exports = Module['instantiateWasm'](info, receiveInstance) return exports } catch (e) { err('Module.instantiateWasm callback failed with error: ' + e) readyPromiseReject(e) } } instantiateAsync().catch(readyPromiseReject) return {} } var tempDouble var tempI64 function ExitStatus(status) { this.name = 'ExitStatus' this.message = 'Program terminated with exit(' + status + ')' this.status = status } function callRuntimeCallbacks(callbacks) { while (callbacks.length > 0) { callbacks.shift()(Module) } } function ExceptionInfo(excPtr) { this.excPtr = excPtr this.ptr = excPtr - 24 this.set_type = function(type) { HEAPU32[(this.ptr + 4) >> 2] = type } this.get_type = function() { return HEAPU32[(this.ptr + 4) >> 2] } this.set_destructor = function(destructor) { HEAPU32[(this.ptr + 8) >> 2] = destructor } this.get_destructor = function() { return HEAPU32[(this.ptr + 8) >> 2] } this.set_refcount = function(refcount) { HEAP32[this.ptr >> 2] = refcount } this.set_caught = function(caught) { caught = caught ? 1 : 0 HEAP8[(this.ptr + 12) >> 0] = caught } this.get_caught = function() { return HEAP8[(this.ptr + 12) >> 0] != 0 } this.set_rethrown = function(rethrown) { rethrown = rethrown ? 1 : 0 HEAP8[(this.ptr + 13) >> 0] = rethrown } this.get_rethrown = function() { return HEAP8[(this.ptr + 13) >> 0] != 0 } this.init = function(type, destructor) { this.set_adjusted_ptr(0) this.set_type(type) this.set_destructor(destructor) this.set_refcount(0) this.set_caught(false) this.set_rethrown(false) } this.add_ref = function() { var value = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = value + 1 } this.release_ref = function() { var prev = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = prev - 1 return prev === 1 } this.set_adjusted_ptr = function(adjustedPtr) { HEAPU32[(this.ptr + 16) >> 2] = adjustedPtr } this.get_adjusted_ptr = function() { return HEAPU32[(this.ptr + 16) >> 2] } this.get_exception_ptr = function() { var isPointer = ___cxa_is_pointer_type(this.get_type()) if (isPointer) { return HEAPU32[this.excPtr >> 2] } var adjusted = this.get_adjusted_ptr() if (adjusted !== 0) return adjusted return this.excPtr } } var exceptionLast = 0 var uncaughtExceptionCount = 0 function ___cxa_throw(ptr, type, destructor) { var info = new ExceptionInfo(ptr) info.init(type, destructor) exceptionLast = ptr uncaughtExceptionCount++ throw ptr } function setErrNo(value) { HEAP32[___errno_location() >> 2] = value return value } var PATH = { isAbs: path => path.charAt(0) === '/', splitPath: filename => { var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/ return splitPathRe.exec(filename).slice(1) }, normalizeArray: (parts, allowAboveRoot) => { var up = 0 for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i] if (last === '.') { parts.splice(i, 1) } else if (last === '..') { parts.splice(i, 1) up++ } else if (up) { parts.splice(i, 1) up-- } } if (allowAboveRoot) { for (; up; up--) { parts.unshift('..') } } return parts }, normalize: path => { var isAbsolute = PATH.isAbs(path), trailingSlash = path.substr(-1) === '/' path = PATH.normalizeArray( path.split('/').filter(p => !!p), !isAbsolute ).join('/') if (!path && !isAbsolute) { path = '.' } if (path && trailingSlash) { path += '/' } return (isAbsolute ? '/' : '') + path }, dirname: path => { var result = PATH.splitPath(path), root = result[0], dir = result[1] if (!root && !dir) { return '.' } if (dir) { dir = dir.substr(0, dir.length - 1) } return root + dir }, basename: path => { if (path === '/') return '/' path = PATH.normalize(path) path = path.replace(/\/$/, '') var lastSlash = path.lastIndexOf('/') if (lastSlash === -1) return path return path.substr(lastSlash + 1) }, join: function() { var paths = Array.prototype.slice.call(arguments) return PATH.normalize(paths.join('/')) }, join2: (l, r) => { return PATH.normalize(l + '/' + r) }, } function getRandomDevice() { if ( typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function' ) { var randomBuffer = new Uint8Array(1) return () => { crypto.getRandomValues(randomBuffer) return randomBuffer[0] } } else if (ENVIRONMENT_IS_NODE) { try { var crypto_module = require('crypto') return () => crypto_module['randomBytes'](1)[0] } catch (e) {} } return () => abort('randomDevice') } var PATH_FS = { resolve: function() { var resolvedPath = '', resolvedAbsolute = false for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = i >= 0 ? arguments[i] : FS.cwd() if (typeof path != 'string') { throw new TypeError('Arguments to path.resolve must be strings') } else if (!path) { return '' } resolvedPath = path + '/' + resolvedPath resolvedAbsolute = PATH.isAbs(path) } resolvedPath = PATH.normalizeArray( resolvedPath.split('/').filter(p => !!p), !resolvedAbsolute ).join('/') return (resolvedAbsolute ? '/' : '') + resolvedPath || '.' }, relative: (from, to) => { from = PATH_FS.resolve(from).substr(1) to = PATH_FS.resolve(to).substr(1) function trim(arr) { var start = 0 for (; start < arr.length; start++) { if (arr[start] !== '') break } var end = arr.length - 1 for (; end >= 0; end--) { if (arr[end] !== '') break } if (start > end) return [] return arr.slice(start, end - start + 1) } var fromParts = trim(from.split('/')) var toParts = trim(to.split('/')) var length = Math.min(fromParts.length, toParts.length) var samePartsLength = length for (var i = 0; i < length; i++) { if (fromParts[i] !== toParts[i]) { samePartsLength = i break } } var outputParts = [] for (var i = samePartsLength; i < fromParts.length; i++) { outputParts.push('..') } outputParts = outputParts.concat(toParts.slice(samePartsLength)) return outputParts.join('/') }, } function intArrayFromString(stringy, dontAddNull, length) { var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1 var u8array = new Array(len) var numBytesWritten = stringToUTF8Array( stringy, u8array, 0, u8array.length ) if (dontAddNull) u8array.length = numBytesWritten return u8array } var TTY = { ttys: [], init: function() {}, shutdown: function() {}, register: function(dev, ops) { TTY.ttys[dev] = { input: [], output: [], ops: ops } FS.registerDevice(dev, TTY.stream_ops) }, stream_ops: { open: function(stream) { var tty = TTY.ttys[stream.node.rdev] if (!tty) { throw new FS.ErrnoError(43) } stream.tty = tty stream.seekable = false }, close: function(stream) { stream.tty.ops.fsync(stream.tty) }, fsync: function(stream) { stream.tty.ops.fsync(stream.tty) }, read: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.get_char) { throw new FS.ErrnoError(60) } var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = stream.tty.ops.get_char(stream.tty) } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.put_char) { throw new FS.ErrnoError(60) } try { for (var i = 0; i < length; i++) { stream.tty.ops.put_char(stream.tty, buffer[offset + i]) } } catch (e) { throw new FS.ErrnoError(29) } if (length) { stream.node.timestamp = Date.now() } return i }, }, default_tty_ops: { get_char: function(tty) { if (!tty.input.length) { var result = null if (ENVIRONMENT_IS_NODE) { var BUFSIZE = 256 var buf = Buffer.alloc(BUFSIZE) var bytesRead = 0 try { bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE, -1) } catch (e) { if (e.toString().includes('EOF')) bytesRead = 0 else throw e } if (bytesRead > 0) { result = buf.slice(0, bytesRead).toString('utf-8') } else { result = null } } else if ( typeof window != 'undefined' && typeof window.prompt == 'function' ) { result = window.prompt('Input: ') if (result !== null) { result += '\n' } } else if (typeof readline == 'function') { result = readline() if (result !== null) { result += '\n' } } if (!result) { return null } tty.input = intArrayFromString(result, true) } return tty.input.shift() }, put_char: function(tty, val) { if (val === null || val === 10) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, default_tty1_ops: { put_char: function(tty, val) { if (val === null || val === 10) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, } function mmapAlloc(size) { abort() } var MEMFS = { ops_table: null, mount: function(mount) { return MEMFS.createNode(null, '/', 16384 | 511, 0) }, createNode: function(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { throw new FS.ErrnoError(63) } if (!MEMFS.ops_table) { MEMFS.ops_table = { dir: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, lookup: MEMFS.node_ops.lookup, mknod: MEMFS.node_ops.mknod, rename: MEMFS.node_ops.rename, unlink: MEMFS.node_ops.unlink, rmdir: MEMFS.node_ops.rmdir, readdir: MEMFS.node_ops.readdir, symlink: MEMFS.node_ops.symlink, }, stream: { llseek: MEMFS.stream_ops.llseek }, }, file: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: { llseek: MEMFS.stream_ops.llseek, read: MEMFS.stream_ops.read, write: MEMFS.stream_ops.write, allocate: MEMFS.stream_ops.allocate, mmap: MEMFS.stream_ops.mmap, msync: MEMFS.stream_ops.msync, }, }, link: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, readlink: MEMFS.node_ops.readlink, }, stream: {}, }, chrdev: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: FS.chrdev_stream_ops, }, } } var node = FS.createNode(parent, name, mode, dev) if (FS.isDir(node.mode)) { node.node_ops = MEMFS.ops_table.dir.node node.stream_ops = MEMFS.ops_table.dir.stream node.contents = {} } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node node.stream_ops = MEMFS.ops_table.file.stream node.usedBytes = 0 node.contents = null } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node node.stream_ops = MEMFS.ops_table.link.stream } else if (FS.isChrdev(node.mode)) { node.node_ops = MEMFS.ops_table.chrdev.node node.stream_ops = MEMFS.ops_table.chrdev.stream } node.timestamp = Date.now() if (parent) { parent.contents[name] = node parent.timestamp = node.timestamp } return node }, getFileDataAsTypedArray: function(node) { if (!node.contents) return new Uint8Array(0) if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes) return new Uint8Array(node.contents) }, expandFileStorage: function(node, newCapacity) { var prevCapacity = node.contents ? node.contents.length : 0 if (prevCapacity >= newCapacity) return var CAPACITY_DOUBLING_MAX = 1024 * 1024 newCapacity = Math.max( newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2 : 1.125)) >>> 0 ) if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256) var oldContents = node.contents node.contents = new Uint8Array(newCapacity) if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0) }, resizeFileStorage: function(node, newSize) { if (node.usedBytes == newSize) return if (newSize == 0) { node.contents = null node.usedBytes = 0 } else { var oldContents = node.contents node.contents = new Uint8Array(newSize) if (oldContents) { node.contents.set( oldContents.subarray(0, Math.min(newSize, node.usedBytes)) ) } node.usedBytes = newSize } }, node_ops: { getattr: function(node) { var attr = {} attr.dev = FS.isChrdev(node.mode) ? node.id : 1 attr.ino = node.id attr.mode = node.mode attr.nlink = 1 attr.uid = 0 attr.gid = 0 attr.rdev = node.rdev if (FS.isDir(node.mode)) { attr.size = 4096 } else if (FS.isFile(node.mode)) { attr.size = node.usedBytes } else if (FS.isLink(node.mode)) { attr.size = node.link.length } else { attr.size = 0 } attr.atime = new Date(node.timestamp) attr.mtime = new Date(node.timestamp) attr.ctime = new Date(node.timestamp) attr.blksize = 4096 attr.blocks = Math.ceil(attr.size / attr.blksize) return attr }, setattr: function(node, attr) { if (attr.mode !== undefined) { node.mode = attr.mode } if (attr.timestamp !== undefined) { node.timestamp = attr.timestamp } if (attr.size !== undefined) { MEMFS.resizeFileStorage(node, attr.size) } }, lookup: function(parent, name) { throw FS.genericErrors[44] }, mknod: function(parent, name, mode, dev) { return MEMFS.createNode(parent, name, mode, dev) }, rename: function(old_node, new_dir, new_name) { if (FS.isDir(old_node.mode)) { var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (new_node) { for (var i in new_node.contents) { throw new FS.ErrnoError(55) } } } delete old_node.parent.contents[old_node.name] old_node.parent.timestamp = Date.now() old_node.name = new_name new_dir.contents[new_name] = old_node new_dir.timestamp = old_node.parent.timestamp old_node.parent = new_dir }, unlink: function(parent, name) { delete parent.contents[name] parent.timestamp = Date.now() }, rmdir: function(parent, name) { var node = FS.lookupNode(parent, name) for (var i in node.contents) { throw new FS.ErrnoError(55) } delete parent.contents[name] parent.timestamp = Date.now() }, readdir: function(node) { var entries = ['.', '..'] for (var key in node.contents) { if (!node.contents.hasOwnProperty(key)) { continue } entries.push(key) } return entries }, symlink: function(parent, newname, oldpath) { var node = MEMFS.createNode(parent, newname, 511 | 40960, 0) node.link = oldpath return node }, readlink: function(node) { if (!FS.isLink(node.mode)) { throw new FS.ErrnoError(28) } return node.link }, }, stream_ops: { read: function(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= stream.node.usedBytes) return 0 var size = Math.min(stream.node.usedBytes - position, length) if (size > 8 && contents.subarray) { buffer.set(contents.subarray(position, position + size), offset) } else { for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i] } return size }, write: function(stream, buffer, offset, length, position, canOwn) { if (buffer.buffer === HEAP8.buffer) { canOwn = false } if (!length) return 0 var node = stream.node node.timestamp = Date.now() if (buffer.subarray && (!node.contents || node.contents.subarray)) { if (canOwn) { node.contents = buffer.subarray(offset, offset + length) node.usedBytes = length return length } else if (node.usedBytes === 0 && position === 0) { node.contents = buffer.slice(offset, offset + length) node.usedBytes = length return length } else if (position + length <= node.usedBytes) { node.contents.set( buffer.subarray(offset, offset + length), position ) return length } } MEMFS.expandFileStorage(node, position + length) if (node.contents.subarray && buffer.subarray) { node.contents.set( buffer.subarray(offset, offset + length), position ) } else { for (var i = 0; i < length; i++) { node.contents[position + i] = buffer[offset + i] } } node.usedBytes = Math.max(node.usedBytes, position + length) return length }, llseek: function(stream, offset, whence) { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { position += stream.node.usedBytes } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, allocate: function(stream, offset, length) { MEMFS.expandFileStorage(stream.node, offset + length) stream.node.usedBytes = Math.max( stream.node.usedBytes, offset + length ) }, mmap: function(stream, length, position, prot, flags) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr var allocated var contents = stream.node.contents if (!(flags & 2) && contents.buffer === buffer) { allocated = false ptr = contents.byteOffset } else { if (position > 0 || position + length < contents.length) { if (contents.subarray) { contents = contents.subarray(position, position + length) } else { contents = Array.prototype.slice.call( contents, position, position + length ) } } allocated = true ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } HEAP8.set(contents, ptr) } return { ptr: ptr, allocated: allocated } }, msync: function(stream, buffer, offset, length, mmapFlags) { MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } function asyncLoad(url, onload, onerror, noRunDep) { var dep = !noRunDep ? getUniqueRunDependency('al ' + url) : '' readAsync( url, arrayBuffer => { assert( arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).' ) onload(new Uint8Array(arrayBuffer)) if (dep) removeRunDependency(dep) }, event => { if (onerror) { onerror() } else { throw 'Loading data file "' + url + '" failed.' } } ) if (dep) addRunDependency(dep) } var ERRNO_CODES = {} var NODEFS = { isWindows: false, staticInit: () => { NODEFS.isWindows = !!process.platform.match(/^win/) var flags = process['binding']('constants') if (flags['fs']) { flags = flags['fs'] } NODEFS.flagsForNodeMap = { 1024: flags['O_APPEND'], 64: flags['O_CREAT'], 128: flags['O_EXCL'], 256: flags['O_NOCTTY'], 0: flags['O_RDONLY'], 2: flags['O_RDWR'], 4096: flags['O_SYNC'], 512: flags['O_TRUNC'], 1: flags['O_WRONLY'], 131072: flags['O_NOFOLLOW'], } }, convertNodeCode: e => { var code = e.code return ERRNO_CODES[code] }, mount: mount => { return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0) }, createNode: (parent, name, mode, dev) => { if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { throw new FS.ErrnoError(28) } var node = FS.createNode(parent, name, mode) node.node_ops = NODEFS.node_ops node.stream_ops = NODEFS.stream_ops return node }, getMode: path => { var stat try { stat = fs.lstatSync(path) if (NODEFS.isWindows) { stat.mode = stat.mode | ((stat.mode & 292) >> 2) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return stat.mode }, realPath: node => { var parts = [] while (node.parent !== node) { parts.push(node.name) node = node.parent } parts.push(node.mount.opts.root) parts.reverse() return PATH.join.apply(null, parts) }, flagsForNode: flags => { flags &= ~2097152 flags &= ~2048 flags &= ~32768 flags &= ~524288 flags &= ~65536 var newFlags = 0 for (var k in NODEFS.flagsForNodeMap) { if (flags & k) { newFlags |= NODEFS.flagsForNodeMap[k] flags ^= k } } if (flags) { throw new FS.ErrnoError(28) } return newFlags }, node_ops: { getattr: node => { var path = NODEFS.realPath(node) var stat try { stat = fs.lstatSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } if (NODEFS.isWindows && !stat.blksize) { stat.blksize = 4096 } if (NODEFS.isWindows && !stat.blocks) { stat.blocks = ((stat.size + stat.blksize - 1) / stat.blksize) | 0 } return { dev: stat.dev, ino: stat.ino, mode: stat.mode, nlink: stat.nlink, uid: stat.uid, gid: stat.gid, rdev: stat.rdev, size: stat.size, atime: stat.atime, mtime: stat.mtime, ctime: stat.ctime, blksize: stat.blksize, blocks: stat.blocks, } }, setattr: (node, attr) => { var path = NODEFS.realPath(node) try { if (attr.mode !== undefined) { fs.chmodSync(path, attr.mode) node.mode = attr.mode } if (attr.timestamp !== undefined) { var date = new Date(attr.timestamp) fs.utimesSync(path, date, date) } if (attr.size !== undefined) { fs.truncateSync(path, attr.size) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, lookup: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) var mode = NODEFS.getMode(path) return NODEFS.createNode(parent, name, mode) }, mknod: (parent, name, mode, dev) => { var node = NODEFS.createNode(parent, name, mode, dev) var path = NODEFS.realPath(node) try { if (FS.isDir(node.mode)) { fs.mkdirSync(path, node.mode) } else { fs.writeFileSync(path, '', { mode: node.mode }) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return node }, rename: (oldNode, newDir, newName) => { var oldPath = NODEFS.realPath(oldNode) var newPath = PATH.join2(NODEFS.realPath(newDir), newName) try { fs.renameSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } oldNode.name = newName }, unlink: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.unlinkSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, rmdir: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.rmdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readdir: node => { var path = NODEFS.realPath(node) try { return fs.readdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, symlink: (parent, newName, oldPath) => { var newPath = PATH.join2(NODEFS.realPath(parent), newName) try { fs.symlinkSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readlink: node => { var path = NODEFS.realPath(node) try { path = fs.readlinkSync(path) path = nodePath.relative( nodePath.resolve(node.mount.opts.root), path ) return path } catch (e) { if (!e.code) throw e if (e.code === 'UNKNOWN') throw new FS.ErrnoError(28) throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, }, stream_ops: { open: stream => { var path = NODEFS.realPath(stream.node) try { if (FS.isFile(stream.node.mode)) { stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags)) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, close: stream => { try { if (FS.isFile(stream.node.mode) && stream.nfd) { fs.closeSync(stream.nfd) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, read: (stream, buffer, offset, length, position) => { if (length === 0) return 0 try { return fs.readSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, write: (stream, buffer, offset, length, position) => { try { return fs.writeSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, llseek: (stream, offset, whence) => { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { try { var stat = fs.fstatSync(stream.nfd) position += stat.size } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, mmap: (stream, length, position, prot, flags) => { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr = mmapAlloc(length) NODEFS.stream_ops.read(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } }, msync: (stream, buffer, offset, length, mmapFlags) => { NODEFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } var FS = { root: null, mounts: [], devices: {}, streams: [], nextInode: 1, nameTable: null, currentPath: '/', initialized: false, ignorePermissions: true, ErrnoError: null, genericErrors: {}, filesystems: null, syncFSRequests: 0, lookupPath: (path, opts = {}) => { path = PATH_FS.resolve(path) if (!path) return { path: '', node: null } var defaults = { follow_mount: true, recurse_count: 0 } opts = Object.assign(defaults, opts) if (opts.recurse_count > 8) { throw new FS.ErrnoError(32) } var parts = path.split('/').filter(p => !!p) var current = FS.root var current_path = '/' for (var i = 0; i < parts.length; i++) { var islast = i === parts.length - 1 if (islast && opts.parent) { break } current = FS.lookupNode(current, parts[i]) current_path = PATH.join2(current_path, parts[i]) if (FS.isMountpoint(current)) { if (!islast || (islast && opts.follow_mount)) { current = current.mounted.root } } if (!islast || opts.follow) { var count = 0 while (FS.isLink(current.mode)) { var link = FS.readlink(current_path) current_path = PATH_FS.resolve(PATH.dirname(current_path), link) var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count + 1, }) current = lookup.node if (count++ > 40) { throw new FS.ErrnoError(32) } } } } return { path: current_path, node: current } }, getPath: node => { var path while (true) { if (FS.isRoot(node)) { var mount = node.mount.mountpoint if (!path) return mount return mount[mount.length - 1] !== '/' ? mount + '/' + path : mount + path } path = path ? node.name + '/' + path : node.name node = node.parent } }, hashName: (parentid, name) => { var hash = 0 for (var i = 0; i < name.length; i++) { hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0 } return ((parentid + hash) >>> 0) % FS.nameTable.length }, hashAddNode: node => { var hash = FS.hashName(node.parent.id, node.name) node.name_next = FS.nameTable[hash] FS.nameTable[hash] = node }, hashRemoveNode: node => { var hash = FS.hashName(node.parent.id, node.name) if (FS.nameTable[hash] === node) { FS.nameTable[hash] = node.name_next } else { var current = FS.nameTable[hash] while (current) { if (current.name_next === node) { current.name_next = node.name_next break } current = current.name_next } } }, lookupNode: (parent, name) => { var errCode = FS.mayLookup(parent) if (errCode) { throw new FS.ErrnoError(errCode, parent) } var hash = FS.hashName(parent.id, name) for (var node = FS.nameTable[hash]; node; node = node.name_next) { var nodeName = node.name if (node.parent.id === parent.id && nodeName === name) { return node } } return FS.lookup(parent, name) }, createNode: (parent, name, mode, rdev) => { var node = new FS.FSNode(parent, name, mode, rdev) FS.hashAddNode(node) return node }, destroyNode: node => { FS.hashRemoveNode(node) }, isRoot: node => { return node === node.parent }, isMountpoint: node => { return !!node.mounted }, isFile: mode => { return (mode & 61440) === 32768 }, isDir: mode => { return (mode & 61440) === 16384 }, isLink: mode => { return (mode & 61440) === 40960 }, isChrdev: mode => { return (mode & 61440) === 8192 }, isBlkdev: mode => { return (mode & 61440) === 24576 }, isFIFO: mode => { return (mode & 61440) === 4096 }, isSocket: mode => { return (mode & 49152) === 49152 }, flagModes: { r: 0, 'r+': 2, w: 577, 'w+': 578, a: 1089, 'a+': 1090 }, modeStringToFlags: str => { var flags = FS.flagModes[str] if (typeof flags == 'undefined') { throw new Error('Unknown file open mode: ' + str) } return flags }, flagsToPermissionString: flag => { var perms = ['r', 'w', 'rw'][flag & 3] if (flag & 512) { perms += 'w' } return perms }, nodePermissions: (node, perms) => { if (FS.ignorePermissions) { return 0 } if (perms.includes('r') && !(node.mode & 292)) { return 2 } else if (perms.includes('w') && !(node.mode & 146)) { return 2 } else if (perms.includes('x') && !(node.mode & 73)) { return 2 } return 0 }, mayLookup: dir => { var errCode = FS.nodePermissions(dir, 'x') if (errCode) return errCode if (!dir.node_ops.lookup) return 2 return 0 }, mayCreate: (dir, name) => { try { var node = FS.lookupNode(dir, name) return 20 } catch (e) {} return FS.nodePermissions(dir, 'wx') }, mayDelete: (dir, name, isdir) => { var node try { node = FS.lookupNode(dir, name) } catch (e) { return e.errno } var errCode = FS.nodePermissions(dir, 'wx') if (errCode) { return errCode } if (isdir) { if (!FS.isDir(node.mode)) { return 54 } if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return 10 } } else { if (FS.isDir(node.mode)) { return 31 } } return 0 }, mayOpen: (node, flags) => { if (!node) { return 44 } if (FS.isLink(node.mode)) { return 32 } else if (FS.isDir(node.mode)) { if (FS.flagsToPermissionString(flags) !== 'r' || flags & 512) { return 31 } } return FS.nodePermissions(node, FS.flagsToPermissionString(flags)) }, MAX_OPEN_FDS: 4096, nextfd: (fd_start = 0, fd_end = FS.MAX_OPEN_FDS) => { for (var fd = fd_start; fd <= fd_end; fd++) { if (!FS.streams[fd]) { return fd } } throw new FS.ErrnoError(33) }, getStream: fd => FS.streams[fd], createStream: (stream, fd_start, fd_end) => { if (!FS.FSStream) { FS.FSStream = function() { this.shared = {} } FS.FSStream.prototype = {} Object.defineProperties(FS.FSStream.prototype, { object: { get: function() { return this.node }, set: function(val) { this.node = val }, }, isRead: { get: function() { return (this.flags & 2097155) !== 1 }, }, isWrite: { get: function() { return (this.flags & 2097155) !== 0 }, }, isAppend: { get: function() { return this.flags & 1024 }, }, flags: { get: function() { return this.shared.flags }, set: function(val) { this.shared.flags = val }, }, position: { get: function() { return this.shared.position }, set: function(val) { this.shared.position = val }, }, }) } stream = Object.assign(new FS.FSStream(), stream) var fd = FS.nextfd(fd_start, fd_end) stream.fd = fd FS.streams[fd] = stream return stream }, closeStream: fd => { FS.streams[fd] = null }, chrdev_stream_ops: { open: stream => { var device = FS.getDevice(stream.node.rdev) stream.stream_ops = device.stream_ops if (stream.stream_ops.open) { stream.stream_ops.open(stream) } }, llseek: () => { throw new FS.ErrnoError(70) }, }, major: dev => dev >> 8, minor: dev => dev & 255, makedev: (ma, mi) => (ma << 8) | mi, registerDevice: (dev, ops) => { FS.devices[dev] = { stream_ops: ops } }, getDevice: dev => FS.devices[dev], getMounts: mount => { var mounts = [] var check = [mount] while (check.length) { var m = check.pop() mounts.push(m) check.push.apply(check, m.mounts) } return mounts }, syncfs: (populate, callback) => { if (typeof populate == 'function') { callback = populate populate = false } FS.syncFSRequests++ if (FS.syncFSRequests > 1) { err( 'warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work' ) } var mounts = FS.getMounts(FS.root.mount) var completed = 0 function doCallback(errCode) { FS.syncFSRequests-- return callback(errCode) } function done(errCode) { if (errCode) { if (!done.errored) { done.errored = true return doCallback(errCode) } return } if (++completed >= mounts.length) { doCallback(null) } } mounts.forEach(mount => { if (!mount.type.syncfs) { return done(null) } mount.type.syncfs(mount, populate, done) }) }, mount: (type, opts, mountpoint) => { var root = mountpoint === '/' var pseudo = !mountpoint var node if (root && FS.root) { throw new FS.ErrnoError(10) } else if (!root && !pseudo) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) mountpoint = lookup.path node = lookup.node if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } if (!FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } } var mount = { type: type, opts: opts, mountpoint: mountpoint, mounts: [], } var mountRoot = type.mount(mount) mountRoot.mount = mount mount.root = mountRoot if (root) { FS.root = mountRoot } else if (node) { node.mounted = mount if (node.mount) { node.mount.mounts.push(mount) } } return mountRoot }, unmount: mountpoint => { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) if (!FS.isMountpoint(lookup.node)) { throw new FS.ErrnoError(28) } var node = lookup.node var mount = node.mounted var mounts = FS.getMounts(mount) Object.keys(FS.nameTable).forEach(hash => { var current = FS.nameTable[hash] while (current) { var next = current.name_next if (mounts.includes(current.mount)) { FS.destroyNode(current) } current = next } }) node.mounted = null var idx = node.mount.mounts.indexOf(mount) node.mount.mounts.splice(idx, 1) }, lookup: (parent, name) => { return parent.node_ops.lookup(parent, name) }, mknod: (path, mode, dev) => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) if (!name || name === '.' || name === '..') { throw new FS.ErrnoError(28) } var errCode = FS.mayCreate(parent, name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.mknod) { throw new FS.ErrnoError(63) } return parent.node_ops.mknod(parent, name, mode, dev) }, create: (path, mode) => { mode = mode !== undefined ? mode : 438 mode &= 4095 mode |= 32768 return FS.mknod(path, mode, 0) }, mkdir: (path, mode) => { mode = mode !== undefined ? mode : 511 mode &= 511 | 512 mode |= 16384 return FS.mknod(path, mode, 0) }, mkdirTree: (path, mode) => { var dirs = path.split('/') var d = '' for (var i = 0; i < dirs.length; ++i) { if (!dirs[i]) continue d += '/' + dirs[i] try { FS.mkdir(d, mode) } catch (e) { if (e.errno != 20) throw e } } }, mkdev: (path, mode, dev) => { if (typeof dev == 'undefined') { dev = mode mode = 438 } mode |= 8192 return FS.mknod(path, mode, dev) }, symlink: (oldpath, newpath) => { if (!PATH_FS.resolve(oldpath)) { throw new FS.ErrnoError(44) } var lookup = FS.lookupPath(newpath, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var newname = PATH.basename(newpath) var errCode = FS.mayCreate(parent, newname) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.symlink) { throw new FS.ErrnoError(63) } return parent.node_ops.symlink(parent, newname, oldpath) }, rename: (old_path, new_path) => { var old_dirname = PATH.dirname(old_path) var new_dirname = PATH.dirname(new_path) var old_name = PATH.basename(old_path) var new_name = PATH.basename(new_path) var lookup, old_dir, new_dir lookup = FS.lookupPath(old_path, { parent: true }) old_dir = lookup.node lookup = FS.lookupPath(new_path, { parent: true }) new_dir = lookup.node if (!old_dir || !new_dir) throw new FS.ErrnoError(44) if (old_dir.mount !== new_dir.mount) { throw new FS.ErrnoError(75) } var old_node = FS.lookupNode(old_dir, old_name) var relative = PATH_FS.relative(old_path, new_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(28) } relative = PATH_FS.relative(new_path, old_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(55) } var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (old_node === new_node) { return } var isdir = FS.isDir(old_node.mode) var errCode = FS.mayDelete(old_dir, old_name, isdir) if (errCode) { throw new FS.ErrnoError(errCode) } errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!old_dir.node_ops.rename) { throw new FS.ErrnoError(63) } if ( FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node)) ) { throw new FS.ErrnoError(10) } if (new_dir !== old_dir) { errCode = FS.nodePermissions(old_dir, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } } FS.hashRemoveNode(old_node) try { old_dir.node_ops.rename(old_node, new_dir, new_name) } catch (e) { throw e } finally { FS.hashAddNode(old_node) } }, rmdir: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, true) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.rmdir) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.rmdir(parent, name) FS.destroyNode(node) }, readdir: path => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node if (!node.node_ops.readdir) { throw new FS.ErrnoError(54) } return node.node_ops.readdir(node) }, unlink: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, false) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.unlink) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.unlink(parent, name) FS.destroyNode(node) }, readlink: path => { var lookup = FS.lookupPath(path) var link = lookup.node if (!link) { throw new FS.ErrnoError(44) } if (!link.node_ops.readlink) { throw new FS.ErrnoError(28) } return PATH_FS.resolve( FS.getPath(link.parent), link.node_ops.readlink(link) ) }, stat: (path, dontFollow) => { var lookup = FS.lookupPath(path, { follow: !dontFollow }) var node = lookup.node if (!node) { throw new FS.ErrnoError(44) } if (!node.node_ops.getattr) { throw new FS.ErrnoError(63) } return node.node_ops.getattr(node) }, lstat: path => { return FS.stat(path, true) }, chmod: (path, mode, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { mode: (mode & 4095) | (node.mode & ~4095), timestamp: Date.now(), }) }, lchmod: (path, mode) => { FS.chmod(path, mode, true) }, fchmod: (fd, mode) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chmod(stream.node, mode) }, chown: (path, uid, gid, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { timestamp: Date.now() }) }, lchown: (path, uid, gid) => { FS.chown(path, uid, gid, true) }, fchown: (fd, uid, gid) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chown(stream.node, uid, gid) }, truncate: (path, len) => { if (len < 0) { throw new FS.ErrnoError(28) } var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: true }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } if (FS.isDir(node.mode)) { throw new FS.ErrnoError(31) } if (!FS.isFile(node.mode)) { throw new FS.ErrnoError(28) } var errCode = FS.nodePermissions(node, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } node.node_ops.setattr(node, { size: len, timestamp: Date.now() }) }, ftruncate: (fd, len) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(28) } FS.truncate(stream.node, len) }, utime: (path, atime, mtime) => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node node.node_ops.setattr(node, { timestamp: Math.max(atime, mtime) }) }, open: (path, flags, mode) => { if (path === '') { throw new FS.ErrnoError(44) } flags = typeof flags == 'string' ? FS.modeStringToFlags(flags) : flags mode = typeof mode == 'undefined' ? 438 : mode if (flags & 64) { mode = (mode & 4095) | 32768 } else { mode = 0 } var node if (typeof path == 'object') { node = path } else { path = PATH.normalize(path) try { var lookup = FS.lookupPath(path, { follow: !(flags & 131072) }) node = lookup.node } catch (e) {} } var created = false if (flags & 64) { if (node) { if (flags & 128) { throw new FS.ErrnoError(20) } } else { node = FS.mknod(path, mode, 0) created = true } } if (!node) { throw new FS.ErrnoError(44) } if (FS.isChrdev(node.mode)) { flags &= ~512 } if (flags & 65536 && !FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } if (!created) { var errCode = FS.mayOpen(node, flags) if (errCode) { throw new FS.ErrnoError(errCode) } } if (flags & 512 && !created) { FS.truncate(node, 0) } flags &= ~(128 | 512 | 131072) var stream = FS.createStream({ node: node, path: FS.getPath(node), flags: flags, seekable: true, position: 0, stream_ops: node.stream_ops, ungotten: [], error: false, }) if (stream.stream_ops.open) { stream.stream_ops.open(stream) } if (Module['logReadFiles'] && !(flags & 1)) { if (!FS.readFiles) FS.readFiles = {} if (!(path in FS.readFiles)) { FS.readFiles[path] = 1 } } return stream }, close: stream => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (stream.getdents) stream.getdents = null try { if (stream.stream_ops.close) { stream.stream_ops.close(stream) } } catch (e) { throw e } finally { FS.closeStream(stream.fd) } stream.fd = null }, isClosed: stream => { return stream.fd === null }, llseek: (stream, offset, whence) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (!stream.seekable || !stream.stream_ops.llseek) { throw new FS.ErrnoError(70) } if (whence != 0 && whence != 1 && whence != 2) { throw new FS.ErrnoError(28) } stream.position = stream.stream_ops.llseek(stream, offset, whence) stream.ungotten = [] return stream.position }, read: (stream, buffer, offset, length, position) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.read) { throw new FS.ErrnoError(28) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesRead = stream.stream_ops.read( stream, buffer, offset, length, position ) if (!seeking) stream.position += bytesRead return bytesRead }, write: (stream, buffer, offset, length, position, canOwn) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.write) { throw new FS.ErrnoError(28) } if (stream.seekable && stream.flags & 1024) { FS.llseek(stream, 0, 2) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesWritten = stream.stream_ops.write( stream, buffer, offset, length, position, canOwn ) if (!seeking) stream.position += bytesWritten return bytesWritten }, allocate: (stream, offset, length) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (offset < 0 || length <= 0) { throw new FS.ErrnoError(28) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(43) } if (!stream.stream_ops.allocate) { throw new FS.ErrnoError(138) } stream.stream_ops.allocate(stream, offset, length) }, mmap: (stream, length, position, prot, flags) => { if ( (prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2 ) { throw new FS.ErrnoError(2) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(2) } if (!stream.stream_ops.mmap) { throw new FS.ErrnoError(43) } return stream.stream_ops.mmap(stream, length, position, prot, flags) }, msync: (stream, buffer, offset, length, mmapFlags) => { if (!stream.stream_ops.msync) { return 0 } return stream.stream_ops.msync( stream, buffer, offset, length, mmapFlags ) }, munmap: stream => 0, ioctl: (stream, cmd, arg) => { if (!stream.stream_ops.ioctl) { throw new FS.ErrnoError(59) } return stream.stream_ops.ioctl(stream, cmd, arg) }, readFile: (path, opts = {}) => { opts.flags = opts.flags || 0 opts.encoding = opts.encoding || 'binary' if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { throw new Error('Invalid encoding type "' + opts.encoding + '"') } var ret var stream = FS.open(path, opts.flags) var stat = FS.stat(path) var length = stat.size var buf = new Uint8Array(length) FS.read(stream, buf, 0, length, 0) if (opts.encoding === 'utf8') { ret = UTF8ArrayToString(buf, 0) } else if (opts.encoding === 'binary') { ret = buf } FS.close(stream) return ret }, writeFile: (path, data, opts = {}) => { opts.flags = opts.flags || 577 var stream = FS.open(path, opts.flags, opts.mode) if (typeof data == 'string') { var buf = new Uint8Array(lengthBytesUTF8(data) + 1) var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length) FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn) } else if (ArrayBuffer.isView(data)) { FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn) } else { throw new Error('Unsupported data type') } FS.close(stream) }, cwd: () => FS.currentPath, chdir: path => { var lookup = FS.lookupPath(path, { follow: true }) if (lookup.node === null) { throw new FS.ErrnoError(44) } if (!FS.isDir(lookup.node.mode)) { throw new FS.ErrnoError(54) } var errCode = FS.nodePermissions(lookup.node, 'x') if (errCode) { throw new FS.ErrnoError(errCode) } FS.currentPath = lookup.path }, createDefaultDirectories: () => { FS.mkdir('/tmp') FS.mkdir('/home') FS.mkdir('/home/web_user') }, createDefaultDevices: () => { FS.mkdir('/dev') FS.registerDevice(FS.makedev(1, 3), { read: () => 0, write: (stream, buffer, offset, length, pos) => length, }) FS.mkdev('/dev/null', FS.makedev(1, 3)) TTY.register(FS.makedev(5, 0), TTY.default_tty_ops) TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops) FS.mkdev('/dev/tty', FS.makedev(5, 0)) FS.mkdev('/dev/tty1', FS.makedev(6, 0)) var random_device = getRandomDevice() FS.createDevice('/dev', 'random', random_device) FS.createDevice('/dev', 'urandom', random_device) FS.mkdir('/dev/shm') FS.mkdir('/dev/shm/tmp') }, createSpecialDirectories: () => { FS.mkdir('/proc') var proc_self = FS.mkdir('/proc/self') FS.mkdir('/proc/self/fd') FS.mount( { mount: () => { var node = FS.createNode(proc_self, 'fd', 16384 | 511, 73) node.node_ops = { lookup: (parent, name) => { var fd = +name var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) var ret = { parent: null, mount: { mountpoint: 'fake' }, node_ops: { readlink: () => stream.path }, } ret.parent = ret return ret }, } return node }, }, {}, '/proc/self/fd' ) }, createStandardStreams: () => { if (Module['stdin']) { FS.createDevice('/dev', 'stdin', Module['stdin']) } else { FS.symlink('/dev/tty', '/dev/stdin') } if (Module['stdout']) { FS.createDevice('/dev', 'stdout', null, Module['stdout']) } else { FS.symlink('/dev/tty', '/dev/stdout') } if (Module['stderr']) { FS.createDevice('/dev', 'stderr', null, Module['stderr']) } else { FS.symlink('/dev/tty1', '/dev/stderr') } var stdin = FS.open('/dev/stdin', 0) var stdout = FS.open('/dev/stdout', 1) var stderr = FS.open('/dev/stderr', 1) }, ensureErrnoError: () => { if (FS.ErrnoError) return FS.ErrnoError = function ErrnoError(errno, node) { this.node = node this.setErrno = function(errno) { this.errno = errno } this.setErrno(errno) this.message = 'FS error' } FS.ErrnoError.prototype = new Error() FS.ErrnoError.prototype.constructor = FS.ErrnoError ;[44].forEach(code => { FS.genericErrors[code] = new FS.ErrnoError(code) FS.genericErrors[code].stack = '' }) }, staticInit: () => { FS.ensureErrnoError() FS.nameTable = new Array(4096) FS.mount(MEMFS, {}, '/') FS.createDefaultDirectories() FS.createDefaultDevices() FS.createSpecialDirectories() FS.filesystems = { MEMFS: MEMFS, NODEFS: NODEFS } }, init: (input, output, error) => { FS.init.initialized = true FS.ensureErrnoError() Module['stdin'] = input || Module['stdin'] Module['stdout'] = output || Module['stdout'] Module['stderr'] = error || Module['stderr'] FS.createStandardStreams() }, quit: () => { FS.init.initialized = false for (var i = 0; i < FS.streams.length; i++) { var stream = FS.streams[i] if (!stream) { continue } FS.close(stream) } }, getMode: (canRead, canWrite) => { var mode = 0 if (canRead) mode |= 292 | 73 if (canWrite) mode |= 146 return mode }, findObject: (path, dontResolveLastLink) => { var ret = FS.analyzePath(path, dontResolveLastLink) if (!ret.exists) { return null } return ret.object }, analyzePath: (path, dontResolveLastLink) => { try { var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) path = lookup.path } catch (e) {} var ret = { isRoot: false, exists: false, error: 0, name: null, path: null, object: null, parentExists: false, parentPath: null, parentObject: null, } try { var lookup = FS.lookupPath(path, { parent: true }) ret.parentExists = true ret.parentPath = lookup.path ret.parentObject = lookup.node ret.name = PATH.basename(path) lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) ret.exists = true ret.path = lookup.path ret.object = lookup.node ret.name = lookup.node.name ret.isRoot = lookup.path === '/' } catch (e) { ret.error = e.errno } return ret }, createPath: (parent, path, canRead, canWrite) => { parent = typeof parent == 'string' ? parent : FS.getPath(parent) var parts = path.split('/').reverse() while (parts.length) { var part = parts.pop() if (!part) continue var current = PATH.join2(parent, part) try { FS.mkdir(current) } catch (e) {} parent = current } return current }, createFile: (parent, name, properties, canRead, canWrite) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(canRead, canWrite) return FS.create(path, mode) }, createDataFile: (parent, name, data, canRead, canWrite, canOwn) => { var path = name if (parent) { parent = typeof parent == 'string' ? parent : FS.getPath(parent) path = name ? PATH.join2(parent, name) : parent } var mode = FS.getMode(canRead, canWrite) var node = FS.create(path, mode) if (data) { if (typeof data == 'string') { var arr = new Array(data.length) for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i) data = arr } FS.chmod(node, mode | 146) var stream = FS.open(node, 577) FS.write(stream, data, 0, data.length, 0, canOwn) FS.close(stream) FS.chmod(node, mode) } return node }, createDevice: (parent, name, input, output) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(!!input, !!output) if (!FS.createDevice.major) FS.createDevice.major = 64 var dev = FS.makedev(FS.createDevice.major++, 0) FS.registerDevice(dev, { open: stream => { stream.seekable = false }, close: stream => { if (output && output.buffer && output.buffer.length) { output(10) } }, read: (stream, buffer, offset, length, pos) => { var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = input() } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: (stream, buffer, offset, length, pos) => { for (var i = 0; i < length; i++) { try { output(buffer[offset + i]) } catch (e) { throw new FS.ErrnoError(29) } } if (length) { stream.node.timestamp = Date.now() } return i }, }) return FS.mkdev(path, mode, dev) }, forceLoadFile: obj => { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true if (typeof XMLHttpRequest != 'undefined') { throw new Error( 'Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.' ) } else if (read_) { try { obj.contents = intArrayFromString(read_(obj.url), true) obj.usedBytes = obj.contents.length } catch (e) { throw new FS.ErrnoError(29) } } else { throw new Error('Cannot load without read() or XMLHttpRequest.') } }, createLazyFile: (parent, name, url, canRead, canWrite) => { function LazyUint8Array() { this.lengthKnown = false this.chunks = [] } LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) { if (idx > this.length - 1 || idx < 0) { return undefined } var chunkOffset = idx % this.chunkSize var chunkNum = (idx / this.chunkSize) | 0 return this.getter(chunkNum)[chunkOffset] } LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter( getter ) { this.getter = getter } LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { var xhr = new XMLHttpRequest() xhr.open('HEAD', url, false) xhr.send(null) if (!((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) throw new Error("Couldn't load " + url + '. Status: ' + xhr.status) var datalength = Number(xhr.getResponseHeader('Content-length')) var header var hasByteServing = (header = xhr.getResponseHeader('Accept-Ranges')) && header === 'bytes' var usesGzip = (header = xhr.getResponseHeader('Content-Encoding')) && header === 'gzip' var chunkSize = 1024 * 1024 if (!hasByteServing) chunkSize = datalength var doXHR = (from, to) => { if (from > to) throw new Error( 'invalid range (' + from + ', ' + to + ') or no bytes requested!' ) if (to > datalength - 1) throw new Error( 'only ' + datalength + ' bytes available! programmer error!' ) var xhr = new XMLHttpRequest() xhr.open('GET', url, false) if (datalength !== chunkSize) xhr.setRequestHeader('Range', 'bytes=' + from + '-' + to) xhr.responseType = 'arraybuffer' if (xhr.overrideMimeType) { xhr.overrideMimeType('text/plain; charset=x-user-defined') } xhr.send(null) if ( !((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ) throw new Error( "Couldn't load " + url + '. Status: ' + xhr.status ) if (xhr.response !== undefined) { return new Uint8Array(xhr.response || []) } return intArrayFromString(xhr.responseText || '', true) } var lazyArray = this lazyArray.setDataGetter(chunkNum => { var start = chunkNum * chunkSize var end = (chunkNum + 1) * chunkSize - 1 end = Math.min(end, datalength - 1) if (typeof lazyArray.chunks[chunkNum] == 'undefined') { lazyArray.chunks[chunkNum] = doXHR(start, end) } if (typeof lazyArray.chunks[chunkNum] == 'undefined') throw new Error('doXHR failed!') return lazyArray.chunks[chunkNum] }) if (usesGzip || !datalength) { chunkSize = datalength = 1 datalength = this.getter(0).length chunkSize = datalength out( 'LazyFiles on gzip forces download of the whole file when length is accessed' ) } this._length = datalength this._chunkSize = chunkSize this.lengthKnown = true } if (typeof XMLHttpRequest != 'undefined') { if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc' var lazyArray = new LazyUint8Array() Object.defineProperties(lazyArray, { length: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._length }, }, chunkSize: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._chunkSize }, }, }) var properties = { isDevice: false, contents: lazyArray } } else { var properties = { isDevice: false, url: url } } var node = FS.createFile(parent, name, properties, canRead, canWrite) if (properties.contents) { node.contents = properties.contents } else if (properties.url) { node.contents = null node.url = properties.url } Object.defineProperties(node, { usedBytes: { get: function() { return this.contents.length }, }, }) var stream_ops = {} var keys = Object.keys(node.stream_ops) keys.forEach(key => { var fn = node.stream_ops[key] stream_ops[key] = function forceLoadLazyFile() { FS.forceLoadFile(node) return fn.apply(null, arguments) } }) function writeChunks(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= contents.length) return 0 var size = Math.min(contents.length - position, length) if (contents.slice) { for (var i = 0; i < size; i++) { buffer[offset + i] = contents[position + i] } } else { for (var i = 0; i < size; i++) { buffer[offset + i] = contents.get(position + i) } } return size } stream_ops.read = (stream, buffer, offset, length, position) => { FS.forceLoadFile(node) return writeChunks(stream, buffer, offset, length, position) } stream_ops.mmap = (stream, length, position, prot, flags) => { FS.forceLoadFile(node) var ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } writeChunks(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } } node.stream_ops = stream_ops return node }, createPreloadedFile: ( parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish ) => { var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent var dep = getUniqueRunDependency('cp ' + fullname) function processData(byteArray) { function finish(byteArray) { if (preFinish) preFinish() if (!dontCreateFile) { FS.createDataFile( parent, name, byteArray, canRead, canWrite, canOwn ) } if (onload) onload() removeRunDependency(dep) } if ( Browser.handledByPreloadPlugin(byteArray, fullname, finish, () => { if (onerror) onerror() removeRunDependency(dep) }) ) { return } finish(byteArray) } addRunDependency(dep) if (typeof url == 'string') { asyncLoad(url, byteArray => processData(byteArray), onerror) } else { processData(url) } }, indexedDB: () => { return ( window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB ) }, DB_NAME: () => { return 'EM_FS_' + window.location.pathname }, DB_VERSION: 20, DB_STORE_NAME: 'FILE_DATA', saveFilesToDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = () => { out('creating db') var db = openRequest.result db.createObjectStore(FS.DB_STORE_NAME) } openRequest.onsuccess = () => { var db = openRequest.result var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite') var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var putRequest = files.put( FS.analyzePath(path).object.contents, path ) putRequest.onsuccess = () => { ok++ if (ok + fail == total) finish() } putRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, loadFilesFromDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = onerror openRequest.onsuccess = () => { var db = openRequest.result try { var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly') } catch (e) { onerror(e) return } var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var getRequest = files.get(path) getRequest.onsuccess = () => { if (FS.analyzePath(path).exists) { FS.unlink(path) } FS.createDataFile( PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true ) ok++ if (ok + fail == total) finish() } getRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, } var SYSCALLS = { DEFAULT_POLLMASK: 5, calculateAt: function(dirfd, path, allowEmpty) { if (PATH.isAbs(path)) { return path } var dir if (dirfd === -100) { dir = FS.cwd() } else { var dirstream = SYSCALLS.getStreamFromFD(dirfd) dir = dirstream.path } if (path.length == 0) { if (!allowEmpty) { throw new FS.ErrnoError(44) } return dir } return PATH.join2(dir, path) }, doStat: function(func, path, buf) { try { var stat = func(path) } catch (e) { if ( e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node)) ) { return -54 } throw e } HEAP32[buf >> 2] = stat.dev HEAP32[(buf + 8) >> 2] = stat.ino HEAP32[(buf + 12) >> 2] = stat.mode HEAPU32[(buf + 16) >> 2] = stat.nlink HEAP32[(buf + 20) >> 2] = stat.uid HEAP32[(buf + 24) >> 2] = stat.gid HEAP32[(buf + 28) >> 2] = stat.rdev ;(tempI64 = [ stat.size >>> 0, ((tempDouble = stat.size), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 40) >> 2] = tempI64[0]), (HEAP32[(buf + 44) >> 2] = tempI64[1]) HEAP32[(buf + 48) >> 2] = 4096 HEAP32[(buf + 52) >> 2] = stat.blocks var atime = stat.atime.getTime() var mtime = stat.mtime.getTime() var ctime = stat.ctime.getTime() ;(tempI64 = [ Math.floor(atime / 1e3) >>> 0, ((tempDouble = Math.floor(atime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 56) >> 2] = tempI64[0]), (HEAP32[(buf + 60) >> 2] = tempI64[1]) HEAPU32[(buf + 64) >> 2] = (atime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(mtime / 1e3) >>> 0, ((tempDouble = Math.floor(mtime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 72) >> 2] = tempI64[0]), (HEAP32[(buf + 76) >> 2] = tempI64[1]) HEAPU32[(buf + 80) >> 2] = (mtime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(ctime / 1e3) >>> 0, ((tempDouble = Math.floor(ctime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 88) >> 2] = tempI64[0]), (HEAP32[(buf + 92) >> 2] = tempI64[1]) HEAPU32[(buf + 96) >> 2] = (ctime % 1e3) * 1e3 ;(tempI64 = [ stat.ino >>> 0, ((tempDouble = stat.ino), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 104) >> 2] = tempI64[0]), (HEAP32[(buf + 108) >> 2] = tempI64[1]) return 0 }, doMsync: function(addr, stream, len, flags, offset) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } if (flags & 2) { return 0 } var buffer = HEAPU8.slice(addr, addr + len) FS.msync(stream, buffer, offset, len, flags) }, varargs: undefined, get: function() { SYSCALLS.varargs += 4 var ret = HEAP32[(SYSCALLS.varargs - 4) >> 2] return ret }, getStr: function(ptr) { var ret = UTF8ToString(ptr) return ret }, getStreamFromFD: function(fd) { var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) return stream }, } function ___syscall_fcntl64(fd, cmd, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (cmd) { case 0: { var arg = SYSCALLS.get() if (arg < 0) { return -28 } var newStream newStream = FS.createStream(stream, arg) return newStream.fd } case 1: case 2: return 0 case 3: return stream.flags case 4: { var arg = SYSCALLS.get() stream.flags |= arg return 0 } case 5: { var arg = SYSCALLS.get() var offset = 0 HEAP16[(arg + offset) >> 1] = 2 return 0 } case 6: case 7: return 0 case 16: case 8: return -28 case 9: setErrNo(28) return -1 default: { return -28 } } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_getcwd(buf, size) { try { if (size === 0) return -28 var cwd = FS.cwd() var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1 if (size < cwdLengthInBytes) return -68 stringToUTF8(cwd, buf, size) return cwdLengthInBytes } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_ioctl(fd, op, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (op) { case 21509: case 21505: { if (!stream.tty) return -59 return 0 } case 21510: case 21511: case 21512: case 21506: case 21507: case 21508: { if (!stream.tty) return -59 return 0 } case 21519: { if (!stream.tty) return -59 var argp = SYSCALLS.get() HEAP32[argp >> 2] = 0 return 0 } case 21520: { if (!stream.tty) return -59 return -28 } case 21531: { var argp = SYSCALLS.get() return FS.ioctl(stream, op, argp) } case 21523: { if (!stream.tty) return -59 return 0 } case 21524: { if (!stream.tty) return -59 return 0 } default: return -28 } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_openat(dirfd, path, flags, varargs) { SYSCALLS.varargs = varargs try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) var mode = varargs ? SYSCALLS.get() : 0 return FS.open(path, flags, mode).fd } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_readlinkat(dirfd, path, buf, bufsize) { try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) if (bufsize <= 0) return -28 var ret = FS.readlink(path) var len = Math.min(bufsize, lengthBytesUTF8(ret)) var endChar = HEAP8[buf + len] stringToUTF8(ret, buf, bufsize + 1) HEAP8[buf + len] = endChar return len } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_stat64(path, buf) { try { path = SYSCALLS.getStr(path) return SYSCALLS.doStat(FS.stat, path, buf) } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function _abort() { abort('') } function _emscripten_memcpy_big(dest, src, num) { HEAPU8.copyWithin(dest, src, src + num) } function getHeapMax() { return 2147483648 } function emscripten_realloc_buffer(size) { try { wasmMemory.grow((size - buffer.byteLength + 65535) >>> 16) updateGlobalBufferAndViews(wasmMemory.buffer) return 1 } catch (e) {} } function _emscripten_resize_heap(requestedSize) { var oldSize = HEAPU8.length requestedSize = requestedSize >>> 0 var maxHeapSize = getHeapMax() if (requestedSize > maxHeapSize) { return false } let alignUp = (x, multiple) => x + ((multiple - (x % multiple)) % multiple) for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown) overGrownHeapSize = Math.min( overGrownHeapSize, requestedSize + 100663296 ) var newSize = Math.min( maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536) ) var replacement = emscripten_realloc_buffer(newSize) if (replacement) { return true } } return false } var ENV = {} function getExecutableName() { return thisProgram || './this.program' } function getEnvStrings() { if (!getEnvStrings.strings) { var lang = ( (typeof navigator == 'object' && navigator.languages && navigator.languages[0]) || 'C' ).replace('-', '_') + '.UTF-8' var env = { USER: 'web_user', LOGNAME: 'web_user', PATH: '/', PWD: '/', HOME: '/home/web_user', LANG: lang, _: getExecutableName(), } for (var x in ENV) { if (ENV[x] === undefined) delete env[x] else env[x] = ENV[x] } var strings = [] for (var x in env) { strings.push(x + '=' + env[x]) } getEnvStrings.strings = strings } return getEnvStrings.strings } function writeAsciiToMemory(str, buffer, dontAddNull) { for (var i = 0; i < str.length; ++i) { HEAP8[buffer++ >> 0] = str.charCodeAt(i) } if (!dontAddNull) HEAP8[buffer >> 0] = 0 } function _environ_get(__environ, environ_buf) { var bufSize = 0 getEnvStrings().forEach(function(string, i) { var ptr = environ_buf + bufSize HEAPU32[(__environ + i * 4) >> 2] = ptr writeAsciiToMemory(string, ptr) bufSize += string.length + 1 }) return 0 } function _environ_sizes_get(penviron_count, penviron_buf_size) { var strings = getEnvStrings() HEAPU32[penviron_count >> 2] = strings.length var bufSize = 0 strings.forEach(function(string) { bufSize += string.length + 1 }) HEAPU32[penviron_buf_size >> 2] = bufSize return 0 } function _proc_exit(code) { EXITSTATUS = code if (!keepRuntimeAlive()) { if (Module['onExit']) Module['onExit'](code) ABORT = true } quit_(code, new ExitStatus(code)) } function exitJS(status, implicit) { EXITSTATUS = status _proc_exit(status) } var _exit = exitJS function _fd_close(fd) { try { var stream = SYSCALLS.getStreamFromFD(fd) FS.close(stream) return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doReadv(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.read(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr if (curr < len) break } return ret } function _fd_read(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doReadv(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function convertI32PairToI53Checked(lo, hi) { return (hi + 2097152) >>> 0 < 4194305 - !!lo ? (lo >>> 0) + hi * 4294967296 : NaN } function _fd_seek(fd, offset_low, offset_high, whence, newOffset) { try { var offset = convertI32PairToI53Checked(offset_low, offset_high) if (isNaN(offset)) return 61 var stream = SYSCALLS.getStreamFromFD(fd) FS.llseek(stream, offset, whence) ;(tempI64 = [ stream.position >>> 0, ((tempDouble = stream.position), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[newOffset >> 2] = tempI64[0]), (HEAP32[(newOffset + 4) >> 2] = tempI64[1]) if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doWritev(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.write(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr } return ret } function _fd_write(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doWritev(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function __isLeapYear(year) { return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) } function __arraySum(array, index) { var sum = 0 for (var i = 0; i <= index; sum += array[i++]) {} return sum } var __MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] var __MONTH_DAYS_REGULAR = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] function __addDays(date, days) { var newDate = new Date(date.getTime()) while (days > 0) { var leap = __isLeapYear(newDate.getFullYear()) var currentMonth = newDate.getMonth() var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth] if (days > daysInCurrentMonth - newDate.getDate()) { days -= daysInCurrentMonth - newDate.getDate() + 1 newDate.setDate(1) if (currentMonth < 11) { newDate.setMonth(currentMonth + 1) } else { newDate.setMonth(0) newDate.setFullYear(newDate.getFullYear() + 1) } } else { newDate.setDate(newDate.getDate() + days) return newDate } } return newDate } function writeArrayToMemory(array, buffer) { HEAP8.set(array, buffer) } function _strftime(s, maxsize, format, tm) { var tm_zone = HEAP32[(tm + 40) >> 2] var date = { tm_sec: HEAP32[tm >> 2], tm_min: HEAP32[(tm + 4) >> 2], tm_hour: HEAP32[(tm + 8) >> 2], tm_mday: HEAP32[(tm + 12) >> 2], tm_mon: HEAP32[(tm + 16) >> 2], tm_year: HEAP32[(tm + 20) >> 2], tm_wday: HEAP32[(tm + 24) >> 2], tm_yday: HEAP32[(tm + 28) >> 2], tm_isdst: HEAP32[(tm + 32) >> 2], tm_gmtoff: HEAP32[(tm + 36) >> 2], tm_zone: tm_zone ? UTF8ToString(tm_zone) : '', } var pattern = UTF8ToString(format) var EXPANSION_RULES_1 = { '%c': '%a %b %d %H:%M:%S %Y', '%D': '%m/%d/%y', '%F': '%Y-%m-%d', '%h': '%b', '%r': '%I:%M:%S %p', '%R': '%H:%M', '%T': '%H:%M:%S', '%x': '%m/%d/%y', '%X': '%H:%M:%S', '%Ec': '%c', '%EC': '%C', '%Ex': '%m/%d/%y', '%EX': '%H:%M:%S', '%Ey': '%y', '%EY': '%Y', '%Od': '%d', '%Oe': '%e', '%OH': '%H', '%OI': '%I', '%Om': '%m', '%OM': '%M', '%OS': '%S', '%Ou': '%u', '%OU': '%U', '%OV': '%V', '%Ow': '%w', '%OW': '%W', '%Oy': '%y', } for (var rule in EXPANSION_RULES_1) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_1[rule] ) } var WEEKDAYS = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', ] var MONTHS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ] function leadingSomething(value, digits, character) { var str = typeof value == 'number' ? value.toString() : value || '' while (str.length < digits) { str = character[0] + str } return str } function leadingNulls(value, digits) { return leadingSomething(value, digits, '0') } function compareByDay(date1, date2) { function sgn(value) { return value < 0 ? -1 : value > 0 ? 1 : 0 } var compare if ((compare = sgn(date1.getFullYear() - date2.getFullYear())) === 0) { if ((compare = sgn(date1.getMonth() - date2.getMonth())) === 0) { compare = sgn(date1.getDate() - date2.getDate()) } } return compare } function getFirstWeekStartDate(janFourth) { switch (janFourth.getDay()) { case 0: return new Date(janFourth.getFullYear() - 1, 11, 29) case 1: return janFourth case 2: return new Date(janFourth.getFullYear(), 0, 3) case 3: return new Date(janFourth.getFullYear(), 0, 2) case 4: return new Date(janFourth.getFullYear(), 0, 1) case 5: return new Date(janFourth.getFullYear() - 1, 11, 31) case 6: return new Date(janFourth.getFullYear() - 1, 11, 30) } } function getWeekBasedYear(date) { var thisDate = __addDays( new Date(date.tm_year + 1900, 0, 1), date.tm_yday ) var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4) var janFourthNextYear = new Date(thisDate.getFullYear() + 1, 0, 4) var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear) var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear) if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { return thisDate.getFullYear() + 1 } return thisDate.getFullYear() } return thisDate.getFullYear() - 1 } var EXPANSION_RULES_2 = { '%a': function(date) { return WEEKDAYS[date.tm_wday].substring(0, 3) }, '%A': function(date) { return WEEKDAYS[date.tm_wday] }, '%b': function(date) { return MONTHS[date.tm_mon].substring(0, 3) }, '%B': function(date) { return MONTHS[date.tm_mon] }, '%C': function(date) { var year = date.tm_year + 1900 return leadingNulls((year / 100) | 0, 2) }, '%d': function(date) { return leadingNulls(date.tm_mday, 2) }, '%e': function(date) { return leadingSomething(date.tm_mday, 2, ' ') }, '%g': function(date) { return getWeekBasedYear(date) .toString() .substring(2) }, '%G': function(date) { return getWeekBasedYear(date) }, '%H': function(date) { return leadingNulls(date.tm_hour, 2) }, '%I': function(date) { var twelveHour = date.tm_hour if (twelveHour == 0) twelveHour = 12 else if (twelveHour > 12) twelveHour -= 12 return leadingNulls(twelveHour, 2) }, '%j': function(date) { return leadingNulls( date.tm_mday + __arraySum( __isLeapYear(date.tm_year + 1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon - 1 ), 3 ) }, '%m': function(date) { return leadingNulls(date.tm_mon + 1, 2) }, '%M': function(date) { return leadingNulls(date.tm_min, 2) }, '%n': function() { return '\n' }, '%p': function(date) { if (date.tm_hour >= 0 && date.tm_hour < 12) { return 'AM' } return 'PM' }, '%S': function(date) { return leadingNulls(date.tm_sec, 2) }, '%t': function() { return '\t' }, '%u': function(date) { return date.tm_wday || 7 }, '%U': function(date) { var days = date.tm_yday + 7 - date.tm_wday return leadingNulls(Math.floor(days / 7), 2) }, '%V': function(date) { var val = Math.floor( (date.tm_yday + 7 - ((date.tm_wday + 6) % 7)) / 7 ) if ((date.tm_wday + 371 - date.tm_yday - 2) % 7 <= 2) { val++ } if (!val) { val = 52 var dec31 = (date.tm_wday + 7 - date.tm_yday - 1) % 7 if ( dec31 == 4 || (dec31 == 5 && __isLeapYear((date.tm_year % 400) - 1)) ) { val++ } } else if (val == 53) { var jan1 = (date.tm_wday + 371 - date.tm_yday) % 7 if (jan1 != 4 && (jan1 != 3 || !__isLeapYear(date.tm_year))) val = 1 } return leadingNulls(val, 2) }, '%w': function(date) { return date.tm_wday }, '%W': function(date) { var days = date.tm_yday + 7 - ((date.tm_wday + 6) % 7) return leadingNulls(Math.floor(days / 7), 2) }, '%y': function(date) { return (date.tm_year + 1900).toString().substring(2) }, '%Y': function(date) { return date.tm_year + 1900 }, '%z': function(date) { var off = date.tm_gmtoff var ahead = off >= 0 off = Math.abs(off) / 60 off = (off / 60) * 100 + (off % 60) return (ahead ? '+' : '-') + String('0000' + off).slice(-4) }, '%Z': function(date) { return date.tm_zone }, '%%': function() { return '%' }, } pattern = pattern.replace(/%%/g, '\0\0') for (var rule in EXPANSION_RULES_2) { if (pattern.includes(rule)) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date) ) } } pattern = pattern.replace(/\0\0/g, '%') var bytes = intArrayFromString(pattern, false) if (bytes.length > maxsize) { return 0 } writeArrayToMemory(bytes, s) return bytes.length - 1 } function _strftime_l(s, maxsize, format, tm, loc) { return _strftime(s, maxsize, format, tm) } function handleException(e) { if (e instanceof ExitStatus || e == 'unwind') { return EXITSTATUS } quit_(1, e) } function allocateUTF8OnStack(str) { var size = lengthBytesUTF8(str) + 1 var ret = stackAlloc(size) stringToUTF8Array(str, HEAP8, ret, size) return ret } function getCFunc(ident) { var func = Module['_' + ident] return func } function ccall(ident, returnType, argTypes, args, opts) { var toC = { string: str => { var ret = 0 if (str !== null && str !== undefined && str !== 0) { var len = (str.length << 2) + 1 ret = stackAlloc(len) stringToUTF8(str, ret, len) } return ret }, array: arr => { var ret = stackAlloc(arr.length) writeArrayToMemory(arr, ret) return ret }, } function convertReturnValue(ret) { if (returnType === 'string') { return UTF8ToString(ret) } if (returnType === 'boolean') return Boolean(ret) return ret } var func = getCFunc(ident) var cArgs = [] var stack = 0 if (args) { for (var i = 0; i < args.length; i++) { var converter = toC[argTypes[i]] if (converter) { if (stack === 0) stack = stackSave() cArgs[i] = converter(args[i]) } else { cArgs[i] = args[i] } } } var ret = func.apply(null, cArgs) function onDone(ret) { if (stack !== 0) stackRestore(stack) return convertReturnValue(ret) } ret = onDone(ret) return ret } function cwrap(ident, returnType, argTypes, opts) { argTypes = argTypes || [] var numericArgs = argTypes.every( type => type === 'number' || type === 'boolean' ) var numericRet = returnType !== 'string' if (numericRet && numericArgs && !opts) { return getCFunc(ident) } return function() { return ccall(ident, returnType, argTypes, arguments, opts) } } function AsciiToString(ptr) { var str = '' while (1) { var ch = HEAPU8[ptr++ >> 0] if (!ch) return str str += String.fromCharCode(ch) } } var FSNode = function(parent, name, mode, rdev) { if (!parent) { parent = this } this.parent = parent this.mount = parent.mount this.mounted = null this.id = FS.nextInode++ this.name = name this.mode = mode this.node_ops = {} this.stream_ops = {} this.rdev = rdev } var readMode = 292 | 73 var writeMode = 146 Object.defineProperties(FSNode.prototype, { read: { get: function() { return (this.mode & readMode) === readMode }, set: function(val) { val ? (this.mode |= readMode) : (this.mode &= ~readMode) }, }, write: { get: function() { return (this.mode & writeMode) === writeMode }, set: function(val) { val ? (this.mode |= writeMode) : (this.mode &= ~writeMode) }, }, isFolder: { get: function() { return FS.isDir(this.mode) }, }, isDevice: { get: function() { return FS.isChrdev(this.mode) }, }, }) FS.FSNode = FSNode FS.staticInit() Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_unlink'] = FS.unlink Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice if (ENVIRONMENT_IS_NODE) { NODEFS.staticInit() } ERRNO_CODES = { EPERM: 63, ENOENT: 44, ESRCH: 71, EINTR: 27, EIO: 29, ENXIO: 60, E2BIG: 1, ENOEXEC: 45, EBADF: 8, ECHILD: 12, EAGAIN: 6, EWOULDBLOCK: 6, ENOMEM: 48, EACCES: 2, EFAULT: 21, ENOTBLK: 105, EBUSY: 10, EEXIST: 20, EXDEV: 75, ENODEV: 43, ENOTDIR: 54, EISDIR: 31, EINVAL: 28, ENFILE: 41, EMFILE: 33, ENOTTY: 59, ETXTBSY: 74, EFBIG: 22, ENOSPC: 51, ESPIPE: 70, EROFS: 69, EMLINK: 34, EPIPE: 64, EDOM: 18, ERANGE: 68, ENOMSG: 49, EIDRM: 24, ECHRNG: 106, EL2NSYNC: 156, EL3HLT: 107, EL3RST: 108, ELNRNG: 109, EUNATCH: 110, ENOCSI: 111, EL2HLT: 112, EDEADLK: 16, ENOLCK: 46, EBADE: 113, EBADR: 114, EXFULL: 115, ENOANO: 104, EBADRQC: 103, EBADSLT: 102, EDEADLOCK: 16, EBFONT: 101, ENOSTR: 100, ENODATA: 116, ETIME: 117, ENOSR: 118, ENONET: 119, ENOPKG: 120, EREMOTE: 121, ENOLINK: 47, EADV: 122, ESRMNT: 123, ECOMM: 124, EPROTO: 65, EMULTIHOP: 36, EDOTDOT: 125, EBADMSG: 9, ENOTUNIQ: 126, EBADFD: 127, EREMCHG: 128, ELIBACC: 129, ELIBBAD: 130, ELIBSCN: 131, ELIBMAX: 132, ELIBEXEC: 133, ENOSYS: 52, ENOTEMPTY: 55, ENAMETOOLONG: 37, ELOOP: 32, EOPNOTSUPP: 138, EPFNOSUPPORT: 139, ECONNRESET: 15, ENOBUFS: 42, EAFNOSUPPORT: 5, EPROTOTYPE: 67, ENOTSOCK: 57, ENOPROTOOPT: 50, ESHUTDOWN: 140, ECONNREFUSED: 14, EADDRINUSE: 3, ECONNABORTED: 13, ENETUNREACH: 40, ENETDOWN: 38, ETIMEDOUT: 73, EHOSTDOWN: 142, EHOSTUNREACH: 23, EINPROGRESS: 26, EALREADY: 7, EDESTADDRREQ: 17, EMSGSIZE: 35, EPROTONOSUPPORT: 66, ESOCKTNOSUPPORT: 137, EADDRNOTAVAIL: 4, ENETRESET: 39, EISCONN: 30, ENOTCONN: 53, ETOOMANYREFS: 141, EUSERS: 136, EDQUOT: 19, ESTALE: 72, ENOTSUP: 138, ENOMEDIUM: 148, EILSEQ: 25, EOVERFLOW: 61, ECANCELED: 11, ENOTRECOVERABLE: 56, EOWNERDEAD: 62, ESTRPIPE: 135, } var asmLibraryArg = { a: ___cxa_throw, d: ___syscall_fcntl64, r: ___syscall_getcwd, i: ___syscall_ioctl, j: ___syscall_openat, n: ___syscall_readlinkat, o: ___syscall_stat64, b: _abort, f: _emscripten_memcpy_big, m: _emscripten_resize_heap, p: _environ_get, q: _environ_sizes_get, c: _exit, e: _fd_close, h: _fd_read, k: _fd_seek, g: _fd_write, l: _strftime_l, } var asm = createWasm() var ___wasm_call_ctors = (Module['___wasm_call_ctors'] = function() { return (___wasm_call_ctors = Module['___wasm_call_ctors'] = Module['asm']['t']).apply(null, arguments) }) var _main = (Module['_main'] = function() { return (_main = Module['_main'] = Module['asm']['u']).apply( null, arguments ) }) var ___errno_location = (Module['___errno_location'] = function() { return (___errno_location = Module['___errno_location'] = Module['asm']['v']).apply(null, arguments) }) var _itk_wasm_input_array_alloc = (Module[ '_itk_wasm_input_array_alloc' ] = function() { return (_itk_wasm_input_array_alloc = Module[ '_itk_wasm_input_array_alloc' ] = Module['asm']['w']).apply(null, arguments) }) var _itk_wasm_input_json_alloc = (Module[ '_itk_wasm_input_json_alloc' ] = function() { return (_itk_wasm_input_json_alloc = Module[ '_itk_wasm_input_json_alloc' ] = Module['asm']['x']).apply(null, arguments) }) var _itk_wasm_output_json_address = (Module[ '_itk_wasm_output_json_address' ] = function() { return (_itk_wasm_output_json_address = Module[ '_itk_wasm_output_json_address' ] = Module['asm']['y']).apply(null, arguments) }) var _itk_wasm_output_json_size = (Module[ '_itk_wasm_output_json_size' ] = function() { return (_itk_wasm_output_json_size = Module[ '_itk_wasm_output_json_size' ] = Module['asm']['z']).apply(null, arguments) }) var _itk_wasm_output_array_address = (Module[ '_itk_wasm_output_array_address' ] = function() { return (_itk_wasm_output_array_address = Module[ '_itk_wasm_output_array_address' ] = Module['asm']['A']).apply(null, arguments) }) var _itk_wasm_output_array_size = (Module[ '_itk_wasm_output_array_size' ] = function() { return (_itk_wasm_output_array_size = Module[ '_itk_wasm_output_array_size' ] = Module['asm']['B']).apply(null, arguments) }) var _itk_wasm_free_all = (Module['_itk_wasm_free_all'] = function() { return (_itk_wasm_free_all = Module['_itk_wasm_free_all'] = Module['asm']['C']).apply(null, arguments) }) var stackSave = (Module['stackSave'] = function() { return (stackSave = Module['stackSave'] = Module['asm']['E']).apply( null, arguments ) }) var stackRestore = (Module['stackRestore'] = function() { return (stackRestore = Module['stackRestore'] = Module['asm']['F']).apply( null, arguments ) }) var stackAlloc = (Module['stackAlloc'] = function() { return (stackAlloc = Module['stackAlloc'] = Module['asm']['G']).apply( null, arguments ) }) var ___cxa_is_pointer_type = (Module[ '___cxa_is_pointer_type' ] = function() { return (___cxa_is_pointer_type = Module['___cxa_is_pointer_type'] = Module['asm']['H']).apply(null, arguments) }) Module['addRunDependency'] = addRunDependency Module['removeRunDependency'] = removeRunDependency Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice Module['FS_unlink'] = FS.unlink Module['callMain'] = callMain Module['ccall'] = ccall Module['cwrap'] = cwrap Module['AsciiToString'] = AsciiToString Module['writeArrayToMemory'] = writeArrayToMemory Module['writeAsciiToMemory'] = writeAsciiToMemory var calledRun dependenciesFulfilled = function runCaller() { if (!calledRun) run() if (!calledRun) dependenciesFulfilled = runCaller } function callMain(args) { var entryFunction = Module['_main'] args = args || [] args.unshift(thisProgram) var argc = args.length var argv = stackAlloc((argc + 1) * 4) var argv_ptr = argv >> 2 args.forEach(arg => { HEAP32[argv_ptr++] = allocateUTF8OnStack(arg) }) HEAP32[argv_ptr] = 0 try { var ret = entryFunction(argc, argv) exitJS(ret, true) return ret } catch (e) { return handleException(e) } } function run(args) { args = args || arguments_ if (runDependencies > 0) { return } preRun() if (runDependencies > 0) { return } function doRun() { if (calledRun) return calledRun = true Module['calledRun'] = true if (ABORT) return initRuntime() preMain() readyPromiseResolve(Module) if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized']() if (shouldRunNow) callMain(args) postRun() } if (Module['setStatus']) { Module['setStatus']('Running...') setTimeout(function() { setTimeout(function() { Module['setStatus']('') }, 1) doRun() }, 1) } else { doRun() } } if (Module['preInit']) { if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']] while (Module['preInit'].length > 0) { Module['preInit'].pop()() } } var shouldRunNow = false if (Module['noInitialRun']) shouldRunNow = false run() Module.mountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) if (FS.isDir(containingDir) || containingDir === '/') { return } var currentDir = '/' var splitContainingDir = containingDir.split(path.sep) for (var ii = 1; ii < splitContainingDir.length; ii++) { currentDir += splitContainingDir[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } FS.mount(NODEFS, { root: containingDir }, currentDir) return currentDir + path.basename(filePath) } Module.unmountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) FS.unmount(containingDir) } Module.fs_mkdirs = function(dirs) { var currentDir = '/' var splitDirs = dirs.split('/') for (var ii = 1; ii < splitDirs.length; ++ii) { currentDir += splitDirs[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } } Module.fs_readFile = function(path, opts) { return FS.readFile(path, opts) } Module.fs_writeFile = function(path, data, opts) { return FS.writeFile(path, data, opts) } Module.fs_unlink = function(path) { return FS.unlink(path) } Module.fs_open = function(path, flags, mode) { return FS.open(path, flags, mode) } Module.fs_stat = function(path) { return FS.stat(path) } Module.fs_read = function(stream, buffer, offset, length, position) { return FS.read(stream, buffer, offset, length, position) } Module.fs_close = function(stream) { return FS.close(stream) } return ResampleLabelImage.ready } })() export default ResampleLabelImage ================================================ FILE: src/IO/ResampleLabelImage/emscripten-build/ResampleLabelImage.umd.js ================================================ var ResampleLabelImage = (() => { var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename return function(ResampleLabelImage) { ResampleLabelImage = ResampleLabelImage || {} var Module = typeof ResampleLabelImage != 'undefined' ? ResampleLabelImage : {} var readyPromiseResolve, readyPromiseReject Module['ready'] = new Promise(function(resolve, reject) { readyPromiseResolve = resolve readyPromiseReject = reject }) var mStdout = null var mStderr = null Module['resetModuleStdout'] = function() { mStdout = '' } Module['resetModuleStderr'] = function() { mStderr = '' } Module['print'] = function(text) { console.log(text) mStdout += text + '\n' } Module['printErr'] = function(text) { console.error(text) mStderr += text + '\n' } Module['getModuleStdout'] = function() { return mStdout } Module['getModuleStderr'] = function() { return mStderr } var moduleOverrides = Object.assign({}, Module) var arguments_ = [] var thisProgram = './this.program' var quit_ = (status, toThrow) => { throw toThrow } var ENVIRONMENT_IS_WEB = typeof window == 'object' var ENVIRONMENT_IS_WORKER = typeof importScripts == 'function' var ENVIRONMENT_IS_NODE = typeof process == 'object' && typeof process.versions == 'object' && typeof process.versions.node == 'string' var scriptDirectory = '' function locateFile(path) { if (Module['locateFile']) { return Module['locateFile'](path, scriptDirectory) } return scriptDirectory + path } var read_, readAsync, readBinary, setWindowTitle function logExceptionOnExit(e) { if (e instanceof ExitStatus) return let toLog = e err('exiting due to exception: ' + toLog) } if (ENVIRONMENT_IS_NODE) { var fs = require('fs') var nodePath = require('path') if (ENVIRONMENT_IS_WORKER) { scriptDirectory = nodePath.dirname(scriptDirectory) + '/' } else { scriptDirectory = __dirname + '/' } read_ = (filename, binary) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) return fs.readFileSync(filename, binary ? undefined : 'utf8') } readBinary = filename => { var ret = read_(filename, true) if (!ret.buffer) { ret = new Uint8Array(ret) } return ret } readAsync = (filename, onload, onerror) => { filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename) fs.readFile(filename, function(err, data) { if (err) onerror(err) else onload(data.buffer) }) } if (process['argv'].length > 1) { thisProgram = process['argv'][1].replace(/\\/g, '/') } arguments_ = process['argv'].slice(2) process['on']('uncaughtException', function(ex) { if (!(ex instanceof ExitStatus)) { throw ex } }) process['on']('unhandledRejection', function(reason) { throw reason }) quit_ = (status, toThrow) => { if (keepRuntimeAlive()) { process['exitCode'] = status throw toThrow } logExceptionOnExit(toThrow) process['exit'](status) } Module['inspect'] = function() { return '[Emscripten Module object]' } } else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { if (ENVIRONMENT_IS_WORKER) { scriptDirectory = self.location.href } else if (typeof document != 'undefined' && document.currentScript) { scriptDirectory = document.currentScript.src } if (_scriptDir) { scriptDirectory = _scriptDir } if (scriptDirectory.indexOf('blob:') !== 0) { scriptDirectory = scriptDirectory.substr( 0, scriptDirectory.replace(/[?#].*/, '').lastIndexOf('/') + 1 ) } else { scriptDirectory = '' } { read_ = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.send(null) return xhr.responseText } if (ENVIRONMENT_IS_WORKER) { readBinary = url => { var xhr = new XMLHttpRequest() xhr.open('GET', url, false) xhr.responseType = 'arraybuffer' xhr.send(null) return new Uint8Array(xhr.response) } } readAsync = (url, onload, onerror) => { var xhr = new XMLHttpRequest() xhr.open('GET', url, true) xhr.responseType = 'arraybuffer' xhr.onload = () => { if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { onload(xhr.response) return } onerror() } xhr.onerror = onerror xhr.send(null) } } setWindowTitle = title => (document.title = title) } else { } var out = Module['print'] || console.log.bind(console) var err = Module['printErr'] || console.warn.bind(console) Object.assign(Module, moduleOverrides) moduleOverrides = null if (Module['arguments']) arguments_ = Module['arguments'] if (Module['thisProgram']) thisProgram = Module['thisProgram'] if (Module['quit']) quit_ = Module['quit'] var wasmBinary if (Module['wasmBinary']) wasmBinary = Module['wasmBinary'] var noExitRuntime = Module['noExitRuntime'] || true if (typeof WebAssembly != 'object') { abort('no native wasm support detected') } var wasmMemory var ABORT = false var EXITSTATUS function assert(condition, text) { if (!condition) { abort(text) } } var UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { var endIdx = idx + maxBytesToRead var endPtr = idx while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)) } var str = '' while (idx < endPtr) { var u0 = heapOrArray[idx++] if (!(u0 & 128)) { str += String.fromCharCode(u0) continue } var u1 = heapOrArray[idx++] & 63 if ((u0 & 224) == 192) { str += String.fromCharCode(((u0 & 31) << 6) | u1) continue } var u2 = heapOrArray[idx++] & 63 if ((u0 & 240) == 224) { u0 = ((u0 & 15) << 12) | (u1 << 6) | u2 } else { u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63) } if (u0 < 65536) { str += String.fromCharCode(u0) } else { var ch = u0 - 65536 str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)) } } return str } function UTF8ToString(ptr, maxBytesToRead) { return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '' } function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { if (!(maxBytesToWrite > 0)) return 0 var startIdx = outIdx var endIdx = outIdx + maxBytesToWrite - 1 for (var i = 0; i < str.length; ++i) { var u = str.charCodeAt(i) if (u >= 55296 && u <= 57343) { var u1 = str.charCodeAt(++i) u = (65536 + ((u & 1023) << 10)) | (u1 & 1023) } if (u <= 127) { if (outIdx >= endIdx) break heap[outIdx++] = u } else if (u <= 2047) { if (outIdx + 1 >= endIdx) break heap[outIdx++] = 192 | (u >> 6) heap[outIdx++] = 128 | (u & 63) } else if (u <= 65535) { if (outIdx + 2 >= endIdx) break heap[outIdx++] = 224 | (u >> 12) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } else { if (outIdx + 3 >= endIdx) break heap[outIdx++] = 240 | (u >> 18) heap[outIdx++] = 128 | ((u >> 12) & 63) heap[outIdx++] = 128 | ((u >> 6) & 63) heap[outIdx++] = 128 | (u & 63) } } heap[outIdx] = 0 return outIdx - startIdx } function stringToUTF8(str, outPtr, maxBytesToWrite) { return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite) } function lengthBytesUTF8(str) { var len = 0 for (var i = 0; i < str.length; ++i) { var c = str.charCodeAt(i) if (c <= 127) { len++ } else if (c <= 2047) { len += 2 } else if (c >= 55296 && c <= 57343) { len += 4 ++i } else { len += 3 } } return len } var buffer, HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64 function updateGlobalBufferAndViews(buf) { buffer = buf Module['HEAP8'] = HEAP8 = new Int8Array(buf) Module['HEAP16'] = HEAP16 = new Int16Array(buf) Module['HEAP32'] = HEAP32 = new Int32Array(buf) Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf) Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf) Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf) Module['HEAPF32'] = HEAPF32 = new Float32Array(buf) Module['HEAPF64'] = HEAPF64 = new Float64Array(buf) } var INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 16777216 var wasmTable var __ATPRERUN__ = [] var __ATINIT__ = [] var __ATMAIN__ = [] var __ATPOSTRUN__ = [] var runtimeInitialized = false function keepRuntimeAlive() { return noExitRuntime } function preRun() { if (Module['preRun']) { if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']] while (Module['preRun'].length) { addOnPreRun(Module['preRun'].shift()) } } callRuntimeCallbacks(__ATPRERUN__) } function initRuntime() { runtimeInitialized = true if (!Module['noFSInit'] && !FS.init.initialized) FS.init() FS.ignorePermissions = false TTY.init() callRuntimeCallbacks(__ATINIT__) } function preMain() { callRuntimeCallbacks(__ATMAIN__) } function postRun() { if (Module['postRun']) { if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']] while (Module['postRun'].length) { addOnPostRun(Module['postRun'].shift()) } } callRuntimeCallbacks(__ATPOSTRUN__) } function addOnPreRun(cb) { __ATPRERUN__.unshift(cb) } function addOnInit(cb) { __ATINIT__.unshift(cb) } function addOnPostRun(cb) { __ATPOSTRUN__.unshift(cb) } var runDependencies = 0 var runDependencyWatcher = null var dependenciesFulfilled = null function getUniqueRunDependency(id) { return id } function addRunDependency(id) { runDependencies++ if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } } function removeRunDependency(id) { runDependencies-- if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies) } if (runDependencies == 0) { if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher) runDependencyWatcher = null } if (dependenciesFulfilled) { var callback = dependenciesFulfilled dependenciesFulfilled = null callback() } } } function abort(what) { if (Module['onAbort']) { Module['onAbort'](what) } what = 'Aborted(' + what + ')' err(what) ABORT = true EXITSTATUS = 1 what += '. Build with -sASSERTIONS for more info.' var e = new WebAssembly.RuntimeError(what) readyPromiseReject(e) throw e } var dataURIPrefix = 'data:application/octet-stream;base64,' function isDataURI(filename) { return filename.startsWith(dataURIPrefix) } function isFileURI(filename) { return filename.startsWith('file://') } var wasmBinaryFile wasmBinaryFile = 'ResampleLabelImage.umd.wasm' if (!isDataURI(wasmBinaryFile)) { wasmBinaryFile = locateFile(wasmBinaryFile) } function getBinary(file) { try { if (file == wasmBinaryFile && wasmBinary) { return new Uint8Array(wasmBinary) } if (readBinary) { return readBinary(file) } throw 'both async and sync fetching of the wasm failed' } catch (err) { abort(err) } } function getBinaryPromise() { if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { if (typeof fetch == 'function' && !isFileURI(wasmBinaryFile)) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }) .then(function(response) { if (!response['ok']) { throw "failed to load wasm binary file at '" + wasmBinaryFile + "'" } return response['arrayBuffer']() }) .catch(function() { return getBinary(wasmBinaryFile) }) } else { if (readAsync) { return new Promise(function(resolve, reject) { readAsync( wasmBinaryFile, function(response) { resolve(new Uint8Array(response)) }, reject ) }) } } } return Promise.resolve().then(function() { return getBinary(wasmBinaryFile) }) } function createWasm() { var info = { a: asmLibraryArg } function receiveInstance(instance, module) { var exports = instance.exports Module['asm'] = exports wasmMemory = Module['asm']['s'] updateGlobalBufferAndViews(wasmMemory.buffer) wasmTable = Module['asm']['D'] addOnInit(Module['asm']['t']) removeRunDependency('wasm-instantiate') } addRunDependency('wasm-instantiate') function receiveInstantiationResult(result) { receiveInstance(result['instance']) } function instantiateArrayBuffer(receiver) { return getBinaryPromise() .then(function(binary) { return WebAssembly.instantiate(binary, info) }) .then(function(instance) { return instance }) .then(receiver, function(reason) { err('failed to asynchronously prepare wasm: ' + reason) abort(reason) }) } function instantiateAsync() { if ( !wasmBinary && typeof WebAssembly.instantiateStreaming == 'function' && !isDataURI(wasmBinaryFile) && !isFileURI(wasmBinaryFile) && !ENVIRONMENT_IS_NODE && typeof fetch == 'function' ) { return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then( function(response) { var result = WebAssembly.instantiateStreaming(response, info) return result.then(receiveInstantiationResult, function(reason) { err('wasm streaming compile failed: ' + reason) err('falling back to ArrayBuffer instantiation') return instantiateArrayBuffer(receiveInstantiationResult) }) } ) } else { return instantiateArrayBuffer(receiveInstantiationResult) } } if (Module['instantiateWasm']) { try { var exports = Module['instantiateWasm'](info, receiveInstance) return exports } catch (e) { err('Module.instantiateWasm callback failed with error: ' + e) readyPromiseReject(e) } } instantiateAsync().catch(readyPromiseReject) return {} } var tempDouble var tempI64 function ExitStatus(status) { this.name = 'ExitStatus' this.message = 'Program terminated with exit(' + status + ')' this.status = status } function callRuntimeCallbacks(callbacks) { while (callbacks.length > 0) { callbacks.shift()(Module) } } function ExceptionInfo(excPtr) { this.excPtr = excPtr this.ptr = excPtr - 24 this.set_type = function(type) { HEAPU32[(this.ptr + 4) >> 2] = type } this.get_type = function() { return HEAPU32[(this.ptr + 4) >> 2] } this.set_destructor = function(destructor) { HEAPU32[(this.ptr + 8) >> 2] = destructor } this.get_destructor = function() { return HEAPU32[(this.ptr + 8) >> 2] } this.set_refcount = function(refcount) { HEAP32[this.ptr >> 2] = refcount } this.set_caught = function(caught) { caught = caught ? 1 : 0 HEAP8[(this.ptr + 12) >> 0] = caught } this.get_caught = function() { return HEAP8[(this.ptr + 12) >> 0] != 0 } this.set_rethrown = function(rethrown) { rethrown = rethrown ? 1 : 0 HEAP8[(this.ptr + 13) >> 0] = rethrown } this.get_rethrown = function() { return HEAP8[(this.ptr + 13) >> 0] != 0 } this.init = function(type, destructor) { this.set_adjusted_ptr(0) this.set_type(type) this.set_destructor(destructor) this.set_refcount(0) this.set_caught(false) this.set_rethrown(false) } this.add_ref = function() { var value = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = value + 1 } this.release_ref = function() { var prev = HEAP32[this.ptr >> 2] HEAP32[this.ptr >> 2] = prev - 1 return prev === 1 } this.set_adjusted_ptr = function(adjustedPtr) { HEAPU32[(this.ptr + 16) >> 2] = adjustedPtr } this.get_adjusted_ptr = function() { return HEAPU32[(this.ptr + 16) >> 2] } this.get_exception_ptr = function() { var isPointer = ___cxa_is_pointer_type(this.get_type()) if (isPointer) { return HEAPU32[this.excPtr >> 2] } var adjusted = this.get_adjusted_ptr() if (adjusted !== 0) return adjusted return this.excPtr } } var exceptionLast = 0 var uncaughtExceptionCount = 0 function ___cxa_throw(ptr, type, destructor) { var info = new ExceptionInfo(ptr) info.init(type, destructor) exceptionLast = ptr uncaughtExceptionCount++ throw ptr } function setErrNo(value) { HEAP32[___errno_location() >> 2] = value return value } var PATH = { isAbs: path => path.charAt(0) === '/', splitPath: filename => { var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/ return splitPathRe.exec(filename).slice(1) }, normalizeArray: (parts, allowAboveRoot) => { var up = 0 for (var i = parts.length - 1; i >= 0; i--) { var last = parts[i] if (last === '.') { parts.splice(i, 1) } else if (last === '..') { parts.splice(i, 1) up++ } else if (up) { parts.splice(i, 1) up-- } } if (allowAboveRoot) { for (; up; up--) { parts.unshift('..') } } return parts }, normalize: path => { var isAbsolute = PATH.isAbs(path), trailingSlash = path.substr(-1) === '/' path = PATH.normalizeArray( path.split('/').filter(p => !!p), !isAbsolute ).join('/') if (!path && !isAbsolute) { path = '.' } if (path && trailingSlash) { path += '/' } return (isAbsolute ? '/' : '') + path }, dirname: path => { var result = PATH.splitPath(path), root = result[0], dir = result[1] if (!root && !dir) { return '.' } if (dir) { dir = dir.substr(0, dir.length - 1) } return root + dir }, basename: path => { if (path === '/') return '/' path = PATH.normalize(path) path = path.replace(/\/$/, '') var lastSlash = path.lastIndexOf('/') if (lastSlash === -1) return path return path.substr(lastSlash + 1) }, join: function() { var paths = Array.prototype.slice.call(arguments) return PATH.normalize(paths.join('/')) }, join2: (l, r) => { return PATH.normalize(l + '/' + r) }, } function getRandomDevice() { if ( typeof crypto == 'object' && typeof crypto['getRandomValues'] == 'function' ) { var randomBuffer = new Uint8Array(1) return () => { crypto.getRandomValues(randomBuffer) return randomBuffer[0] } } else if (ENVIRONMENT_IS_NODE) { try { var crypto_module = require('crypto') return () => crypto_module['randomBytes'](1)[0] } catch (e) {} } return () => abort('randomDevice') } var PATH_FS = { resolve: function() { var resolvedPath = '', resolvedAbsolute = false for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { var path = i >= 0 ? arguments[i] : FS.cwd() if (typeof path != 'string') { throw new TypeError('Arguments to path.resolve must be strings') } else if (!path) { return '' } resolvedPath = path + '/' + resolvedPath resolvedAbsolute = PATH.isAbs(path) } resolvedPath = PATH.normalizeArray( resolvedPath.split('/').filter(p => !!p), !resolvedAbsolute ).join('/') return (resolvedAbsolute ? '/' : '') + resolvedPath || '.' }, relative: (from, to) => { from = PATH_FS.resolve(from).substr(1) to = PATH_FS.resolve(to).substr(1) function trim(arr) { var start = 0 for (; start < arr.length; start++) { if (arr[start] !== '') break } var end = arr.length - 1 for (; end >= 0; end--) { if (arr[end] !== '') break } if (start > end) return [] return arr.slice(start, end - start + 1) } var fromParts = trim(from.split('/')) var toParts = trim(to.split('/')) var length = Math.min(fromParts.length, toParts.length) var samePartsLength = length for (var i = 0; i < length; i++) { if (fromParts[i] !== toParts[i]) { samePartsLength = i break } } var outputParts = [] for (var i = samePartsLength; i < fromParts.length; i++) { outputParts.push('..') } outputParts = outputParts.concat(toParts.slice(samePartsLength)) return outputParts.join('/') }, } function intArrayFromString(stringy, dontAddNull, length) { var len = length > 0 ? length : lengthBytesUTF8(stringy) + 1 var u8array = new Array(len) var numBytesWritten = stringToUTF8Array( stringy, u8array, 0, u8array.length ) if (dontAddNull) u8array.length = numBytesWritten return u8array } var TTY = { ttys: [], init: function() {}, shutdown: function() {}, register: function(dev, ops) { TTY.ttys[dev] = { input: [], output: [], ops: ops } FS.registerDevice(dev, TTY.stream_ops) }, stream_ops: { open: function(stream) { var tty = TTY.ttys[stream.node.rdev] if (!tty) { throw new FS.ErrnoError(43) } stream.tty = tty stream.seekable = false }, close: function(stream) { stream.tty.ops.fsync(stream.tty) }, fsync: function(stream) { stream.tty.ops.fsync(stream.tty) }, read: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.get_char) { throw new FS.ErrnoError(60) } var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = stream.tty.ops.get_char(stream.tty) } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: function(stream, buffer, offset, length, pos) { if (!stream.tty || !stream.tty.ops.put_char) { throw new FS.ErrnoError(60) } try { for (var i = 0; i < length; i++) { stream.tty.ops.put_char(stream.tty, buffer[offset + i]) } } catch (e) { throw new FS.ErrnoError(29) } if (length) { stream.node.timestamp = Date.now() } return i }, }, default_tty_ops: { get_char: function(tty) { if (!tty.input.length) { var result = null if (ENVIRONMENT_IS_NODE) { var BUFSIZE = 256 var buf = Buffer.alloc(BUFSIZE) var bytesRead = 0 try { bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE, -1) } catch (e) { if (e.toString().includes('EOF')) bytesRead = 0 else throw e } if (bytesRead > 0) { result = buf.slice(0, bytesRead).toString('utf-8') } else { result = null } } else if ( typeof window != 'undefined' && typeof window.prompt == 'function' ) { result = window.prompt('Input: ') if (result !== null) { result += '\n' } } else if (typeof readline == 'function') { result = readline() if (result !== null) { result += '\n' } } if (!result) { return null } tty.input = intArrayFromString(result, true) } return tty.input.shift() }, put_char: function(tty, val) { if (val === null || val === 10) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { out(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, default_tty1_ops: { put_char: function(tty, val) { if (val === null || val === 10) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } else { if (val != 0) tty.output.push(val) } }, fsync: function(tty) { if (tty.output && tty.output.length > 0) { err(UTF8ArrayToString(tty.output, 0)) tty.output = [] } }, }, } function mmapAlloc(size) { abort() } var MEMFS = { ops_table: null, mount: function(mount) { return MEMFS.createNode(null, '/', 16384 | 511, 0) }, createNode: function(parent, name, mode, dev) { if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { throw new FS.ErrnoError(63) } if (!MEMFS.ops_table) { MEMFS.ops_table = { dir: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, lookup: MEMFS.node_ops.lookup, mknod: MEMFS.node_ops.mknod, rename: MEMFS.node_ops.rename, unlink: MEMFS.node_ops.unlink, rmdir: MEMFS.node_ops.rmdir, readdir: MEMFS.node_ops.readdir, symlink: MEMFS.node_ops.symlink, }, stream: { llseek: MEMFS.stream_ops.llseek }, }, file: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: { llseek: MEMFS.stream_ops.llseek, read: MEMFS.stream_ops.read, write: MEMFS.stream_ops.write, allocate: MEMFS.stream_ops.allocate, mmap: MEMFS.stream_ops.mmap, msync: MEMFS.stream_ops.msync, }, }, link: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, readlink: MEMFS.node_ops.readlink, }, stream: {}, }, chrdev: { node: { getattr: MEMFS.node_ops.getattr, setattr: MEMFS.node_ops.setattr, }, stream: FS.chrdev_stream_ops, }, } } var node = FS.createNode(parent, name, mode, dev) if (FS.isDir(node.mode)) { node.node_ops = MEMFS.ops_table.dir.node node.stream_ops = MEMFS.ops_table.dir.stream node.contents = {} } else if (FS.isFile(node.mode)) { node.node_ops = MEMFS.ops_table.file.node node.stream_ops = MEMFS.ops_table.file.stream node.usedBytes = 0 node.contents = null } else if (FS.isLink(node.mode)) { node.node_ops = MEMFS.ops_table.link.node node.stream_ops = MEMFS.ops_table.link.stream } else if (FS.isChrdev(node.mode)) { node.node_ops = MEMFS.ops_table.chrdev.node node.stream_ops = MEMFS.ops_table.chrdev.stream } node.timestamp = Date.now() if (parent) { parent.contents[name] = node parent.timestamp = node.timestamp } return node }, getFileDataAsTypedArray: function(node) { if (!node.contents) return new Uint8Array(0) if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes) return new Uint8Array(node.contents) }, expandFileStorage: function(node, newCapacity) { var prevCapacity = node.contents ? node.contents.length : 0 if (prevCapacity >= newCapacity) return var CAPACITY_DOUBLING_MAX = 1024 * 1024 newCapacity = Math.max( newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2 : 1.125)) >>> 0 ) if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256) var oldContents = node.contents node.contents = new Uint8Array(newCapacity) if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0) }, resizeFileStorage: function(node, newSize) { if (node.usedBytes == newSize) return if (newSize == 0) { node.contents = null node.usedBytes = 0 } else { var oldContents = node.contents node.contents = new Uint8Array(newSize) if (oldContents) { node.contents.set( oldContents.subarray(0, Math.min(newSize, node.usedBytes)) ) } node.usedBytes = newSize } }, node_ops: { getattr: function(node) { var attr = {} attr.dev = FS.isChrdev(node.mode) ? node.id : 1 attr.ino = node.id attr.mode = node.mode attr.nlink = 1 attr.uid = 0 attr.gid = 0 attr.rdev = node.rdev if (FS.isDir(node.mode)) { attr.size = 4096 } else if (FS.isFile(node.mode)) { attr.size = node.usedBytes } else if (FS.isLink(node.mode)) { attr.size = node.link.length } else { attr.size = 0 } attr.atime = new Date(node.timestamp) attr.mtime = new Date(node.timestamp) attr.ctime = new Date(node.timestamp) attr.blksize = 4096 attr.blocks = Math.ceil(attr.size / attr.blksize) return attr }, setattr: function(node, attr) { if (attr.mode !== undefined) { node.mode = attr.mode } if (attr.timestamp !== undefined) { node.timestamp = attr.timestamp } if (attr.size !== undefined) { MEMFS.resizeFileStorage(node, attr.size) } }, lookup: function(parent, name) { throw FS.genericErrors[44] }, mknod: function(parent, name, mode, dev) { return MEMFS.createNode(parent, name, mode, dev) }, rename: function(old_node, new_dir, new_name) { if (FS.isDir(old_node.mode)) { var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (new_node) { for (var i in new_node.contents) { throw new FS.ErrnoError(55) } } } delete old_node.parent.contents[old_node.name] old_node.parent.timestamp = Date.now() old_node.name = new_name new_dir.contents[new_name] = old_node new_dir.timestamp = old_node.parent.timestamp old_node.parent = new_dir }, unlink: function(parent, name) { delete parent.contents[name] parent.timestamp = Date.now() }, rmdir: function(parent, name) { var node = FS.lookupNode(parent, name) for (var i in node.contents) { throw new FS.ErrnoError(55) } delete parent.contents[name] parent.timestamp = Date.now() }, readdir: function(node) { var entries = ['.', '..'] for (var key in node.contents) { if (!node.contents.hasOwnProperty(key)) { continue } entries.push(key) } return entries }, symlink: function(parent, newname, oldpath) { var node = MEMFS.createNode(parent, newname, 511 | 40960, 0) node.link = oldpath return node }, readlink: function(node) { if (!FS.isLink(node.mode)) { throw new FS.ErrnoError(28) } return node.link }, }, stream_ops: { read: function(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= stream.node.usedBytes) return 0 var size = Math.min(stream.node.usedBytes - position, length) if (size > 8 && contents.subarray) { buffer.set(contents.subarray(position, position + size), offset) } else { for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i] } return size }, write: function(stream, buffer, offset, length, position, canOwn) { if (buffer.buffer === HEAP8.buffer) { canOwn = false } if (!length) return 0 var node = stream.node node.timestamp = Date.now() if (buffer.subarray && (!node.contents || node.contents.subarray)) { if (canOwn) { node.contents = buffer.subarray(offset, offset + length) node.usedBytes = length return length } else if (node.usedBytes === 0 && position === 0) { node.contents = buffer.slice(offset, offset + length) node.usedBytes = length return length } else if (position + length <= node.usedBytes) { node.contents.set( buffer.subarray(offset, offset + length), position ) return length } } MEMFS.expandFileStorage(node, position + length) if (node.contents.subarray && buffer.subarray) { node.contents.set( buffer.subarray(offset, offset + length), position ) } else { for (var i = 0; i < length; i++) { node.contents[position + i] = buffer[offset + i] } } node.usedBytes = Math.max(node.usedBytes, position + length) return length }, llseek: function(stream, offset, whence) { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { position += stream.node.usedBytes } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, allocate: function(stream, offset, length) { MEMFS.expandFileStorage(stream.node, offset + length) stream.node.usedBytes = Math.max( stream.node.usedBytes, offset + length ) }, mmap: function(stream, length, position, prot, flags) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr var allocated var contents = stream.node.contents if (!(flags & 2) && contents.buffer === buffer) { allocated = false ptr = contents.byteOffset } else { if (position > 0 || position + length < contents.length) { if (contents.subarray) { contents = contents.subarray(position, position + length) } else { contents = Array.prototype.slice.call( contents, position, position + length ) } } allocated = true ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } HEAP8.set(contents, ptr) } return { ptr: ptr, allocated: allocated } }, msync: function(stream, buffer, offset, length, mmapFlags) { MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } function asyncLoad(url, onload, onerror, noRunDep) { var dep = !noRunDep ? getUniqueRunDependency('al ' + url) : '' readAsync( url, arrayBuffer => { assert( arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).' ) onload(new Uint8Array(arrayBuffer)) if (dep) removeRunDependency(dep) }, event => { if (onerror) { onerror() } else { throw 'Loading data file "' + url + '" failed.' } } ) if (dep) addRunDependency(dep) } var ERRNO_CODES = {} var NODEFS = { isWindows: false, staticInit: () => { NODEFS.isWindows = !!process.platform.match(/^win/) var flags = process['binding']('constants') if (flags['fs']) { flags = flags['fs'] } NODEFS.flagsForNodeMap = { 1024: flags['O_APPEND'], 64: flags['O_CREAT'], 128: flags['O_EXCL'], 256: flags['O_NOCTTY'], 0: flags['O_RDONLY'], 2: flags['O_RDWR'], 4096: flags['O_SYNC'], 512: flags['O_TRUNC'], 1: flags['O_WRONLY'], 131072: flags['O_NOFOLLOW'], } }, convertNodeCode: e => { var code = e.code return ERRNO_CODES[code] }, mount: mount => { return NODEFS.createNode(null, '/', NODEFS.getMode(mount.opts.root), 0) }, createNode: (parent, name, mode, dev) => { if (!FS.isDir(mode) && !FS.isFile(mode) && !FS.isLink(mode)) { throw new FS.ErrnoError(28) } var node = FS.createNode(parent, name, mode) node.node_ops = NODEFS.node_ops node.stream_ops = NODEFS.stream_ops return node }, getMode: path => { var stat try { stat = fs.lstatSync(path) if (NODEFS.isWindows) { stat.mode = stat.mode | ((stat.mode & 292) >> 2) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return stat.mode }, realPath: node => { var parts = [] while (node.parent !== node) { parts.push(node.name) node = node.parent } parts.push(node.mount.opts.root) parts.reverse() return PATH.join.apply(null, parts) }, flagsForNode: flags => { flags &= ~2097152 flags &= ~2048 flags &= ~32768 flags &= ~524288 flags &= ~65536 var newFlags = 0 for (var k in NODEFS.flagsForNodeMap) { if (flags & k) { newFlags |= NODEFS.flagsForNodeMap[k] flags ^= k } } if (flags) { throw new FS.ErrnoError(28) } return newFlags }, node_ops: { getattr: node => { var path = NODEFS.realPath(node) var stat try { stat = fs.lstatSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } if (NODEFS.isWindows && !stat.blksize) { stat.blksize = 4096 } if (NODEFS.isWindows && !stat.blocks) { stat.blocks = ((stat.size + stat.blksize - 1) / stat.blksize) | 0 } return { dev: stat.dev, ino: stat.ino, mode: stat.mode, nlink: stat.nlink, uid: stat.uid, gid: stat.gid, rdev: stat.rdev, size: stat.size, atime: stat.atime, mtime: stat.mtime, ctime: stat.ctime, blksize: stat.blksize, blocks: stat.blocks, } }, setattr: (node, attr) => { var path = NODEFS.realPath(node) try { if (attr.mode !== undefined) { fs.chmodSync(path, attr.mode) node.mode = attr.mode } if (attr.timestamp !== undefined) { var date = new Date(attr.timestamp) fs.utimesSync(path, date, date) } if (attr.size !== undefined) { fs.truncateSync(path, attr.size) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, lookup: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) var mode = NODEFS.getMode(path) return NODEFS.createNode(parent, name, mode) }, mknod: (parent, name, mode, dev) => { var node = NODEFS.createNode(parent, name, mode, dev) var path = NODEFS.realPath(node) try { if (FS.isDir(node.mode)) { fs.mkdirSync(path, node.mode) } else { fs.writeFileSync(path, '', { mode: node.mode }) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } return node }, rename: (oldNode, newDir, newName) => { var oldPath = NODEFS.realPath(oldNode) var newPath = PATH.join2(NODEFS.realPath(newDir), newName) try { fs.renameSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } oldNode.name = newName }, unlink: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.unlinkSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, rmdir: (parent, name) => { var path = PATH.join2(NODEFS.realPath(parent), name) try { fs.rmdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readdir: node => { var path = NODEFS.realPath(node) try { return fs.readdirSync(path) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, symlink: (parent, newName, oldPath) => { var newPath = PATH.join2(NODEFS.realPath(parent), newName) try { fs.symlinkSync(oldPath, newPath) } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, readlink: node => { var path = NODEFS.realPath(node) try { path = fs.readlinkSync(path) path = nodePath.relative( nodePath.resolve(node.mount.opts.root), path ) return path } catch (e) { if (!e.code) throw e if (e.code === 'UNKNOWN') throw new FS.ErrnoError(28) throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, }, stream_ops: { open: stream => { var path = NODEFS.realPath(stream.node) try { if (FS.isFile(stream.node.mode)) { stream.nfd = fs.openSync(path, NODEFS.flagsForNode(stream.flags)) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, close: stream => { try { if (FS.isFile(stream.node.mode) && stream.nfd) { fs.closeSync(stream.nfd) } } catch (e) { if (!e.code) throw e throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, read: (stream, buffer, offset, length, position) => { if (length === 0) return 0 try { return fs.readSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, write: (stream, buffer, offset, length, position) => { try { return fs.writeSync( stream.nfd, Buffer.from(buffer.buffer), offset, length, position ) } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } }, llseek: (stream, offset, whence) => { var position = offset if (whence === 1) { position += stream.position } else if (whence === 2) { if (FS.isFile(stream.node.mode)) { try { var stat = fs.fstatSync(stream.nfd) position += stat.size } catch (e) { throw new FS.ErrnoError(NODEFS.convertNodeCode(e)) } } } if (position < 0) { throw new FS.ErrnoError(28) } return position }, mmap: (stream, length, position, prot, flags) => { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } var ptr = mmapAlloc(length) NODEFS.stream_ops.read(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } }, msync: (stream, buffer, offset, length, mmapFlags) => { NODEFS.stream_ops.write(stream, buffer, 0, length, offset, false) return 0 }, }, } var FS = { root: null, mounts: [], devices: {}, streams: [], nextInode: 1, nameTable: null, currentPath: '/', initialized: false, ignorePermissions: true, ErrnoError: null, genericErrors: {}, filesystems: null, syncFSRequests: 0, lookupPath: (path, opts = {}) => { path = PATH_FS.resolve(path) if (!path) return { path: '', node: null } var defaults = { follow_mount: true, recurse_count: 0 } opts = Object.assign(defaults, opts) if (opts.recurse_count > 8) { throw new FS.ErrnoError(32) } var parts = path.split('/').filter(p => !!p) var current = FS.root var current_path = '/' for (var i = 0; i < parts.length; i++) { var islast = i === parts.length - 1 if (islast && opts.parent) { break } current = FS.lookupNode(current, parts[i]) current_path = PATH.join2(current_path, parts[i]) if (FS.isMountpoint(current)) { if (!islast || (islast && opts.follow_mount)) { current = current.mounted.root } } if (!islast || opts.follow) { var count = 0 while (FS.isLink(current.mode)) { var link = FS.readlink(current_path) current_path = PATH_FS.resolve(PATH.dirname(current_path), link) var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count + 1, }) current = lookup.node if (count++ > 40) { throw new FS.ErrnoError(32) } } } } return { path: current_path, node: current } }, getPath: node => { var path while (true) { if (FS.isRoot(node)) { var mount = node.mount.mountpoint if (!path) return mount return mount[mount.length - 1] !== '/' ? mount + '/' + path : mount + path } path = path ? node.name + '/' + path : node.name node = node.parent } }, hashName: (parentid, name) => { var hash = 0 for (var i = 0; i < name.length; i++) { hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0 } return ((parentid + hash) >>> 0) % FS.nameTable.length }, hashAddNode: node => { var hash = FS.hashName(node.parent.id, node.name) node.name_next = FS.nameTable[hash] FS.nameTable[hash] = node }, hashRemoveNode: node => { var hash = FS.hashName(node.parent.id, node.name) if (FS.nameTable[hash] === node) { FS.nameTable[hash] = node.name_next } else { var current = FS.nameTable[hash] while (current) { if (current.name_next === node) { current.name_next = node.name_next break } current = current.name_next } } }, lookupNode: (parent, name) => { var errCode = FS.mayLookup(parent) if (errCode) { throw new FS.ErrnoError(errCode, parent) } var hash = FS.hashName(parent.id, name) for (var node = FS.nameTable[hash]; node; node = node.name_next) { var nodeName = node.name if (node.parent.id === parent.id && nodeName === name) { return node } } return FS.lookup(parent, name) }, createNode: (parent, name, mode, rdev) => { var node = new FS.FSNode(parent, name, mode, rdev) FS.hashAddNode(node) return node }, destroyNode: node => { FS.hashRemoveNode(node) }, isRoot: node => { return node === node.parent }, isMountpoint: node => { return !!node.mounted }, isFile: mode => { return (mode & 61440) === 32768 }, isDir: mode => { return (mode & 61440) === 16384 }, isLink: mode => { return (mode & 61440) === 40960 }, isChrdev: mode => { return (mode & 61440) === 8192 }, isBlkdev: mode => { return (mode & 61440) === 24576 }, isFIFO: mode => { return (mode & 61440) === 4096 }, isSocket: mode => { return (mode & 49152) === 49152 }, flagModes: { r: 0, 'r+': 2, w: 577, 'w+': 578, a: 1089, 'a+': 1090 }, modeStringToFlags: str => { var flags = FS.flagModes[str] if (typeof flags == 'undefined') { throw new Error('Unknown file open mode: ' + str) } return flags }, flagsToPermissionString: flag => { var perms = ['r', 'w', 'rw'][flag & 3] if (flag & 512) { perms += 'w' } return perms }, nodePermissions: (node, perms) => { if (FS.ignorePermissions) { return 0 } if (perms.includes('r') && !(node.mode & 292)) { return 2 } else if (perms.includes('w') && !(node.mode & 146)) { return 2 } else if (perms.includes('x') && !(node.mode & 73)) { return 2 } return 0 }, mayLookup: dir => { var errCode = FS.nodePermissions(dir, 'x') if (errCode) return errCode if (!dir.node_ops.lookup) return 2 return 0 }, mayCreate: (dir, name) => { try { var node = FS.lookupNode(dir, name) return 20 } catch (e) {} return FS.nodePermissions(dir, 'wx') }, mayDelete: (dir, name, isdir) => { var node try { node = FS.lookupNode(dir, name) } catch (e) { return e.errno } var errCode = FS.nodePermissions(dir, 'wx') if (errCode) { return errCode } if (isdir) { if (!FS.isDir(node.mode)) { return 54 } if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) { return 10 } } else { if (FS.isDir(node.mode)) { return 31 } } return 0 }, mayOpen: (node, flags) => { if (!node) { return 44 } if (FS.isLink(node.mode)) { return 32 } else if (FS.isDir(node.mode)) { if (FS.flagsToPermissionString(flags) !== 'r' || flags & 512) { return 31 } } return FS.nodePermissions(node, FS.flagsToPermissionString(flags)) }, MAX_OPEN_FDS: 4096, nextfd: (fd_start = 0, fd_end = FS.MAX_OPEN_FDS) => { for (var fd = fd_start; fd <= fd_end; fd++) { if (!FS.streams[fd]) { return fd } } throw new FS.ErrnoError(33) }, getStream: fd => FS.streams[fd], createStream: (stream, fd_start, fd_end) => { if (!FS.FSStream) { FS.FSStream = function() { this.shared = {} } FS.FSStream.prototype = {} Object.defineProperties(FS.FSStream.prototype, { object: { get: function() { return this.node }, set: function(val) { this.node = val }, }, isRead: { get: function() { return (this.flags & 2097155) !== 1 }, }, isWrite: { get: function() { return (this.flags & 2097155) !== 0 }, }, isAppend: { get: function() { return this.flags & 1024 }, }, flags: { get: function() { return this.shared.flags }, set: function(val) { this.shared.flags = val }, }, position: { get: function() { return this.shared.position }, set: function(val) { this.shared.position = val }, }, }) } stream = Object.assign(new FS.FSStream(), stream) var fd = FS.nextfd(fd_start, fd_end) stream.fd = fd FS.streams[fd] = stream return stream }, closeStream: fd => { FS.streams[fd] = null }, chrdev_stream_ops: { open: stream => { var device = FS.getDevice(stream.node.rdev) stream.stream_ops = device.stream_ops if (stream.stream_ops.open) { stream.stream_ops.open(stream) } }, llseek: () => { throw new FS.ErrnoError(70) }, }, major: dev => dev >> 8, minor: dev => dev & 255, makedev: (ma, mi) => (ma << 8) | mi, registerDevice: (dev, ops) => { FS.devices[dev] = { stream_ops: ops } }, getDevice: dev => FS.devices[dev], getMounts: mount => { var mounts = [] var check = [mount] while (check.length) { var m = check.pop() mounts.push(m) check.push.apply(check, m.mounts) } return mounts }, syncfs: (populate, callback) => { if (typeof populate == 'function') { callback = populate populate = false } FS.syncFSRequests++ if (FS.syncFSRequests > 1) { err( 'warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work' ) } var mounts = FS.getMounts(FS.root.mount) var completed = 0 function doCallback(errCode) { FS.syncFSRequests-- return callback(errCode) } function done(errCode) { if (errCode) { if (!done.errored) { done.errored = true return doCallback(errCode) } return } if (++completed >= mounts.length) { doCallback(null) } } mounts.forEach(mount => { if (!mount.type.syncfs) { return done(null) } mount.type.syncfs(mount, populate, done) }) }, mount: (type, opts, mountpoint) => { var root = mountpoint === '/' var pseudo = !mountpoint var node if (root && FS.root) { throw new FS.ErrnoError(10) } else if (!root && !pseudo) { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) mountpoint = lookup.path node = lookup.node if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } if (!FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } } var mount = { type: type, opts: opts, mountpoint: mountpoint, mounts: [], } var mountRoot = type.mount(mount) mountRoot.mount = mount mount.root = mountRoot if (root) { FS.root = mountRoot } else if (node) { node.mounted = mount if (node.mount) { node.mount.mounts.push(mount) } } return mountRoot }, unmount: mountpoint => { var lookup = FS.lookupPath(mountpoint, { follow_mount: false }) if (!FS.isMountpoint(lookup.node)) { throw new FS.ErrnoError(28) } var node = lookup.node var mount = node.mounted var mounts = FS.getMounts(mount) Object.keys(FS.nameTable).forEach(hash => { var current = FS.nameTable[hash] while (current) { var next = current.name_next if (mounts.includes(current.mount)) { FS.destroyNode(current) } current = next } }) node.mounted = null var idx = node.mount.mounts.indexOf(mount) node.mount.mounts.splice(idx, 1) }, lookup: (parent, name) => { return parent.node_ops.lookup(parent, name) }, mknod: (path, mode, dev) => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) if (!name || name === '.' || name === '..') { throw new FS.ErrnoError(28) } var errCode = FS.mayCreate(parent, name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.mknod) { throw new FS.ErrnoError(63) } return parent.node_ops.mknod(parent, name, mode, dev) }, create: (path, mode) => { mode = mode !== undefined ? mode : 438 mode &= 4095 mode |= 32768 return FS.mknod(path, mode, 0) }, mkdir: (path, mode) => { mode = mode !== undefined ? mode : 511 mode &= 511 | 512 mode |= 16384 return FS.mknod(path, mode, 0) }, mkdirTree: (path, mode) => { var dirs = path.split('/') var d = '' for (var i = 0; i < dirs.length; ++i) { if (!dirs[i]) continue d += '/' + dirs[i] try { FS.mkdir(d, mode) } catch (e) { if (e.errno != 20) throw e } } }, mkdev: (path, mode, dev) => { if (typeof dev == 'undefined') { dev = mode mode = 438 } mode |= 8192 return FS.mknod(path, mode, dev) }, symlink: (oldpath, newpath) => { if (!PATH_FS.resolve(oldpath)) { throw new FS.ErrnoError(44) } var lookup = FS.lookupPath(newpath, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var newname = PATH.basename(newpath) var errCode = FS.mayCreate(parent, newname) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.symlink) { throw new FS.ErrnoError(63) } return parent.node_ops.symlink(parent, newname, oldpath) }, rename: (old_path, new_path) => { var old_dirname = PATH.dirname(old_path) var new_dirname = PATH.dirname(new_path) var old_name = PATH.basename(old_path) var new_name = PATH.basename(new_path) var lookup, old_dir, new_dir lookup = FS.lookupPath(old_path, { parent: true }) old_dir = lookup.node lookup = FS.lookupPath(new_path, { parent: true }) new_dir = lookup.node if (!old_dir || !new_dir) throw new FS.ErrnoError(44) if (old_dir.mount !== new_dir.mount) { throw new FS.ErrnoError(75) } var old_node = FS.lookupNode(old_dir, old_name) var relative = PATH_FS.relative(old_path, new_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(28) } relative = PATH_FS.relative(new_path, old_dirname) if (relative.charAt(0) !== '.') { throw new FS.ErrnoError(55) } var new_node try { new_node = FS.lookupNode(new_dir, new_name) } catch (e) {} if (old_node === new_node) { return } var isdir = FS.isDir(old_node.mode) var errCode = FS.mayDelete(old_dir, old_name, isdir) if (errCode) { throw new FS.ErrnoError(errCode) } errCode = new_node ? FS.mayDelete(new_dir, new_name, isdir) : FS.mayCreate(new_dir, new_name) if (errCode) { throw new FS.ErrnoError(errCode) } if (!old_dir.node_ops.rename) { throw new FS.ErrnoError(63) } if ( FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node)) ) { throw new FS.ErrnoError(10) } if (new_dir !== old_dir) { errCode = FS.nodePermissions(old_dir, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } } FS.hashRemoveNode(old_node) try { old_dir.node_ops.rename(old_node, new_dir, new_name) } catch (e) { throw e } finally { FS.hashAddNode(old_node) } }, rmdir: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, true) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.rmdir) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.rmdir(parent, name) FS.destroyNode(node) }, readdir: path => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node if (!node.node_ops.readdir) { throw new FS.ErrnoError(54) } return node.node_ops.readdir(node) }, unlink: path => { var lookup = FS.lookupPath(path, { parent: true }) var parent = lookup.node if (!parent) { throw new FS.ErrnoError(44) } var name = PATH.basename(path) var node = FS.lookupNode(parent, name) var errCode = FS.mayDelete(parent, name, false) if (errCode) { throw new FS.ErrnoError(errCode) } if (!parent.node_ops.unlink) { throw new FS.ErrnoError(63) } if (FS.isMountpoint(node)) { throw new FS.ErrnoError(10) } parent.node_ops.unlink(parent, name) FS.destroyNode(node) }, readlink: path => { var lookup = FS.lookupPath(path) var link = lookup.node if (!link) { throw new FS.ErrnoError(44) } if (!link.node_ops.readlink) { throw new FS.ErrnoError(28) } return PATH_FS.resolve( FS.getPath(link.parent), link.node_ops.readlink(link) ) }, stat: (path, dontFollow) => { var lookup = FS.lookupPath(path, { follow: !dontFollow }) var node = lookup.node if (!node) { throw new FS.ErrnoError(44) } if (!node.node_ops.getattr) { throw new FS.ErrnoError(63) } return node.node_ops.getattr(node) }, lstat: path => { return FS.stat(path, true) }, chmod: (path, mode, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { mode: (mode & 4095) | (node.mode & ~4095), timestamp: Date.now(), }) }, lchmod: (path, mode) => { FS.chmod(path, mode, true) }, fchmod: (fd, mode) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chmod(stream.node, mode) }, chown: (path, uid, gid, dontFollow) => { var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: !dontFollow }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } node.node_ops.setattr(node, { timestamp: Date.now() }) }, lchown: (path, uid, gid) => { FS.chown(path, uid, gid, true) }, fchown: (fd, uid, gid) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } FS.chown(stream.node, uid, gid) }, truncate: (path, len) => { if (len < 0) { throw new FS.ErrnoError(28) } var node if (typeof path == 'string') { var lookup = FS.lookupPath(path, { follow: true }) node = lookup.node } else { node = path } if (!node.node_ops.setattr) { throw new FS.ErrnoError(63) } if (FS.isDir(node.mode)) { throw new FS.ErrnoError(31) } if (!FS.isFile(node.mode)) { throw new FS.ErrnoError(28) } var errCode = FS.nodePermissions(node, 'w') if (errCode) { throw new FS.ErrnoError(errCode) } node.node_ops.setattr(node, { size: len, timestamp: Date.now() }) }, ftruncate: (fd, len) => { var stream = FS.getStream(fd) if (!stream) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(28) } FS.truncate(stream.node, len) }, utime: (path, atime, mtime) => { var lookup = FS.lookupPath(path, { follow: true }) var node = lookup.node node.node_ops.setattr(node, { timestamp: Math.max(atime, mtime) }) }, open: (path, flags, mode) => { if (path === '') { throw new FS.ErrnoError(44) } flags = typeof flags == 'string' ? FS.modeStringToFlags(flags) : flags mode = typeof mode == 'undefined' ? 438 : mode if (flags & 64) { mode = (mode & 4095) | 32768 } else { mode = 0 } var node if (typeof path == 'object') { node = path } else { path = PATH.normalize(path) try { var lookup = FS.lookupPath(path, { follow: !(flags & 131072) }) node = lookup.node } catch (e) {} } var created = false if (flags & 64) { if (node) { if (flags & 128) { throw new FS.ErrnoError(20) } } else { node = FS.mknod(path, mode, 0) created = true } } if (!node) { throw new FS.ErrnoError(44) } if (FS.isChrdev(node.mode)) { flags &= ~512 } if (flags & 65536 && !FS.isDir(node.mode)) { throw new FS.ErrnoError(54) } if (!created) { var errCode = FS.mayOpen(node, flags) if (errCode) { throw new FS.ErrnoError(errCode) } } if (flags & 512 && !created) { FS.truncate(node, 0) } flags &= ~(128 | 512 | 131072) var stream = FS.createStream({ node: node, path: FS.getPath(node), flags: flags, seekable: true, position: 0, stream_ops: node.stream_ops, ungotten: [], error: false, }) if (stream.stream_ops.open) { stream.stream_ops.open(stream) } if (Module['logReadFiles'] && !(flags & 1)) { if (!FS.readFiles) FS.readFiles = {} if (!(path in FS.readFiles)) { FS.readFiles[path] = 1 } } return stream }, close: stream => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (stream.getdents) stream.getdents = null try { if (stream.stream_ops.close) { stream.stream_ops.close(stream) } } catch (e) { throw e } finally { FS.closeStream(stream.fd) } stream.fd = null }, isClosed: stream => { return stream.fd === null }, llseek: (stream, offset, whence) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (!stream.seekable || !stream.stream_ops.llseek) { throw new FS.ErrnoError(70) } if (whence != 0 && whence != 1 && whence != 2) { throw new FS.ErrnoError(28) } stream.position = stream.stream_ops.llseek(stream, offset, whence) stream.ungotten = [] return stream.position }, read: (stream, buffer, offset, length, position) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.read) { throw new FS.ErrnoError(28) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesRead = stream.stream_ops.read( stream, buffer, offset, length, position ) if (!seeking) stream.position += bytesRead return bytesRead }, write: (stream, buffer, offset, length, position, canOwn) => { if (length < 0 || position < 0) { throw new FS.ErrnoError(28) } if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(31) } if (!stream.stream_ops.write) { throw new FS.ErrnoError(28) } if (stream.seekable && stream.flags & 1024) { FS.llseek(stream, 0, 2) } var seeking = typeof position != 'undefined' if (!seeking) { position = stream.position } else if (!stream.seekable) { throw new FS.ErrnoError(70) } var bytesWritten = stream.stream_ops.write( stream, buffer, offset, length, position, canOwn ) if (!seeking) stream.position += bytesWritten return bytesWritten }, allocate: (stream, offset, length) => { if (FS.isClosed(stream)) { throw new FS.ErrnoError(8) } if (offset < 0 || length <= 0) { throw new FS.ErrnoError(28) } if ((stream.flags & 2097155) === 0) { throw new FS.ErrnoError(8) } if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) { throw new FS.ErrnoError(43) } if (!stream.stream_ops.allocate) { throw new FS.ErrnoError(138) } stream.stream_ops.allocate(stream, offset, length) }, mmap: (stream, length, position, prot, flags) => { if ( (prot & 2) !== 0 && (flags & 2) === 0 && (stream.flags & 2097155) !== 2 ) { throw new FS.ErrnoError(2) } if ((stream.flags & 2097155) === 1) { throw new FS.ErrnoError(2) } if (!stream.stream_ops.mmap) { throw new FS.ErrnoError(43) } return stream.stream_ops.mmap(stream, length, position, prot, flags) }, msync: (stream, buffer, offset, length, mmapFlags) => { if (!stream.stream_ops.msync) { return 0 } return stream.stream_ops.msync( stream, buffer, offset, length, mmapFlags ) }, munmap: stream => 0, ioctl: (stream, cmd, arg) => { if (!stream.stream_ops.ioctl) { throw new FS.ErrnoError(59) } return stream.stream_ops.ioctl(stream, cmd, arg) }, readFile: (path, opts = {}) => { opts.flags = opts.flags || 0 opts.encoding = opts.encoding || 'binary' if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') { throw new Error('Invalid encoding type "' + opts.encoding + '"') } var ret var stream = FS.open(path, opts.flags) var stat = FS.stat(path) var length = stat.size var buf = new Uint8Array(length) FS.read(stream, buf, 0, length, 0) if (opts.encoding === 'utf8') { ret = UTF8ArrayToString(buf, 0) } else if (opts.encoding === 'binary') { ret = buf } FS.close(stream) return ret }, writeFile: (path, data, opts = {}) => { opts.flags = opts.flags || 577 var stream = FS.open(path, opts.flags, opts.mode) if (typeof data == 'string') { var buf = new Uint8Array(lengthBytesUTF8(data) + 1) var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length) FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn) } else if (ArrayBuffer.isView(data)) { FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn) } else { throw new Error('Unsupported data type') } FS.close(stream) }, cwd: () => FS.currentPath, chdir: path => { var lookup = FS.lookupPath(path, { follow: true }) if (lookup.node === null) { throw new FS.ErrnoError(44) } if (!FS.isDir(lookup.node.mode)) { throw new FS.ErrnoError(54) } var errCode = FS.nodePermissions(lookup.node, 'x') if (errCode) { throw new FS.ErrnoError(errCode) } FS.currentPath = lookup.path }, createDefaultDirectories: () => { FS.mkdir('/tmp') FS.mkdir('/home') FS.mkdir('/home/web_user') }, createDefaultDevices: () => { FS.mkdir('/dev') FS.registerDevice(FS.makedev(1, 3), { read: () => 0, write: (stream, buffer, offset, length, pos) => length, }) FS.mkdev('/dev/null', FS.makedev(1, 3)) TTY.register(FS.makedev(5, 0), TTY.default_tty_ops) TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops) FS.mkdev('/dev/tty', FS.makedev(5, 0)) FS.mkdev('/dev/tty1', FS.makedev(6, 0)) var random_device = getRandomDevice() FS.createDevice('/dev', 'random', random_device) FS.createDevice('/dev', 'urandom', random_device) FS.mkdir('/dev/shm') FS.mkdir('/dev/shm/tmp') }, createSpecialDirectories: () => { FS.mkdir('/proc') var proc_self = FS.mkdir('/proc/self') FS.mkdir('/proc/self/fd') FS.mount( { mount: () => { var node = FS.createNode(proc_self, 'fd', 16384 | 511, 73) node.node_ops = { lookup: (parent, name) => { var fd = +name var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) var ret = { parent: null, mount: { mountpoint: 'fake' }, node_ops: { readlink: () => stream.path }, } ret.parent = ret return ret }, } return node }, }, {}, '/proc/self/fd' ) }, createStandardStreams: () => { if (Module['stdin']) { FS.createDevice('/dev', 'stdin', Module['stdin']) } else { FS.symlink('/dev/tty', '/dev/stdin') } if (Module['stdout']) { FS.createDevice('/dev', 'stdout', null, Module['stdout']) } else { FS.symlink('/dev/tty', '/dev/stdout') } if (Module['stderr']) { FS.createDevice('/dev', 'stderr', null, Module['stderr']) } else { FS.symlink('/dev/tty1', '/dev/stderr') } var stdin = FS.open('/dev/stdin', 0) var stdout = FS.open('/dev/stdout', 1) var stderr = FS.open('/dev/stderr', 1) }, ensureErrnoError: () => { if (FS.ErrnoError) return FS.ErrnoError = function ErrnoError(errno, node) { this.node = node this.setErrno = function(errno) { this.errno = errno } this.setErrno(errno) this.message = 'FS error' } FS.ErrnoError.prototype = new Error() FS.ErrnoError.prototype.constructor = FS.ErrnoError ;[44].forEach(code => { FS.genericErrors[code] = new FS.ErrnoError(code) FS.genericErrors[code].stack = '' }) }, staticInit: () => { FS.ensureErrnoError() FS.nameTable = new Array(4096) FS.mount(MEMFS, {}, '/') FS.createDefaultDirectories() FS.createDefaultDevices() FS.createSpecialDirectories() FS.filesystems = { MEMFS: MEMFS, NODEFS: NODEFS } }, init: (input, output, error) => { FS.init.initialized = true FS.ensureErrnoError() Module['stdin'] = input || Module['stdin'] Module['stdout'] = output || Module['stdout'] Module['stderr'] = error || Module['stderr'] FS.createStandardStreams() }, quit: () => { FS.init.initialized = false for (var i = 0; i < FS.streams.length; i++) { var stream = FS.streams[i] if (!stream) { continue } FS.close(stream) } }, getMode: (canRead, canWrite) => { var mode = 0 if (canRead) mode |= 292 | 73 if (canWrite) mode |= 146 return mode }, findObject: (path, dontResolveLastLink) => { var ret = FS.analyzePath(path, dontResolveLastLink) if (!ret.exists) { return null } return ret.object }, analyzePath: (path, dontResolveLastLink) => { try { var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) path = lookup.path } catch (e) {} var ret = { isRoot: false, exists: false, error: 0, name: null, path: null, object: null, parentExists: false, parentPath: null, parentObject: null, } try { var lookup = FS.lookupPath(path, { parent: true }) ret.parentExists = true ret.parentPath = lookup.path ret.parentObject = lookup.node ret.name = PATH.basename(path) lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }) ret.exists = true ret.path = lookup.path ret.object = lookup.node ret.name = lookup.node.name ret.isRoot = lookup.path === '/' } catch (e) { ret.error = e.errno } return ret }, createPath: (parent, path, canRead, canWrite) => { parent = typeof parent == 'string' ? parent : FS.getPath(parent) var parts = path.split('/').reverse() while (parts.length) { var part = parts.pop() if (!part) continue var current = PATH.join2(parent, part) try { FS.mkdir(current) } catch (e) {} parent = current } return current }, createFile: (parent, name, properties, canRead, canWrite) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(canRead, canWrite) return FS.create(path, mode) }, createDataFile: (parent, name, data, canRead, canWrite, canOwn) => { var path = name if (parent) { parent = typeof parent == 'string' ? parent : FS.getPath(parent) path = name ? PATH.join2(parent, name) : parent } var mode = FS.getMode(canRead, canWrite) var node = FS.create(path, mode) if (data) { if (typeof data == 'string') { var arr = new Array(data.length) for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i) data = arr } FS.chmod(node, mode | 146) var stream = FS.open(node, 577) FS.write(stream, data, 0, data.length, 0, canOwn) FS.close(stream) FS.chmod(node, mode) } return node }, createDevice: (parent, name, input, output) => { var path = PATH.join2( typeof parent == 'string' ? parent : FS.getPath(parent), name ) var mode = FS.getMode(!!input, !!output) if (!FS.createDevice.major) FS.createDevice.major = 64 var dev = FS.makedev(FS.createDevice.major++, 0) FS.registerDevice(dev, { open: stream => { stream.seekable = false }, close: stream => { if (output && output.buffer && output.buffer.length) { output(10) } }, read: (stream, buffer, offset, length, pos) => { var bytesRead = 0 for (var i = 0; i < length; i++) { var result try { result = input() } catch (e) { throw new FS.ErrnoError(29) } if (result === undefined && bytesRead === 0) { throw new FS.ErrnoError(6) } if (result === null || result === undefined) break bytesRead++ buffer[offset + i] = result } if (bytesRead) { stream.node.timestamp = Date.now() } return bytesRead }, write: (stream, buffer, offset, length, pos) => { for (var i = 0; i < length; i++) { try { output(buffer[offset + i]) } catch (e) { throw new FS.ErrnoError(29) } } if (length) { stream.node.timestamp = Date.now() } return i }, }) return FS.mkdev(path, mode, dev) }, forceLoadFile: obj => { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true if (typeof XMLHttpRequest != 'undefined') { throw new Error( 'Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.' ) } else if (read_) { try { obj.contents = intArrayFromString(read_(obj.url), true) obj.usedBytes = obj.contents.length } catch (e) { throw new FS.ErrnoError(29) } } else { throw new Error('Cannot load without read() or XMLHttpRequest.') } }, createLazyFile: (parent, name, url, canRead, canWrite) => { function LazyUint8Array() { this.lengthKnown = false this.chunks = [] } LazyUint8Array.prototype.get = function LazyUint8Array_get(idx) { if (idx > this.length - 1 || idx < 0) { return undefined } var chunkOffset = idx % this.chunkSize var chunkNum = (idx / this.chunkSize) | 0 return this.getter(chunkNum)[chunkOffset] } LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter( getter ) { this.getter = getter } LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() { var xhr = new XMLHttpRequest() xhr.open('HEAD', url, false) xhr.send(null) if (!((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304)) throw new Error("Couldn't load " + url + '. Status: ' + xhr.status) var datalength = Number(xhr.getResponseHeader('Content-length')) var header var hasByteServing = (header = xhr.getResponseHeader('Accept-Ranges')) && header === 'bytes' var usesGzip = (header = xhr.getResponseHeader('Content-Encoding')) && header === 'gzip' var chunkSize = 1024 * 1024 if (!hasByteServing) chunkSize = datalength var doXHR = (from, to) => { if (from > to) throw new Error( 'invalid range (' + from + ', ' + to + ') or no bytes requested!' ) if (to > datalength - 1) throw new Error( 'only ' + datalength + ' bytes available! programmer error!' ) var xhr = new XMLHttpRequest() xhr.open('GET', url, false) if (datalength !== chunkSize) xhr.setRequestHeader('Range', 'bytes=' + from + '-' + to) xhr.responseType = 'arraybuffer' if (xhr.overrideMimeType) { xhr.overrideMimeType('text/plain; charset=x-user-defined') } xhr.send(null) if ( !((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ) throw new Error( "Couldn't load " + url + '. Status: ' + xhr.status ) if (xhr.response !== undefined) { return new Uint8Array(xhr.response || []) } return intArrayFromString(xhr.responseText || '', true) } var lazyArray = this lazyArray.setDataGetter(chunkNum => { var start = chunkNum * chunkSize var end = (chunkNum + 1) * chunkSize - 1 end = Math.min(end, datalength - 1) if (typeof lazyArray.chunks[chunkNum] == 'undefined') { lazyArray.chunks[chunkNum] = doXHR(start, end) } if (typeof lazyArray.chunks[chunkNum] == 'undefined') throw new Error('doXHR failed!') return lazyArray.chunks[chunkNum] }) if (usesGzip || !datalength) { chunkSize = datalength = 1 datalength = this.getter(0).length chunkSize = datalength out( 'LazyFiles on gzip forces download of the whole file when length is accessed' ) } this._length = datalength this._chunkSize = chunkSize this.lengthKnown = true } if (typeof XMLHttpRequest != 'undefined') { if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc' var lazyArray = new LazyUint8Array() Object.defineProperties(lazyArray, { length: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._length }, }, chunkSize: { get: function() { if (!this.lengthKnown) { this.cacheLength() } return this._chunkSize }, }, }) var properties = { isDevice: false, contents: lazyArray } } else { var properties = { isDevice: false, url: url } } var node = FS.createFile(parent, name, properties, canRead, canWrite) if (properties.contents) { node.contents = properties.contents } else if (properties.url) { node.contents = null node.url = properties.url } Object.defineProperties(node, { usedBytes: { get: function() { return this.contents.length }, }, }) var stream_ops = {} var keys = Object.keys(node.stream_ops) keys.forEach(key => { var fn = node.stream_ops[key] stream_ops[key] = function forceLoadLazyFile() { FS.forceLoadFile(node) return fn.apply(null, arguments) } }) function writeChunks(stream, buffer, offset, length, position) { var contents = stream.node.contents if (position >= contents.length) return 0 var size = Math.min(contents.length - position, length) if (contents.slice) { for (var i = 0; i < size; i++) { buffer[offset + i] = contents[position + i] } } else { for (var i = 0; i < size; i++) { buffer[offset + i] = contents.get(position + i) } } return size } stream_ops.read = (stream, buffer, offset, length, position) => { FS.forceLoadFile(node) return writeChunks(stream, buffer, offset, length, position) } stream_ops.mmap = (stream, length, position, prot, flags) => { FS.forceLoadFile(node) var ptr = mmapAlloc(length) if (!ptr) { throw new FS.ErrnoError(48) } writeChunks(stream, HEAP8, ptr, length, position) return { ptr: ptr, allocated: true } } node.stream_ops = stream_ops return node }, createPreloadedFile: ( parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish ) => { var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent var dep = getUniqueRunDependency('cp ' + fullname) function processData(byteArray) { function finish(byteArray) { if (preFinish) preFinish() if (!dontCreateFile) { FS.createDataFile( parent, name, byteArray, canRead, canWrite, canOwn ) } if (onload) onload() removeRunDependency(dep) } if ( Browser.handledByPreloadPlugin(byteArray, fullname, finish, () => { if (onerror) onerror() removeRunDependency(dep) }) ) { return } finish(byteArray) } addRunDependency(dep) if (typeof url == 'string') { asyncLoad(url, byteArray => processData(byteArray), onerror) } else { processData(url) } }, indexedDB: () => { return ( window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB ) }, DB_NAME: () => { return 'EM_FS_' + window.location.pathname }, DB_VERSION: 20, DB_STORE_NAME: 'FILE_DATA', saveFilesToDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = () => { out('creating db') var db = openRequest.result db.createObjectStore(FS.DB_STORE_NAME) } openRequest.onsuccess = () => { var db = openRequest.result var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite') var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var putRequest = files.put( FS.analyzePath(path).object.contents, path ) putRequest.onsuccess = () => { ok++ if (ok + fail == total) finish() } putRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, loadFilesFromDB: (paths, onload, onerror) => { onload = onload || (() => {}) onerror = onerror || (() => {}) var indexedDB = FS.indexedDB() try { var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION) } catch (e) { return onerror(e) } openRequest.onupgradeneeded = onerror openRequest.onsuccess = () => { var db = openRequest.result try { var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly') } catch (e) { onerror(e) return } var files = transaction.objectStore(FS.DB_STORE_NAME) var ok = 0, fail = 0, total = paths.length function finish() { if (fail == 0) onload() else onerror() } paths.forEach(path => { var getRequest = files.get(path) getRequest.onsuccess = () => { if (FS.analyzePath(path).exists) { FS.unlink(path) } FS.createDataFile( PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true ) ok++ if (ok + fail == total) finish() } getRequest.onerror = () => { fail++ if (ok + fail == total) finish() } }) transaction.onerror = onerror } openRequest.onerror = onerror }, } var SYSCALLS = { DEFAULT_POLLMASK: 5, calculateAt: function(dirfd, path, allowEmpty) { if (PATH.isAbs(path)) { return path } var dir if (dirfd === -100) { dir = FS.cwd() } else { var dirstream = SYSCALLS.getStreamFromFD(dirfd) dir = dirstream.path } if (path.length == 0) { if (!allowEmpty) { throw new FS.ErrnoError(44) } return dir } return PATH.join2(dir, path) }, doStat: function(func, path, buf) { try { var stat = func(path) } catch (e) { if ( e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node)) ) { return -54 } throw e } HEAP32[buf >> 2] = stat.dev HEAP32[(buf + 8) >> 2] = stat.ino HEAP32[(buf + 12) >> 2] = stat.mode HEAPU32[(buf + 16) >> 2] = stat.nlink HEAP32[(buf + 20) >> 2] = stat.uid HEAP32[(buf + 24) >> 2] = stat.gid HEAP32[(buf + 28) >> 2] = stat.rdev ;(tempI64 = [ stat.size >>> 0, ((tempDouble = stat.size), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 40) >> 2] = tempI64[0]), (HEAP32[(buf + 44) >> 2] = tempI64[1]) HEAP32[(buf + 48) >> 2] = 4096 HEAP32[(buf + 52) >> 2] = stat.blocks var atime = stat.atime.getTime() var mtime = stat.mtime.getTime() var ctime = stat.ctime.getTime() ;(tempI64 = [ Math.floor(atime / 1e3) >>> 0, ((tempDouble = Math.floor(atime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 56) >> 2] = tempI64[0]), (HEAP32[(buf + 60) >> 2] = tempI64[1]) HEAPU32[(buf + 64) >> 2] = (atime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(mtime / 1e3) >>> 0, ((tempDouble = Math.floor(mtime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 72) >> 2] = tempI64[0]), (HEAP32[(buf + 76) >> 2] = tempI64[1]) HEAPU32[(buf + 80) >> 2] = (mtime % 1e3) * 1e3 ;(tempI64 = [ Math.floor(ctime / 1e3) >>> 0, ((tempDouble = Math.floor(ctime / 1e3)), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 88) >> 2] = tempI64[0]), (HEAP32[(buf + 92) >> 2] = tempI64[1]) HEAPU32[(buf + 96) >> 2] = (ctime % 1e3) * 1e3 ;(tempI64 = [ stat.ino >>> 0, ((tempDouble = stat.ino), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[(buf + 104) >> 2] = tempI64[0]), (HEAP32[(buf + 108) >> 2] = tempI64[1]) return 0 }, doMsync: function(addr, stream, len, flags, offset) { if (!FS.isFile(stream.node.mode)) { throw new FS.ErrnoError(43) } if (flags & 2) { return 0 } var buffer = HEAPU8.slice(addr, addr + len) FS.msync(stream, buffer, offset, len, flags) }, varargs: undefined, get: function() { SYSCALLS.varargs += 4 var ret = HEAP32[(SYSCALLS.varargs - 4) >> 2] return ret }, getStr: function(ptr) { var ret = UTF8ToString(ptr) return ret }, getStreamFromFD: function(fd) { var stream = FS.getStream(fd) if (!stream) throw new FS.ErrnoError(8) return stream }, } function ___syscall_fcntl64(fd, cmd, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (cmd) { case 0: { var arg = SYSCALLS.get() if (arg < 0) { return -28 } var newStream newStream = FS.createStream(stream, arg) return newStream.fd } case 1: case 2: return 0 case 3: return stream.flags case 4: { var arg = SYSCALLS.get() stream.flags |= arg return 0 } case 5: { var arg = SYSCALLS.get() var offset = 0 HEAP16[(arg + offset) >> 1] = 2 return 0 } case 6: case 7: return 0 case 16: case 8: return -28 case 9: setErrNo(28) return -1 default: { return -28 } } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_getcwd(buf, size) { try { if (size === 0) return -28 var cwd = FS.cwd() var cwdLengthInBytes = lengthBytesUTF8(cwd) + 1 if (size < cwdLengthInBytes) return -68 stringToUTF8(cwd, buf, size) return cwdLengthInBytes } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_ioctl(fd, op, varargs) { SYSCALLS.varargs = varargs try { var stream = SYSCALLS.getStreamFromFD(fd) switch (op) { case 21509: case 21505: { if (!stream.tty) return -59 return 0 } case 21510: case 21511: case 21512: case 21506: case 21507: case 21508: { if (!stream.tty) return -59 return 0 } case 21519: { if (!stream.tty) return -59 var argp = SYSCALLS.get() HEAP32[argp >> 2] = 0 return 0 } case 21520: { if (!stream.tty) return -59 return -28 } case 21531: { var argp = SYSCALLS.get() return FS.ioctl(stream, op, argp) } case 21523: { if (!stream.tty) return -59 return 0 } case 21524: { if (!stream.tty) return -59 return 0 } default: return -28 } } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_openat(dirfd, path, flags, varargs) { SYSCALLS.varargs = varargs try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) var mode = varargs ? SYSCALLS.get() : 0 return FS.open(path, flags, mode).fd } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_readlinkat(dirfd, path, buf, bufsize) { try { path = SYSCALLS.getStr(path) path = SYSCALLS.calculateAt(dirfd, path) if (bufsize <= 0) return -28 var ret = FS.readlink(path) var len = Math.min(bufsize, lengthBytesUTF8(ret)) var endChar = HEAP8[buf + len] stringToUTF8(ret, buf, bufsize + 1) HEAP8[buf + len] = endChar return len } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function ___syscall_stat64(path, buf) { try { path = SYSCALLS.getStr(path) return SYSCALLS.doStat(FS.stat, path, buf) } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return -e.errno } } function _abort() { abort('') } function _emscripten_memcpy_big(dest, src, num) { HEAPU8.copyWithin(dest, src, src + num) } function getHeapMax() { return 2147483648 } function emscripten_realloc_buffer(size) { try { wasmMemory.grow((size - buffer.byteLength + 65535) >>> 16) updateGlobalBufferAndViews(wasmMemory.buffer) return 1 } catch (e) {} } function _emscripten_resize_heap(requestedSize) { var oldSize = HEAPU8.length requestedSize = requestedSize >>> 0 var maxHeapSize = getHeapMax() if (requestedSize > maxHeapSize) { return false } let alignUp = (x, multiple) => x + ((multiple - (x % multiple)) % multiple) for (var cutDown = 1; cutDown <= 4; cutDown *= 2) { var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown) overGrownHeapSize = Math.min( overGrownHeapSize, requestedSize + 100663296 ) var newSize = Math.min( maxHeapSize, alignUp(Math.max(requestedSize, overGrownHeapSize), 65536) ) var replacement = emscripten_realloc_buffer(newSize) if (replacement) { return true } } return false } var ENV = {} function getExecutableName() { return thisProgram || './this.program' } function getEnvStrings() { if (!getEnvStrings.strings) { var lang = ( (typeof navigator == 'object' && navigator.languages && navigator.languages[0]) || 'C' ).replace('-', '_') + '.UTF-8' var env = { USER: 'web_user', LOGNAME: 'web_user', PATH: '/', PWD: '/', HOME: '/home/web_user', LANG: lang, _: getExecutableName(), } for (var x in ENV) { if (ENV[x] === undefined) delete env[x] else env[x] = ENV[x] } var strings = [] for (var x in env) { strings.push(x + '=' + env[x]) } getEnvStrings.strings = strings } return getEnvStrings.strings } function writeAsciiToMemory(str, buffer, dontAddNull) { for (var i = 0; i < str.length; ++i) { HEAP8[buffer++ >> 0] = str.charCodeAt(i) } if (!dontAddNull) HEAP8[buffer >> 0] = 0 } function _environ_get(__environ, environ_buf) { var bufSize = 0 getEnvStrings().forEach(function(string, i) { var ptr = environ_buf + bufSize HEAPU32[(__environ + i * 4) >> 2] = ptr writeAsciiToMemory(string, ptr) bufSize += string.length + 1 }) return 0 } function _environ_sizes_get(penviron_count, penviron_buf_size) { var strings = getEnvStrings() HEAPU32[penviron_count >> 2] = strings.length var bufSize = 0 strings.forEach(function(string) { bufSize += string.length + 1 }) HEAPU32[penviron_buf_size >> 2] = bufSize return 0 } function _proc_exit(code) { EXITSTATUS = code if (!keepRuntimeAlive()) { if (Module['onExit']) Module['onExit'](code) ABORT = true } quit_(code, new ExitStatus(code)) } function exitJS(status, implicit) { EXITSTATUS = status _proc_exit(status) } var _exit = exitJS function _fd_close(fd) { try { var stream = SYSCALLS.getStreamFromFD(fd) FS.close(stream) return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doReadv(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.read(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr if (curr < len) break } return ret } function _fd_read(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doReadv(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function convertI32PairToI53Checked(lo, hi) { return (hi + 2097152) >>> 0 < 4194305 - !!lo ? (lo >>> 0) + hi * 4294967296 : NaN } function _fd_seek(fd, offset_low, offset_high, whence, newOffset) { try { var offset = convertI32PairToI53Checked(offset_low, offset_high) if (isNaN(offset)) return 61 var stream = SYSCALLS.getStreamFromFD(fd) FS.llseek(stream, offset, whence) ;(tempI64 = [ stream.position >>> 0, ((tempDouble = stream.position), +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil( (tempDouble - +(~~tempDouble >>> 0)) / 4294967296 ) >>> 0 : 0), ]), (HEAP32[newOffset >> 2] = tempI64[0]), (HEAP32[(newOffset + 4) >> 2] = tempI64[1]) if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function doWritev(stream, iov, iovcnt, offset) { var ret = 0 for (var i = 0; i < iovcnt; i++) { var ptr = HEAPU32[iov >> 2] var len = HEAPU32[(iov + 4) >> 2] iov += 8 var curr = FS.write(stream, HEAP8, ptr, len, offset) if (curr < 0) return -1 ret += curr } return ret } function _fd_write(fd, iov, iovcnt, pnum) { try { var stream = SYSCALLS.getStreamFromFD(fd) var num = doWritev(stream, iov, iovcnt) HEAPU32[pnum >> 2] = num return 0 } catch (e) { if (typeof FS == 'undefined' || !(e instanceof FS.ErrnoError)) throw e return e.errno } } function __isLeapYear(year) { return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) } function __arraySum(array, index) { var sum = 0 for (var i = 0; i <= index; sum += array[i++]) {} return sum } var __MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] var __MONTH_DAYS_REGULAR = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] function __addDays(date, days) { var newDate = new Date(date.getTime()) while (days > 0) { var leap = __isLeapYear(newDate.getFullYear()) var currentMonth = newDate.getMonth() var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth] if (days > daysInCurrentMonth - newDate.getDate()) { days -= daysInCurrentMonth - newDate.getDate() + 1 newDate.setDate(1) if (currentMonth < 11) { newDate.setMonth(currentMonth + 1) } else { newDate.setMonth(0) newDate.setFullYear(newDate.getFullYear() + 1) } } else { newDate.setDate(newDate.getDate() + days) return newDate } } return newDate } function writeArrayToMemory(array, buffer) { HEAP8.set(array, buffer) } function _strftime(s, maxsize, format, tm) { var tm_zone = HEAP32[(tm + 40) >> 2] var date = { tm_sec: HEAP32[tm >> 2], tm_min: HEAP32[(tm + 4) >> 2], tm_hour: HEAP32[(tm + 8) >> 2], tm_mday: HEAP32[(tm + 12) >> 2], tm_mon: HEAP32[(tm + 16) >> 2], tm_year: HEAP32[(tm + 20) >> 2], tm_wday: HEAP32[(tm + 24) >> 2], tm_yday: HEAP32[(tm + 28) >> 2], tm_isdst: HEAP32[(tm + 32) >> 2], tm_gmtoff: HEAP32[(tm + 36) >> 2], tm_zone: tm_zone ? UTF8ToString(tm_zone) : '', } var pattern = UTF8ToString(format) var EXPANSION_RULES_1 = { '%c': '%a %b %d %H:%M:%S %Y', '%D': '%m/%d/%y', '%F': '%Y-%m-%d', '%h': '%b', '%r': '%I:%M:%S %p', '%R': '%H:%M', '%T': '%H:%M:%S', '%x': '%m/%d/%y', '%X': '%H:%M:%S', '%Ec': '%c', '%EC': '%C', '%Ex': '%m/%d/%y', '%EX': '%H:%M:%S', '%Ey': '%y', '%EY': '%Y', '%Od': '%d', '%Oe': '%e', '%OH': '%H', '%OI': '%I', '%Om': '%m', '%OM': '%M', '%OS': '%S', '%Ou': '%u', '%OU': '%U', '%OV': '%V', '%Ow': '%w', '%OW': '%W', '%Oy': '%y', } for (var rule in EXPANSION_RULES_1) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_1[rule] ) } var WEEKDAYS = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', ] var MONTHS = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ] function leadingSomething(value, digits, character) { var str = typeof value == 'number' ? value.toString() : value || '' while (str.length < digits) { str = character[0] + str } return str } function leadingNulls(value, digits) { return leadingSomething(value, digits, '0') } function compareByDay(date1, date2) { function sgn(value) { return value < 0 ? -1 : value > 0 ? 1 : 0 } var compare if ((compare = sgn(date1.getFullYear() - date2.getFullYear())) === 0) { if ((compare = sgn(date1.getMonth() - date2.getMonth())) === 0) { compare = sgn(date1.getDate() - date2.getDate()) } } return compare } function getFirstWeekStartDate(janFourth) { switch (janFourth.getDay()) { case 0: return new Date(janFourth.getFullYear() - 1, 11, 29) case 1: return janFourth case 2: return new Date(janFourth.getFullYear(), 0, 3) case 3: return new Date(janFourth.getFullYear(), 0, 2) case 4: return new Date(janFourth.getFullYear(), 0, 1) case 5: return new Date(janFourth.getFullYear() - 1, 11, 31) case 6: return new Date(janFourth.getFullYear() - 1, 11, 30) } } function getWeekBasedYear(date) { var thisDate = __addDays( new Date(date.tm_year + 1900, 0, 1), date.tm_yday ) var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4) var janFourthNextYear = new Date(thisDate.getFullYear() + 1, 0, 4) var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear) var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear) if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { return thisDate.getFullYear() + 1 } return thisDate.getFullYear() } return thisDate.getFullYear() - 1 } var EXPANSION_RULES_2 = { '%a': function(date) { return WEEKDAYS[date.tm_wday].substring(0, 3) }, '%A': function(date) { return WEEKDAYS[date.tm_wday] }, '%b': function(date) { return MONTHS[date.tm_mon].substring(0, 3) }, '%B': function(date) { return MONTHS[date.tm_mon] }, '%C': function(date) { var year = date.tm_year + 1900 return leadingNulls((year / 100) | 0, 2) }, '%d': function(date) { return leadingNulls(date.tm_mday, 2) }, '%e': function(date) { return leadingSomething(date.tm_mday, 2, ' ') }, '%g': function(date) { return getWeekBasedYear(date) .toString() .substring(2) }, '%G': function(date) { return getWeekBasedYear(date) }, '%H': function(date) { return leadingNulls(date.tm_hour, 2) }, '%I': function(date) { var twelveHour = date.tm_hour if (twelveHour == 0) twelveHour = 12 else if (twelveHour > 12) twelveHour -= 12 return leadingNulls(twelveHour, 2) }, '%j': function(date) { return leadingNulls( date.tm_mday + __arraySum( __isLeapYear(date.tm_year + 1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon - 1 ), 3 ) }, '%m': function(date) { return leadingNulls(date.tm_mon + 1, 2) }, '%M': function(date) { return leadingNulls(date.tm_min, 2) }, '%n': function() { return '\n' }, '%p': function(date) { if (date.tm_hour >= 0 && date.tm_hour < 12) { return 'AM' } return 'PM' }, '%S': function(date) { return leadingNulls(date.tm_sec, 2) }, '%t': function() { return '\t' }, '%u': function(date) { return date.tm_wday || 7 }, '%U': function(date) { var days = date.tm_yday + 7 - date.tm_wday return leadingNulls(Math.floor(days / 7), 2) }, '%V': function(date) { var val = Math.floor( (date.tm_yday + 7 - ((date.tm_wday + 6) % 7)) / 7 ) if ((date.tm_wday + 371 - date.tm_yday - 2) % 7 <= 2) { val++ } if (!val) { val = 52 var dec31 = (date.tm_wday + 7 - date.tm_yday - 1) % 7 if ( dec31 == 4 || (dec31 == 5 && __isLeapYear((date.tm_year % 400) - 1)) ) { val++ } } else if (val == 53) { var jan1 = (date.tm_wday + 371 - date.tm_yday) % 7 if (jan1 != 4 && (jan1 != 3 || !__isLeapYear(date.tm_year))) val = 1 } return leadingNulls(val, 2) }, '%w': function(date) { return date.tm_wday }, '%W': function(date) { var days = date.tm_yday + 7 - ((date.tm_wday + 6) % 7) return leadingNulls(Math.floor(days / 7), 2) }, '%y': function(date) { return (date.tm_year + 1900).toString().substring(2) }, '%Y': function(date) { return date.tm_year + 1900 }, '%z': function(date) { var off = date.tm_gmtoff var ahead = off >= 0 off = Math.abs(off) / 60 off = (off / 60) * 100 + (off % 60) return (ahead ? '+' : '-') + String('0000' + off).slice(-4) }, '%Z': function(date) { return date.tm_zone }, '%%': function() { return '%' }, } pattern = pattern.replace(/%%/g, '\0\0') for (var rule in EXPANSION_RULES_2) { if (pattern.includes(rule)) { pattern = pattern.replace( new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date) ) } } pattern = pattern.replace(/\0\0/g, '%') var bytes = intArrayFromString(pattern, false) if (bytes.length > maxsize) { return 0 } writeArrayToMemory(bytes, s) return bytes.length - 1 } function _strftime_l(s, maxsize, format, tm, loc) { return _strftime(s, maxsize, format, tm) } function handleException(e) { if (e instanceof ExitStatus || e == 'unwind') { return EXITSTATUS } quit_(1, e) } function allocateUTF8OnStack(str) { var size = lengthBytesUTF8(str) + 1 var ret = stackAlloc(size) stringToUTF8Array(str, HEAP8, ret, size) return ret } function getCFunc(ident) { var func = Module['_' + ident] return func } function ccall(ident, returnType, argTypes, args, opts) { var toC = { string: str => { var ret = 0 if (str !== null && str !== undefined && str !== 0) { var len = (str.length << 2) + 1 ret = stackAlloc(len) stringToUTF8(str, ret, len) } return ret }, array: arr => { var ret = stackAlloc(arr.length) writeArrayToMemory(arr, ret) return ret }, } function convertReturnValue(ret) { if (returnType === 'string') { return UTF8ToString(ret) } if (returnType === 'boolean') return Boolean(ret) return ret } var func = getCFunc(ident) var cArgs = [] var stack = 0 if (args) { for (var i = 0; i < args.length; i++) { var converter = toC[argTypes[i]] if (converter) { if (stack === 0) stack = stackSave() cArgs[i] = converter(args[i]) } else { cArgs[i] = args[i] } } } var ret = func.apply(null, cArgs) function onDone(ret) { if (stack !== 0) stackRestore(stack) return convertReturnValue(ret) } ret = onDone(ret) return ret } function cwrap(ident, returnType, argTypes, opts) { argTypes = argTypes || [] var numericArgs = argTypes.every( type => type === 'number' || type === 'boolean' ) var numericRet = returnType !== 'string' if (numericRet && numericArgs && !opts) { return getCFunc(ident) } return function() { return ccall(ident, returnType, argTypes, arguments, opts) } } function AsciiToString(ptr) { var str = '' while (1) { var ch = HEAPU8[ptr++ >> 0] if (!ch) return str str += String.fromCharCode(ch) } } var FSNode = function(parent, name, mode, rdev) { if (!parent) { parent = this } this.parent = parent this.mount = parent.mount this.mounted = null this.id = FS.nextInode++ this.name = name this.mode = mode this.node_ops = {} this.stream_ops = {} this.rdev = rdev } var readMode = 292 | 73 var writeMode = 146 Object.defineProperties(FSNode.prototype, { read: { get: function() { return (this.mode & readMode) === readMode }, set: function(val) { val ? (this.mode |= readMode) : (this.mode &= ~readMode) }, }, write: { get: function() { return (this.mode & writeMode) === writeMode }, set: function(val) { val ? (this.mode |= writeMode) : (this.mode &= ~writeMode) }, }, isFolder: { get: function() { return FS.isDir(this.mode) }, }, isDevice: { get: function() { return FS.isChrdev(this.mode) }, }, }) FS.FSNode = FSNode FS.staticInit() Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_unlink'] = FS.unlink Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice if (ENVIRONMENT_IS_NODE) { NODEFS.staticInit() } ERRNO_CODES = { EPERM: 63, ENOENT: 44, ESRCH: 71, EINTR: 27, EIO: 29, ENXIO: 60, E2BIG: 1, ENOEXEC: 45, EBADF: 8, ECHILD: 12, EAGAIN: 6, EWOULDBLOCK: 6, ENOMEM: 48, EACCES: 2, EFAULT: 21, ENOTBLK: 105, EBUSY: 10, EEXIST: 20, EXDEV: 75, ENODEV: 43, ENOTDIR: 54, EISDIR: 31, EINVAL: 28, ENFILE: 41, EMFILE: 33, ENOTTY: 59, ETXTBSY: 74, EFBIG: 22, ENOSPC: 51, ESPIPE: 70, EROFS: 69, EMLINK: 34, EPIPE: 64, EDOM: 18, ERANGE: 68, ENOMSG: 49, EIDRM: 24, ECHRNG: 106, EL2NSYNC: 156, EL3HLT: 107, EL3RST: 108, ELNRNG: 109, EUNATCH: 110, ENOCSI: 111, EL2HLT: 112, EDEADLK: 16, ENOLCK: 46, EBADE: 113, EBADR: 114, EXFULL: 115, ENOANO: 104, EBADRQC: 103, EBADSLT: 102, EDEADLOCK: 16, EBFONT: 101, ENOSTR: 100, ENODATA: 116, ETIME: 117, ENOSR: 118, ENONET: 119, ENOPKG: 120, EREMOTE: 121, ENOLINK: 47, EADV: 122, ESRMNT: 123, ECOMM: 124, EPROTO: 65, EMULTIHOP: 36, EDOTDOT: 125, EBADMSG: 9, ENOTUNIQ: 126, EBADFD: 127, EREMCHG: 128, ELIBACC: 129, ELIBBAD: 130, ELIBSCN: 131, ELIBMAX: 132, ELIBEXEC: 133, ENOSYS: 52, ENOTEMPTY: 55, ENAMETOOLONG: 37, ELOOP: 32, EOPNOTSUPP: 138, EPFNOSUPPORT: 139, ECONNRESET: 15, ENOBUFS: 42, EAFNOSUPPORT: 5, EPROTOTYPE: 67, ENOTSOCK: 57, ENOPROTOOPT: 50, ESHUTDOWN: 140, ECONNREFUSED: 14, EADDRINUSE: 3, ECONNABORTED: 13, ENETUNREACH: 40, ENETDOWN: 38, ETIMEDOUT: 73, EHOSTDOWN: 142, EHOSTUNREACH: 23, EINPROGRESS: 26, EALREADY: 7, EDESTADDRREQ: 17, EMSGSIZE: 35, EPROTONOSUPPORT: 66, ESOCKTNOSUPPORT: 137, EADDRNOTAVAIL: 4, ENETRESET: 39, EISCONN: 30, ENOTCONN: 53, ETOOMANYREFS: 141, EUSERS: 136, EDQUOT: 19, ESTALE: 72, ENOTSUP: 138, ENOMEDIUM: 148, EILSEQ: 25, EOVERFLOW: 61, ECANCELED: 11, ENOTRECOVERABLE: 56, EOWNERDEAD: 62, ESTRPIPE: 135, } var asmLibraryArg = { a: ___cxa_throw, d: ___syscall_fcntl64, r: ___syscall_getcwd, i: ___syscall_ioctl, j: ___syscall_openat, n: ___syscall_readlinkat, o: ___syscall_stat64, b: _abort, f: _emscripten_memcpy_big, m: _emscripten_resize_heap, p: _environ_get, q: _environ_sizes_get, c: _exit, e: _fd_close, h: _fd_read, k: _fd_seek, g: _fd_write, l: _strftime_l, } var asm = createWasm() var ___wasm_call_ctors = (Module['___wasm_call_ctors'] = function() { return (___wasm_call_ctors = Module['___wasm_call_ctors'] = Module['asm']['t']).apply(null, arguments) }) var _main = (Module['_main'] = function() { return (_main = Module['_main'] = Module['asm']['u']).apply( null, arguments ) }) var ___errno_location = (Module['___errno_location'] = function() { return (___errno_location = Module['___errno_location'] = Module['asm']['v']).apply(null, arguments) }) var _itk_wasm_input_array_alloc = (Module[ '_itk_wasm_input_array_alloc' ] = function() { return (_itk_wasm_input_array_alloc = Module[ '_itk_wasm_input_array_alloc' ] = Module['asm']['w']).apply(null, arguments) }) var _itk_wasm_input_json_alloc = (Module[ '_itk_wasm_input_json_alloc' ] = function() { return (_itk_wasm_input_json_alloc = Module[ '_itk_wasm_input_json_alloc' ] = Module['asm']['x']).apply(null, arguments) }) var _itk_wasm_output_json_address = (Module[ '_itk_wasm_output_json_address' ] = function() { return (_itk_wasm_output_json_address = Module[ '_itk_wasm_output_json_address' ] = Module['asm']['y']).apply(null, arguments) }) var _itk_wasm_output_json_size = (Module[ '_itk_wasm_output_json_size' ] = function() { return (_itk_wasm_output_json_size = Module[ '_itk_wasm_output_json_size' ] = Module['asm']['z']).apply(null, arguments) }) var _itk_wasm_output_array_address = (Module[ '_itk_wasm_output_array_address' ] = function() { return (_itk_wasm_output_array_address = Module[ '_itk_wasm_output_array_address' ] = Module['asm']['A']).apply(null, arguments) }) var _itk_wasm_output_array_size = (Module[ '_itk_wasm_output_array_size' ] = function() { return (_itk_wasm_output_array_size = Module[ '_itk_wasm_output_array_size' ] = Module['asm']['B']).apply(null, arguments) }) var _itk_wasm_free_all = (Module['_itk_wasm_free_all'] = function() { return (_itk_wasm_free_all = Module['_itk_wasm_free_all'] = Module['asm']['C']).apply(null, arguments) }) var stackSave = (Module['stackSave'] = function() { return (stackSave = Module['stackSave'] = Module['asm']['E']).apply( null, arguments ) }) var stackRestore = (Module['stackRestore'] = function() { return (stackRestore = Module['stackRestore'] = Module['asm']['F']).apply( null, arguments ) }) var stackAlloc = (Module['stackAlloc'] = function() { return (stackAlloc = Module['stackAlloc'] = Module['asm']['G']).apply( null, arguments ) }) var ___cxa_is_pointer_type = (Module[ '___cxa_is_pointer_type' ] = function() { return (___cxa_is_pointer_type = Module['___cxa_is_pointer_type'] = Module['asm']['H']).apply(null, arguments) }) Module['addRunDependency'] = addRunDependency Module['removeRunDependency'] = removeRunDependency Module['FS_createPath'] = FS.createPath Module['FS_createDataFile'] = FS.createDataFile Module['FS_createPreloadedFile'] = FS.createPreloadedFile Module['FS_createLazyFile'] = FS.createLazyFile Module['FS_createDevice'] = FS.createDevice Module['FS_unlink'] = FS.unlink Module['callMain'] = callMain Module['ccall'] = ccall Module['cwrap'] = cwrap Module['AsciiToString'] = AsciiToString Module['writeArrayToMemory'] = writeArrayToMemory Module['writeAsciiToMemory'] = writeAsciiToMemory var calledRun dependenciesFulfilled = function runCaller() { if (!calledRun) run() if (!calledRun) dependenciesFulfilled = runCaller } function callMain(args) { var entryFunction = Module['_main'] args = args || [] args.unshift(thisProgram) var argc = args.length var argv = stackAlloc((argc + 1) * 4) var argv_ptr = argv >> 2 args.forEach(arg => { HEAP32[argv_ptr++] = allocateUTF8OnStack(arg) }) HEAP32[argv_ptr] = 0 try { var ret = entryFunction(argc, argv) exitJS(ret, true) return ret } catch (e) { return handleException(e) } } function run(args) { args = args || arguments_ if (runDependencies > 0) { return } preRun() if (runDependencies > 0) { return } function doRun() { if (calledRun) return calledRun = true Module['calledRun'] = true if (ABORT) return initRuntime() preMain() readyPromiseResolve(Module) if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized']() if (shouldRunNow) callMain(args) postRun() } if (Module['setStatus']) { Module['setStatus']('Running...') setTimeout(function() { setTimeout(function() { Module['setStatus']('') }, 1) doRun() }, 1) } else { doRun() } } if (Module['preInit']) { if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']] while (Module['preInit'].length > 0) { Module['preInit'].pop()() } } var shouldRunNow = false if (Module['noInitialRun']) shouldRunNow = false run() Module.mountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) if (FS.isDir(containingDir) || containingDir === '/') { return } var currentDir = '/' var splitContainingDir = containingDir.split(path.sep) for (var ii = 1; ii < splitContainingDir.length; ii++) { currentDir += splitContainingDir[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } FS.mount(NODEFS, { root: containingDir }, currentDir) return currentDir + path.basename(filePath) } Module.unmountContainingDir = function(filePath) { if (!ENVIRONMENT_IS_NODE) { return } var path = require('path') var containingDir = path.dirname(filePath) FS.unmount(containingDir) } Module.fs_mkdirs = function(dirs) { var currentDir = '/' var splitDirs = dirs.split('/') for (var ii = 1; ii < splitDirs.length; ++ii) { currentDir += splitDirs[ii] if (!FS.analyzePath(currentDir).exists) { FS.mkdir(currentDir) } currentDir += '/' } } Module.fs_readFile = function(path, opts) { return FS.readFile(path, opts) } Module.fs_writeFile = function(path, data, opts) { return FS.writeFile(path, data, opts) } Module.fs_unlink = function(path) { return FS.unlink(path) } Module.fs_open = function(path, flags, mode) { return FS.open(path, flags, mode) } Module.fs_stat = function(path) { return FS.stat(path) } Module.fs_read = function(stream, buffer, offset, length, position) { return FS.read(stream, buffer, offset, length, position) } Module.fs_close = function(stream) { return FS.close(stream) } return ResampleLabelImage.ready } })() if (typeof exports === 'object' && typeof module === 'object') module.exports = ResampleLabelImage else if (typeof define === 'function' && define['amd']) define([], function() { return ResampleLabelImage }) else if (typeof exports === 'object') exports['ResampleLabelImage'] = ResampleLabelImage ================================================ FILE: src/IO/ResampleLabelImage/index.mjs ================================================ #!/usr/bin/env node /* eslint-env node */ import { Command } from 'commander/esm.mjs' import path from 'path' const program = new Command() import { readLocalFile, writeLocalFile, runPipelineNode, InterfaceTypes, } from 'itk-wasm' program .description('Resample an image') .option('-l, --label-image', 'Label image as opposed to an intensity image') .arguments(' ') .parse(process.argv) if (program.args.length < 2) { console.error('Please pass in both the input and output file paths.') process.exit(1) } const inputFile = program.args[0] const outputFile = program.args[1] const pipelinePath = path.resolve('./emscripten-build/ResampleLabelImage') try { const inputImage = await readLocalFile(inputFile) const inputs = [ { type: InterfaceTypes.Image, data: inputImage, }, ] const desiredOutputs = [ { type: InterfaceTypes.Image }, // { type: InterfaceTypes.TextStream }, ] const size = [100, 100] const spacing = [2, 2] const origin = [1, 3] const direction = [0, 1, 1, 0] const args = [ '0', '0', '--size', size.join(','), '--spacing', spacing.join(','), '--origin', origin.join(','), '--direction', direction.join(','), // '--max-total-splits', // '10', // '--split', // '5', '--memory-io', ] const { outputs } = await runPipelineNode( pipelinePath, args, desiredOutputs, inputs ) const outputImage = outputs[0].data await writeLocalFile(outputImage, outputFile) } catch (error) { console.error('Error during processing:\n') console.error(error) } ================================================ FILE: src/IO/ResampleLabelImage/package.json ================================================ { "name": "resample", "version": "1.0.0", "description": "", "main": "index.js", "type": "module", "scripts": { "build": "itk-wasm build", "build:debug": "itk-wasm build -- -DCMAKE_BUILD_TYPE=Debug", "test": "node index.mjs ../Downsample/cthead1-bin.png resample.png" }, "author": "", "license": "Apache-2.0", "dependencies": { "itk-image-io": "^1.0.0-b.84", "itk-wasm": "^1.0.0-b.84" } } ================================================ FILE: src/IO/ResampleLabelImage/resampleLabelImage.js ================================================ import { arraysEqual } from '../../internalUtils.js' import { runWasm } from '../itkWasmUtils.js' export async function resampleLabelImage(image, labelImage) { const { size, spacing, origin, direction } = image const args = [ '--size', size.join(','), '--spacing', spacing.join(','), '--origin', origin.join(','), '--direction', direction.join(','), ] return runWasm({ pipeline: 'ResampleLabelImage', args, images: [labelImage] }) } export function compareImageSpaces(imageA, imageB) { const equalKeys = ['size', 'direction', 'origin', 'spacing'].map(key => arraysEqual(imageA[key], imageB[key]) ) return equalKeys.every(b => b) } ================================================ FILE: src/IO/ZarrMultiscaleSpatialImage.js ================================================ import { PixelTypes } from 'itk-wasm' import PQueue from 'p-queue' import MultiscaleSpatialImage from './MultiscaleSpatialImage' import bloscZarrDecompress from '../Compression/bloscZarrDecompress' import ZarrStoreParser from './ZarrStoreParser' import HttpStore from './HttpStore' import { CXYZT, toDimensionMap } from './dimensionUtils' import { getComponentType } from './dtypeUtils' import { MAX_CONCURRENCY } from '../Context/ViewerMachineContext' // ends with zarr and optional nested image name like foo.zarr/image1 export const isZarr = url => /\w[./]zarr\b/.test(url) const TCZYX = Object.freeze(['t', 'c', 'z', 'y', 'x']) const composeTransforms = (transforms = [], dimCount) => transforms.reduce( ({ scale, translation }, transform) => { if (transform.type === 'scale') { const { scale: transformScale } = transform return { scale: scale.map((s, i) => s * transformScale[i]), translation: translation.map((t, i) => t * transformScale[i]), } } else if (transform.type === 'translation') { const { translation: transformTranslation } = transform return { scale, translation: translation.map((t, i) => t + transformTranslation[i]), } } }, { scale: Array(dimCount).fill(1), translation: Array(dimCount).fill(0) } ) export const computeTransform = (imageMetadata, datasetMetadata, dimCount) => { const global = composeTransforms( imageMetadata.coordinateTransformations, dimCount ) const dataset = composeTransforms( datasetMetadata.coordinateTransformations, dimCount ) return composeTransforms( [ { type: 'scale', scale: dataset.scale }, { type: 'translation', translation: dataset.translation }, { type: 'scale', scale: global.scale }, { type: 'translation', translation: global.translation }, ], dimCount ) } // if missing coordinateTransformations, make all scales same size as finest scale const ensureScaleTransforms = datasetsWithArrayMetadata => { const hasDatasetCoordinateTransform = datasetsWithArrayMetadata.some( ({ dataset }) => dataset.coordinateTransformations ) if (hasDatasetCoordinateTransform) return datasetsWithArrayMetadata const targetSize = datasetsWithArrayMetadata[0].pixelArrayMetadata.shape return datasetsWithArrayMetadata.map(({ dataset, pixelArrayMetadata }) => { const { shape } = pixelArrayMetadata const scale = targetSize.map((target, idx) => target / shape[idx]) return { dataset: { ...dataset, coordinateTransformations: [{ scale, type: 'scale' }], }, pixelArrayMetadata, } }) } // lazy creation of voxel/pixel/dimension coordinates array const makeCoords = ({ shape, multiscaleImage, dataset }) => { const axes = multiscaleImage.axes?.map(({ name }) => name) ?? TCZYX const coords = new Map(axes.map(dim => [dim, null])) const { scale: spacingDataset, translation: originDataset, } = computeTransform(multiscaleImage, dataset, axes.length) return { get(dim) { if (coords.get(dim) === null) { // make array const dimIdx = axes.indexOf(dim) const spacing = spacingDataset[dimIdx] const origin = originDataset[dimIdx] const coordsPerElement = new Float32Array(shape[dimIdx]) for (let i = 0; i < coordsPerElement.length; i++) { coordsPerElement[i] = i * spacing + origin } coords.set(dim, coordsPerElement) } return coords.get(dim) }, has(dim) { return axes.includes(dim) }, } } const findAxesLongNames = async ({ dataset, dataSource, dims }) => { const upOneLevel = dataset.path .split('/') .slice(0, -1) .join('') return new Map( await Promise.all( dims.map(dim => dataSource.getItem(`${upOneLevel}/${dim}/.zattrs`)) ).then(dimensionsZattrs => dimensionsZattrs.map(({ long_name }, i) => [dims[i], long_name]) ) ) } const createScaledImageInfo = async ({ multiscaleImage, dataset, pixelArrayMetadata, dataSource, multiscaleSpatialImageVersion, }) => { const scaleZattrs = multiscaleSpatialImageVersion ? await dataSource.getItem(`${dataset.path}/.zattrs`) : {} const dims = scaleZattrs._ARRAY_DIMENSIONS ?? multiscaleImage.axes?.map(axis => axis.name ?? axis) ?? TCZYX // default to TCZYX for NGFF v0.1 const { shape, chunks } = pixelArrayMetadata const chunkSize = toDimensionMap(dims, chunks) const arrayShape = toDimensionMap(dims, shape) const axesNames = multiscaleSpatialImageVersion ? await findAxesLongNames({ dataset, dataSource, dims }) : undefined return { dims, pixelArrayMetadata, name: multiscaleImage.name, pixelArrayPath: dataset.path, coords: makeCoords({ shape, multiscaleImage, dataset }), ranges: scaleZattrs.ranges ?? multiscaleImage.ranges, direction: scaleZattrs.direction ?? multiscaleImage.direction, axesNames, chunkCount: toDimensionMap( dims, dims.map(dim => Math.ceil(arrayShape.get(dim) / chunkSize.get(dim))) ), chunkSize, arrayShape, } } const extractScaleSpacing = async dataSource => { const zattrs = await dataSource.getItem('.zattrs') const { multiscales, multiscaleSpatialImageVersion } = zattrs const multiscaleImage = Array.isArray(multiscales) ? multiscales[0] // if multiple images (multiscales), just grab first one : multiscales const datasetsWithArrayMetadataRaw = await Promise.all( multiscaleImage.datasets.map(async dataset => ({ dataset, pixelArrayMetadata: await dataSource.getItem(`${dataset.path}/.zarray`), })) ) const datasetsWithArrayMetadata = ensureScaleTransforms( datasetsWithArrayMetadataRaw ) const scaleInfo = await Promise.all( datasetsWithArrayMetadata.map(async ({ dataset, pixelArrayMetadata }) => { return createScaledImageInfo({ multiscaleImage, dataset, pixelArrayMetadata, dataSource, multiscaleSpatialImageVersion, }) }) ) const info = scaleInfo[0] const components = info.arrayShape.get('c') ?? 1 const imageType = { // How many spatial dimensions? Count greater than 1, X Y Z elements because "axis" metadata not defined in ngff V0.1 dimension: ['x', 'y', 'z'].filter(dim => info.arrayShape.get(dim) > 1) .length, pixelType: components === 1 ? PixelTypes.Scalar : PixelTypes.VariableLengthVector, componentType: getComponentType(info.pixelArrayMetadata.dtype), components, } return { scaleInfo, imageType } } class ZarrMultiscaleSpatialImage extends MultiscaleSpatialImage { // Store parameter is object with getItem (but not a ZarrStoreParser) static async fromStore(store, maxConcurrency) { const zarrStoreParser = new ZarrStoreParser(store) const { scaleInfo, imageType } = await extractScaleSpacing(zarrStoreParser) return new ZarrMultiscaleSpatialImage( zarrStoreParser, scaleInfo, imageType, maxConcurrency ) } static async fromUrl(url, maxConcurrency) { return ZarrMultiscaleSpatialImage.fromStore( new HttpStore(url), maxConcurrency ) } // Use static factory functions to construct constructor(zarrStoreParser, scaleInfo, imageType, maxConcurrency) { super(scaleInfo, imageType) this.dataSource = zarrStoreParser const concurrency = Math.min( window.navigator.hardwareConcurrency, maxConcurrency ?? MAX_CONCURRENCY ) this.rpcQueue = new PQueue({ concurrency }) } async getChunksImpl(scale, cxyztArray) { const info = this.scaleInfo[scale] const chunkPathBase = info.pixelArrayPath const chunkPaths = [] const chunkPromises = [] const { dimension_separator: dimSeparator = '.' } = info.pixelArrayMetadata for (let index = 0; index < cxyztArray.length; index++) { let chunkPath = `${chunkPathBase}/` for (let dd = 0; dd < info.dims.length; dd++) { const dim = info.dims[dd] chunkPath = `${chunkPath}${ cxyztArray[index][CXYZT.indexOf(dim)] }${dimSeparator}` } chunkPath = chunkPath.slice(0, -1) chunkPaths.push(chunkPath) chunkPromises.push(() => this.dataSource.getItem(chunkPath)) } const compressedChunks = await this.rpcQueue.addAll(chunkPromises) const toDecompress = [] for (let index = 0; index < compressedChunks.length; index++) { toDecompress.push({ data: compressedChunks[index], metadata: info.pixelArrayMetadata, }) } return bloscZarrDecompress(toDecompress) } } export default ZarrMultiscaleSpatialImage ================================================ FILE: src/IO/ZarrStoreParser.js ================================================ const isMetadata = item => ['.zattrs', '.zgroup', '.zarray'].some(knownMetadataFile => item.endsWith(knownMetadataFile) ) class ZarrStore { constructor(store) { this.store = store this.decoder = new TextDecoder() } toJson(data) { return JSON.parse(this.decoder.decode(data)) } async getItem(item) { const data = await this.store.getItem(item) return isMetadata(item) ? this.toJson(data) : data } } export default ZarrStore ================================================ FILE: src/IO/componentTypeToTypedArray.js ================================================ import { IntTypes, FloatTypes } from 'itk-wasm' const componentTypeToTypedArray = new Map([ [IntTypes.Int8, Int8Array], [IntTypes.UInt8, Uint8Array], [IntTypes.Int16, Int16Array], [IntTypes.UInt16, Uint16Array], [IntTypes.Int32, Int32Array], [IntTypes.UInt32, Uint32Array], [FloatTypes.Float32, Float32Array], [FloatTypes.Float64, Float64Array], ]) export default componentTypeToTypedArray ================================================ FILE: src/IO/composeComponents.js ================================================ const sum = (a, b) => a + b const countElements = componentInfo => componentInfo .map(({ data, srcComponentCount }) => data.length / srcComponentCount) .reduce(sum) const getLargestTypeByBytes = componentInfo => componentInfo .map(({ data }) => data) .reduce((lastType, typedArray) => lastType.BYTES_PER_ELEMENT >= typedArray.BYTES_PER_ELEMENT ? lastType : typedArray ) const fuseComponents = ({ componentInfo, arrayToFill }) => { const elementCount = countElements(componentInfo) const fusedImageData = arrayToFill ?? new (getLargestTypeByBytes(componentInfo).constructor)(elementCount) const componentCount = componentInfo.length const tupleCount = elementCount / componentCount for (let cIdx = 0; cIdx < componentCount; cIdx++) { const { data, srcComponentCount, fromComponent } = componentInfo[cIdx] for (let tuple = 0; tuple < tupleCount; tuple++) { fusedImageData[tuple * componentCount + cIdx] = data[tuple * srcComponentCount + fromComponent] } } return fusedImageData } // returns "component infos" export const parseByComponent = scaleImage => { if (!scaleImage) return [] // lift ITK image into array if not already (like from InMemoryMultiscaleSpatialImage) const scaleImages = Array.isArray(scaleImage) ? scaleImage : [scaleImage] return scaleImages.flatMap(image => { const srcComponentCount = image.imageType.components // pull each component from image return [...Array(srcComponentCount).keys()].map(fromComponent => ({ fromComponent, srcComponentCount, image, data: image.data, })) }) } export const pickAndFuseComponents = async ({ image, labelImage, components, }) => { // not Conglomerate, no label image, and all components needed: just return image if ( !Array.isArray(image) && !labelImage && image.imageType.components === components.length ) return image const [imageByComponent, labelByComponent] = [image, labelImage].map(image => parseByComponent(image) ) const componentInfo = components.map( comp => comp >= 0 ? imageByComponent[comp] : labelByComponent[comp * -1 - 1] // label component index starts at -1 ) componentInfo.forEach((compInfo, idx) => { if (!compInfo) { throw new Error( 'pickAndFuseComponents: Missing component for requested component: ' + idx ) } }) const imageArray = fuseComponents({ componentInfo, }) const ranges = componentInfo.map( ({ image: { ranges }, fromComponent }) => ranges && ranges[fromComponent] ) // picks out one from ConglomerateImage const singleImage = Array.isArray(image) ? image[0] : image const itkImage = { ...singleImage, data: imageArray, imageType: { ...singleImage.imageType, components: components.length, }, ranges, } return itkImage } export const composeComponents = images => { const componentCount = images.map(i => i.imageType.components).reduce(sum) // include all components const components = [...Array(componentCount).keys()] // compose Conglomerate images return pickAndFuseComponents({ image: images, components, }) } ================================================ FILE: src/IO/dimensionUtils.js ================================================ export const CXYZT = Object.freeze(['c', 'x', 'y', 'z', 't']) // viewer indexing export const ensuredDims = (defaultValue, ensuredDims, dimMap) => ensuredDims.reduce( (map, dim) => map.set(dim, map.get(dim) ?? defaultValue), new Map(dimMap) ) export const toDimensionMap = (dims, array) => new Map(dims.map((dim, i) => [dim, array[i]])) // example: orderBy(['y', 'x'])(new Map([['x', 1], ['y', 2], ['z', 3]])) -> Map([['y', 2], ['x', 1]]) // drops dimensions that are not in dims! export const orderBy = dims => map => new Map(dims.map(dim => [dim, map.get(dim)])) export const chunkArray = (chunkSize, array) => { const chunks = [] for (let i = 0; i < array.length; i += chunkSize) { chunks.push(array.slice(i, i + chunkSize)) } return chunks } ================================================ FILE: src/IO/dtypeUtils.js ================================================ import { IntTypes, FloatTypes } from 'itk-wasm' // Currently missing on Safari const bigIntArrayType = typeof globalThis.BigInt64Array === 'function' ? globalThis.BigInt64Array : Int32Array const bigUintArrayType = typeof globalThis.BigUint64Array === 'function' ? globalThis.BigUint64Array : Uint32Array // key is sans endian const dtypeUtils = Array.from( new Map([ ['b', [Int8Array, 'getInt8', IntTypes.Int8]], ['B', [Uint8Array, 'getUint8', IntTypes.UInt8]], ['u1', [Uint8Array, 'getUint8', IntTypes.UInt8]], ['i1', [Int8Array, 'getInt8', IntTypes.Int8]], ['u2', [Uint16Array, 'getUint16', IntTypes.UInt16]], ['i2', [Int16Array, 'getInt16', IntTypes.Int16]], ['u4', [Uint32Array, 'getUint32', IntTypes.UInt32]], ['i4', [Int32Array, 'getInt32', IntTypes.Int32]], ['u8', [bigUintArrayType, 'getBigUint64', IntTypes.UInt64]], ['i8', [bigIntArrayType, 'getBigInt64', IntTypes.Int64]], ['f4', [Float32Array, 'getFloat32', FloatTypes.Float32]], ['f8', [Float64Array, 'getFloat64', FloatTypes.Float64]], ]) ).reduce( (map, [dtype, [TypedArray, dataViewGetter, itkComponent]]) => map.set(dtype, { TypedArray, dataViewGetter, itkComponent }), new Map() ) const getType = dtype => dtype.replace(/^(<|>|=|\|)/, '') // remove starting < > = | endianness export const getSize = dtype => { const type = getType(dtype) return type.length < 2 ? 1 : Number(type.slice(-1)) } export const getComponentType = dtype => dtypeUtils.get(getType(dtype)).itkComponent export const getTypedArray = dtype => dtypeUtils.get(getType(dtype)).TypedArray export const testLittleEndian = dtype => dtype.charAt(0) === '<' export const ElementGetter = (dtype, buffer) => { const view = new DataView(buffer) const size = getSize(dtype) const isLittleEndian = testLittleEndian(dtype) const { dataViewGetter } = dtypeUtils.get(getType(dtype)) return index => view[dataViewGetter](index * size, isLittleEndian) } export const getDtype = (typedArrayConstructor, endianness = '<') => { const typedArrayToDtype = new Map( Array.from(dtypeUtils).map(([key, { TypedArray }]) => [TypedArray, key]) ) return `${endianness}${typedArrayToDtype.get(typedArrayConstructor)}` } ================================================ FILE: src/IO/fetchBinaryContent.js ================================================ import axios from 'axios' import any from 'promise.any' async function fetchBinaryContent(urlObj, progressCallback) { if (urlObj.protocol === 'ipfs:') { const splitPathname = urlObj.href.split('/') const cid = splitPathname[2] const path = splitPathname.slice(3).join('/') const httpUrls = [`http://${cid}.ipfs.localhost:8080/${path}`] const externalGateways = ['cf-ipfs.com', 'dweb.link'] externalGateways.forEach(g => { const target = `https://${cid}.ipfs.${g}/${path}` httpUrls.push(target) }) try { const responses = httpUrls.map((target, index) => { const callback = index === 2 ? progressCallback : null return axios.get(target, { onDownloadProgress: callback, responseType: 'arraybuffer', }) }) const response = await any(responses) return response.data } catch (error) { // Possibly no local node or network connection } } else { const response = await axios.get(urlObj.href, { onDownloadProgress: progressCallback, responseType: 'arraybuffer', }) return response.data } } export default fetchBinaryContent ================================================ FILE: src/IO/fetchJsonContent.js ================================================ import vtkHttpDataAccessHelper from 'vtk.js/Sources/IO/Core/DataAccessHelper/HttpDataAccessHelper' const fetchJsonContent = (url, progressCallback) => vtkHttpDataAccessHelper.fetchJSON({}, url, { progressCallback }) export default fetchJsonContent ================================================ FILE: src/IO/itkWasmUtils.js ================================================ import { runPipeline, InterfaceTypes, imageSharedBufferOrCopy, WorkerPool, stackImages, } from 'itk-wasm' export async function runWasm({ pipeline, args, images, outputs = [{ type: InterfaceTypes.Image }], maxSplits = 4, // avoid out of memory errors with larger images }) { const numberOfWorkers = navigator.hardwareConcurrency || 6 const aImage = images[0] const splits = Math.min( parseInt(numberOfWorkers / 2), Math.max(aImage.size[aImage.size.length - 1], 1), maxSplits ) const tasks = [...Array(splits).keys()].map(split => { const taskArgs = [ ...[...Array(images.length).keys()].map(num => num.toString()), '0', ...args.map(o => o.toString()), '--max-total-splits', '' + splits, '--split', '' + split, '--number-of-splits', '' + splits, '--memory-io', ] const inputs = images.map(image => ({ type: InterfaceTypes.Image, data: imageSharedBufferOrCopy(image), })) return [pipeline, taskArgs, outputs, inputs] }) const workerPool = new WorkerPool(numberOfWorkers, runPipeline) const results = await workerPool.runTasks(tasks).promise workerPool.terminateWorkers() const validResults = results.filter(r => r.returnValue === 0) const imageSplits = validResults.map(({ outputs }) => outputs[0].data) return stackImages(imageSplits) } ================================================ FILE: src/IO/ndarrayToItkImage.js ================================================ import { PixelTypes, IntTypes, FloatTypes } from 'itk-wasm' const numpy2itkType = { int8: { componentType: IntTypes.Int8, arrayType: Int8Array, }, uint8: { componentType: IntTypes.UInt8, arrayType: Uint8Array, }, int16: { componentType: IntTypes.Int16, arrayType: Int16Array, }, uint16: { componentType: IntTypes.UInt16, arrayType: Uint16Array, }, int32: { componentType: IntTypes.Int32, arrayType: Int32Array, }, uint32: { componentType: IntTypes.UInt32, arrayType: Uint32Array, }, int64: { componentType: IntTypes.Int64, arrayType: BigInt64Array, }, uint64: { componentType: IntTypes.UInt64, arrayType: BigUint64Array, }, float32: { componentType: FloatTypes.Float32, arrayType: Float32Array, }, float64: { componentType: FloatTypes.Float64, arrayType: Float64Array, }, bool: { componentType: IntTypes.UInt8, arrayType: Uint8Array, }, } function ndarrayToItkImage(array) { if (array._rtype !== 'ndarray') { throw new Error('Invalid ndarray type: ' + array._rtype) } const { componentType, arrayType } = numpy2itkType[array._rdtype] if ( array._rshape.length === 2 || (array._rshape.length == 3 && array._rshape[2] <= 4) ) { const channels = array._rshape.length === 3 ? array._rshape[2] : 1 const pixelType = channels === 1 ? PixelTypes.Scalar : PixelTypes.VariableLengthVector return { imageType: { dimension: 2, pixelType, componentType, components: channels, }, name: 'Image', origin: [0.0, 0.0], spacing: [1.0, 1.0], direction: new Float64Array([1.0, 0.0, 0.0, 1.0]), size: [array._rshape[1], array._rshape[0]], data: new arrayType(array._rvalue), } } else if (array._rshape.length === 3) { return { imageType: { dimension: 3, pixelType: PixelTypes.Scalar, componentType, components: 1, }, name: 'Image', origin: [0.0, 0.0, 0.0], spacing: [1.0, 1.0, 1.0], direction: new Float64Array([ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, ]), size: [array._rshape[2], array._rshape[1], array._rshape[0]], data: new arrayType(array._rvalue), } } else if (array._rshape.length === 4) { return { imageType: { dimension: 3, pixelType: PixelTypes.Scalar, componentType, components: array._rshape[3], }, name: 'Image', origin: [0.0, 0.0, 0.0], spacing: [1.0, 1.0, 1.0], direction: new Float64Array([ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, ]), size: [array._rshape[2], array._rshape[1], array._rshape[0]], data: new arrayType(array._rvalue), } } else { throw new Error(`Unsupported shape: ${array._rshape}`) } } export default ndarrayToItkImage ================================================ FILE: src/IO/ndarrayToPointSet.js ================================================ import vtk from 'vtk.js/Sources/vtk' const numpy2TypedArray = { int8: Int8Array, uint8: Uint8Array, int16: Int16Array, uint16: Uint16Array, int32: Int32Array, uint32: Uint32Array, float32: Float32Array, float64: Float64Array, } function ndarrayToPointSet(array) { if (array._rtype !== 'ndarray') { throw new Error('Invalid ndarray type: ' + array._rtype) } let arrayType = numpy2TypedArray[array._rdtype] if (array._rshape.length !== 2) { throw new Error(`Unsupported dimension: ${array._rshape.length}`) } if (array._rshape[1] === 2) { // convert to 3d point sets const originalPoints = new arrayType(array._rvalue) const newPoints = new Float32Array( new ArrayBuffer(array._rshape[0] * 3 * 4) ) for (let i = 0; i < array._rshape[0]; i++) { newPoints[i * 3] = originalPoints[i * 2] newPoints[i * 3 + 1] = originalPoints[i * 2 + 1] newPoints[i * 3 + 2] = -5.0e-6 } arrayType = Float32Array array = { _rtype: 'ndarray', _rdtype: 'float32', _rshape: [array._rshape[0], 3], _rvalue: newPoints.buffer, } } else if (array._rshape[1] !== 3) { throw new Error(`Unsupported shape: ${array._rshape}`) } const verts = new Uint32Array(new ArrayBuffer(array._rshape[0] * 2 * 4)) for (let i = 0; i < array._rshape[0] * 2; i += 2) { verts[i] = 1 verts[i + 1] = i / 2 } const size = array._rshape.reduce((a, b) => a * b, 1) return vtk({ vtkClass: 'vtkPolyData', points: { vtkClass: 'vtkPoints', name: '_points', numberOfComponents: 3, dataType: arrayType.name, size, values: new arrayType(array._rvalue), }, verts: { vtkClass: 'vtkCellArray', name: '_verts', numberOfComponents: 1, dataType: 'Uint32Array', size: array._rshape[0] * 2, values: verts, }, }) } export default ndarrayToPointSet ================================================ FILE: src/IO/processFiles.js ================================================ import { extensionToMeshIO, readImageFile, readImageDICOMFileSeries, readMeshFile, FloatTypes, getFileExtension, meshToPolyData, } from 'itk-wasm' import vtk from 'vtk.js/Sources/vtk' import vtkXMLPolyDataReader from 'vtk.js/Sources/IO/XML/XMLPolyDataReader' import vtkXMLImageDataReader from 'vtk.js/Sources/IO/XML/XMLImageDataReader' import PromiseFileReader from 'promise-file-reader' import vtkITKHelper from 'vtk.js/Sources/Common/DataModel/ITKHelper' import UserInterface from '../UserInterface' import createViewer from '../createViewer' const MAX_LABELS_IN_LABEL_IMAGE = 64 const readDataFromFiles = async files => { const readers = Array.from(files).map(async file => { const extension = getFileExtension(file.name) if (extension === 'vti') { return PromiseFileReader.readAsArrayBuffer(file).then(fileContents => { const vtiReader = vtkXMLImageDataReader.newInstance() vtiReader.parseAsArrayBuffer(fileContents) const vtkImage = vtiReader.getOutputData(0) const itkImage = vtkITKHelper.convertVtkToItkImage(vtkImage) return Promise.resolve({ is3D: true, data: itkImage, }) }) } else if (extension === 'vtp') { return PromiseFileReader.readAsArrayBuffer(file).then(fileContents => { const vtpReader = vtkXMLPolyDataReader.newInstance() vtpReader.parseAsArrayBuffer(fileContents) const polyData = vtpReader.getOutputData(0) return Promise.resolve({ is3D: true, data: polyData, }) }) } else if (extensionToMeshIO.has(extension)) { let is3D = true try { const read0 = performance.now() const { mesh: itkMesh, webWorker } = await readMeshFile(null, file) const read1 = performance.now() const duration = Number(read1 - read0) .toFixed(1) .toString() const { polyData: itkPolyData } = await meshToPolyData( webWorker, itkMesh ) console.log('Mesh reading took ' + duration + ' milliseconds.') webWorker.terminate() const polyData = vtkITKHelper.convertItkToVtkPolyData(itkPolyData) return { is3D, data: vtk(polyData) } } catch (error) { return readImageFile(null, file) .then(({ image: itkImage, webWorker }) => { webWorker.terminate() is3D = itkImage.imageType.dimension === 3 return Promise.resolve({ is3D, data: itkImage }) }) .catch(error => { return Promise.reject(error) }) } } const { image: itkImage, webWorker } = await readImageFile(null, file) itkImage.name = file.name webWorker.terminate() const is3D = itkImage.imageType.dimension === 3 return { is3D, data: itkImage } }) return await Promise.all(readers) } export const processFiles = async ( container, { files, image, labelImage, fixedImage, config, labelImageNames, rotate, use2D, ...rest } ) => { UserInterface.emptyContainer(container) UserInterface.createLoadingProgress(container) const viewerConfig = await readFiles({ files, image, labelImage, labelImageNames, use2D, }) return createViewer(container, { ...viewerConfig, config, rotate, fixedImage, ...rest, }) } export const readFiles = async ({ files, image, labelImage, labelImageNames, use2D, }) => { let readDICOMSeries = readImageDICOMFileSeries if (files.length < 2 || !image) { readDICOMSeries = function() { return Promise.reject('Skip DICOM series read attempt') } } try { const { image: itkImage, webWorkerPool } = await readDICOMSeries(files) webWorkerPool.terminateWorkers() itkImage.name = files[0].name const is3D = itkImage.imageType.dimension === 3 && !use2D return { image: itkImage, labelImage, use2D: !is3D, } } catch (error) { const dataSets = await readDataFromFiles(files) let imagesFromFiles = dataSets .map(({ data }) => data) .filter(data => !!data && data.imageType !== undefined) // find labelImage const labelImageResult = labelImage ?? imagesFromFiles.find(image => { // Only integer-based pixels considered for label maps const { componentType } = image.imageType if ( componentType !== FloatTypes.Float32 && componentType !== FloatTypes.Float64 ) { // If there are more values than this, it will not be considered a // label map const uniqueLabels = new Set(image.data).size if (uniqueLabels <= MAX_LABELS_IN_LABEL_IMAGE) return image } }) imagesFromFiles = imagesFromFiles.filter( image => image !== labelImageResult ) const labelImageNameData = labelImageNames ? new Map(labelImageNames) : null const geometries = dataSets .filter(({ data }) => { return ( !!data && data.isA !== undefined && data.isA('vtkPolyData') && !!( data.getPolys().getNumberOfValues() || data.getLines().getNumberOfValues() || data.getStrips().getNumberOfValues() ) ) }) .map(({ data }) => data) const pointSets = dataSets .filter(({ data }) => { return ( !!data && data.isA !== undefined && data.isA('vtkPolyData') && !( data.getPolys().getNumberOfValues() || data.getLines().getNumberOfValues() || data.getStrips().getNumberOfValues() ) ) }) .map(({ data }) => data) const outImage = image ?? imagesFromFiles[0] const any3D = [ ...dataSets.map(({ is3D }) => is3D), ...[outImage, labelImageResult].map( image => image?.imageType.dimension === 3 ), ].some(is3D => is3D) return { image: outImage, labelImage: labelImageResult, labelImageNames: labelImageNameData, geometries, pointSets, use2D: use2D || !any3D, } } } ================================================ FILE: src/IO/toMultiscaleSpatialImage.js ================================================ import { readImageArrayBuffer } from 'itk-wasm' import MultiscaleSpatialImage from './MultiscaleSpatialImage' import InMemoryMultiscaleSpatialImage from './InMemoryMultiscaleSpatialImage' import ZarrMultiscaleSpatialImage, { isZarr, } from './ZarrMultiscaleSpatialImage' import ndarrayToItkImage from './ndarrayToItkImage' import fetchBinaryContent from './fetchBinaryContent' async function itkImageToInMemoryMultiscaleSpatialImage(image, isLabelImage) { let chunkSize = [64, 64, 64] if (image.data.length < 2e6) { // Keep a single chunk chunkSize = image.size } else if (image.imageType.dimension === 2) { chunkSize = [256, 256] } const { scaleInfo, imageType, pyramid, } = await InMemoryMultiscaleSpatialImage.buildPyramid( image, chunkSize, isLabelImage ) const multiscaleImage = new InMemoryMultiscaleSpatialImage( pyramid, scaleInfo, imageType, image.name ) return multiscaleImage } async function toMultiscaleSpatialImage( image, isLabelImage = false, maxConcurrency ) { let multiscaleImage = null if (image instanceof MultiscaleSpatialImage) { // Already a multi-scale, chunked image multiscaleImage = image } else if (image.imageType !== undefined) { // itk-wasm Image multiscaleImage = await itkImageToInMemoryMultiscaleSpatialImage( image, isLabelImage ) } else if (typeof image.getItem === 'function') { // key value store multiscaleImage = ZarrMultiscaleSpatialImage.fromStore( image, maxConcurrency ) } else if (image._rtype !== undefined && image._rtype === 'ndarray') { // ndarray const itkImage = ndarrayToItkImage(image) multiscaleImage = await itkImageToInMemoryMultiscaleSpatialImage( itkImage, isLabelImage ) } else if (image.href !== undefined) { if (isZarr(image.href)) { multiscaleImage = ZarrMultiscaleSpatialImage.fromUrl( image, maxConcurrency ) } else { const dataBuffer = await fetchBinaryContent(image) const { image: itkImage, webWorker } = await readImageArrayBuffer( null, dataBuffer, image.pathname.split('/').pop() ) webWorker.terminate() multiscaleImage = await itkImageToInMemoryMultiscaleSpatialImage( itkImage, isLabelImage ) } } else { throw new Error('Unexpected image') } return multiscaleImage } export default toMultiscaleSpatialImage ================================================ FILE: src/IO/uploadFileHandler.js ================================================ import curry from 'curry' import preventDefaults from './UserInterface/preventDefaults' import processFiles from './processFiles' /* Example usage: import uploadIcon from 'itk-vtk-viewer/src/UserInterface/icons/upload.svg'; import getContrastSensitiveStyle from 'itk-vtk-viewer/src/UserInterface/getContrastSensitiveStyle'; const isBackgroundDark = false; const contrastSensitiveStyle = getContrastSensitiveStyle( ['uploadButton'], isBackgroundDark ); const uploadButton = document.createElement('div'); uploadButton.innerHTML = `
${uploadIcon}
`; const fileInput = uploadButton.querySelector('input'); fileInput.addEventListener('change', uploadFileHandler); uploadButton.addEventListener('drop', uploadFileHandler); uploadButton.addEventListener('click', (e) => fileInput.click()); uploadButton.addEventListener('dragover', preventDefaults); mainUIRow.appendChild(uploadButton); */ const uploadFileHandler = curry(async function(container, event) { preventDefaults(event) const dataTransfer = event.dataTransfer const files = event.target.files || dataTransfer.files await processFiles(container, { files }) }) export default uploadFileHandler ================================================ FILE: src/ImJoyPluginAPI.js ================================================ class ImJoyPluginAPI { async setup() { this.viewer = null itkVtkViewer.createViewerFromLocalFiles(container).then(viewer => { this.viewer = viewer }) } async run(ctx) { if (!ctx.data) return let pointSets = null if (ctx.data.pointSets) { pointSets = ctx.data.pointSets if (!Array.isArray(pointSets)) pointSets = [pointSets] pointSets = pointSets.map(points => itkVtkViewer.utils.ndarrayToPointSet(points) ) } if (ctx.config) { this.viewer = await itkVtkViewer.createViewer(container, { image: ctx.data.image, labelImage: ctx.data.labelImage, fixedImage: ctx.data?.fixedImage, compare: ctx.data?.compare, pointSets, geometries: null, rotate: false, config: ctx.config, }) } else { if (pointSets) { await this.setPointSets(ctx.data.pointSets) } } } async setPointSets(pointSets) { if (!Array.isArray(pointSets)) pointSets = [pointSets] pointSets = pointSets.map(points => itkVtkViewer.utils.ndarrayToPointSet(points) ) if (this.viewer === null) { this.viewer = await itkVtkViewer.createViewer(container, { image: null, pointSets, geometries: null, rotate: false, }) } else { await this.viewer.setPointSets(pointSets) } } addPointSet(pointSet) { let points = itkVtkViewer.utils.ndarrayToPointSet(pointSet) this.viewer.addPointSet(points) } async captureImage() { return await this.viewer.captureImage() } async setImage(image, name) { if (this.viewer === null) { this.viewer = await itkVtkViewer.createViewer(container, { image, imageName: name, rotate: false, }) } else { await this.viewer.setImage(image, name) } } getImage(name) { return this.viewer.getImage(name) } async setLabelImage(labelImage) { if (this.viewer === null) { this.viewer = await itkVtkViewer.createViewer(container, { labelImage: labelImage, rotate: false, }) } else { await this.viewer.setLabelImage(labelImage) } } getLabelImage() { return this.viewer.getLabelImage() } registerEventListener(event, callback) { this.viewer.on(event, callback) } getConfig() { return this.viewer.getConfig() } setRenderingViewContainerStyle(containerStyle) { this.viewer.setRenderingViewContainerStyle(containerStyle) } getRenderingViewStyle() { return this.viewer.getRenderingViewContainerStyle() } setBackgroundColor(bgColor) { this.viewer.setBackgroundColor(bgColor) } getBackgroundColor() { return this.viewer.getBackgroundColor() } setUnits(units) { this.viewer.setUnits(units) } getUnits() { return this.viewer.getUnits() } setUICollapsed(collapsed) { this.viewer.setUICollapsed(collapsed) } getUICollapsed() { return this.viewer.getUICollapsed() } setRotateEnabled(enabled) { this.viewer.setRotateEnabled(enabled) } getRotateEnabled() { return this.viewer.getRotateEnabled() } setAnnotationsEnabled(enabled) { this.viewer.setAnnotationsEnabled(enabled) } getAnnotationsEnabled() { return this.viewer.getAnnotationsEnabled() } setAxesEnabled(enabled) { this.viewer.setAxesEnabled(enabled) } getAxesEnabled() { return this.viewer.getAxesEnabled() } setXSlice(position) { this.viewer.setXSlice(position) } getXSlice() { return this.viewer.getXSlice() } setYSlice(position) { this.viewer.setYSlice(position) } getYSlice() { return this.viewer.getYSlice() } setZSlice(position) { this.viewer.setZSlice(position) } getZSlice() { return this.viewer.getZSlice() } setViewMode(mode) { this.viewer.setViewMode(mode) } getViewMode() { return this.viewer.getViewMode() } getLayerNames() { return this.viewer.getLayerNames() } setLayerVisibility(visible, name) { this.viewer.setLayerVisibility(visible, name) } getLayerVisibility(name) { return this.viewer.getLayerVisibility(name) } selectLayer(name) { this.viewer.selectLayer(name) } setImageComponentVisibility(visibility, component, name) { this.viewer.setImageComponentVisibility(visibility, component, name) } getImageComponentVisibility(component, name) { return this.viewer.getImageComponentVisibility(component, name) } setImageInterpolationEnabled(enabled) { this.viewer.setImageInterpolationEnabled(enabled) } getImageInterpolationEnabled() { return this.viewer.getImageInterpolationEnabled() } setImageColorRange(range, component, name) { this.viewer.setImageColorRange(range, component, name) } getImageColorRange(component, name) { return this.viewer.getImageColorRange(component, name) } setImageColorRangeMin(min, component, name) { this.viewer.setImageColorRangeMin(min, component, name) } setImageColorRangeMax(max, component, name) { this.viewer.setImageColorRangeMax(max, component, name) } setImageColorRangeBounds(bounds, component, name) { this.viewer.setImageColorRangeBounds(bounds, component, name) } getImageColorRangeBounds(component, name) { return this.viewer.getImageColorRangeBounds(component, name) } setImageColorMap(colorMap, component, name) { this.viewer.setImageColorMap(colorMap, component, name) } getImageColorMap(component, name) { return this.viewer.getImageColorMap(component, name) } setImagePiecewiseFunctionPoints(points, component, name) { this.viewer.setImagePiecewiseFunctionPoints(points, component, name) } getImagePiecewiseFunctionPoints(component, name) { return this.viewer.getImagePiecewiseFunctionPoints(component, name) } setImageShadowEnabled(shadow, name) { this.viewer.setImageShadowEnabled(shadow, name) } getImageShadowEnabled(name) { return this.viewer.getImageShadowEnabled(name) } setImageGradientOpacity(opacity, name) { this.viewer.setImageGradientOpacity(opacity, name) } getImageGradientOpacity(name) { return this.viewer.getImageGradientOpacity(name) } setImageGradientOpacityScale(scale, name) { this.viewer.setImageGradientOpacityScale(scale, name) } getImageGradientOpacityScale(name) { return this.viewer.getImageGradientOpacityScale(name) } setImageVolumeSampleDistance(distance, name) { this.viewer.setImageVolumeSampleDistance(distance, name) } getImageVolumeSampleDistance(name) { return this.viewer.getImageVolumeSampleDistance(name) } setImageBlendMode(mode, name) { this.viewer.setImageBlendMode(mode, name) } getImageBlendMode(name) { return this.viewer.getImageBlendMode(name) } setLabelImageLookupTable(lookupTable, name) { this.viewer.setLabelImageLookupTable(lookupTable, name) } getLabelImageLookupTable(name) { return this.viewer.getLabelImageLookupTable(name) } setLabelImageBlend(blend, name) { this.viewer.setLabelImageBlend(blend, name) } getLabelImageBlend(name) { return this.viewer.getLabelImageBlend(name) } setLabelImageLabelNames(labelNames, name) { this.viewer.setLabelImageLabelNames(labelNames, name) } getLabelImageLabelNames(name) { return this.viewer.getLabelImageLabelNames(name) } setLabelImageWeights(weights, name) { this.viewer.setLabelImageWeights(weights, name) } getLabelImageWeights(name) { return this.viewer.getLabelImageWeights(name) } setCroppingPlanesEnabled(enabled) { this.viewer.setCroppingPlanesEnabled(enabled) } getCroppingPlanesEnabled() { return this.viewer.getCroppingPlanesEnabled() } setCroppingPlanes(croppingPlanes) { this.viewer.setCroppingPlanes(croppingPlanes) } getCroppingPlanes() { return this.viewer.getCroppingPlanes() } setImageVolumeScatteringBlend(scatteringBlend, name) { this.viewer.setImageVolumeScatteringBlend(scatteringBlend, name) } getImageVolumeScatteringBlend(name) { return this.viewer.getImageVolumeScatteringBlend(name) } setRpcMaxConcurrency(value) { this.viewer.setMaxConcurrency(value) } getRpcMaxConcurrency() { return this.viewer.getMaxConcurrency() } compareImages(fixedImageName, movingImageName, options) { return this.viewer.setCompareImages( fixedImageName, movingImageName, options ) } getCompareImages(name) { return this.viewer.getCompareImages(name) } getLoadedScale(scale) { return this.viewer.getLoadedScale(scale) } getCroppedImageWorldBounds() { return this.viewer.getCroppedImageWorldBounds() } async getCroppedIndexBounds(scale) { return await this.viewer.getCroppedIndexBounds(scale) } } export default ImJoyPluginAPI ================================================ FILE: src/Rendering/Images/createImageRenderingActor.js ================================================ import { assign, createMachine, forwardTo, send } from 'xstate' import { defaultCompare } from '../../Context/ImageActorContext' import { makeTransitions } from './makeTransitions' export const getOutputIntensityComponentCount = actorContext => { const { image, compare: { method }, } = actorContext if (method && method !== 'disabled') return 2 return image.imageType.components } const getLoadedImage = actorContext => actorContext.image ?? actorContext.labelImage const assignColorRange = assign({ images: ( { images }, { data: { name, component, range, keepAutoAdjusting = false } } ) => { const { colorRanges, colorRangeMinAutoAdjust, colorRangeMaxAutoAdjust, } = images.actorContext.get(name) colorRanges.set(component, range) colorRangeMinAutoAdjust.set( component, colorRangeMinAutoAdjust.get(component) && keepAutoAdjusting ) colorRangeMaxAutoAdjust.set( component, colorRangeMaxAutoAdjust.get(component) && keepAutoAdjusting ) return images }, }) const assignColorRangeMin = assign({ images: ({ images }, { data: { name, component } }) => { const { colorRangeMinAutoAdjust } = images.actorContext.get(name) colorRangeMinAutoAdjust.set(component, false) return images }, }) const updateColorRangeMin = (context, e) => { const { data: { name, component, value }, } = e const { colorRanges } = context.images.actorContext.get(name) const [, max] = colorRanges.get(component) context.service.send({ type: 'IMAGE_COLOR_RANGE_CHANGED', data: { name, component, range: [value, max], keepAutoAdjusting: true, // let max change }, }) } const assignColorRangeMax = assign({ images: ({ images }, { data: { name, component } }) => { const { colorRangeMaxAutoAdjust } = images.actorContext.get(name) colorRangeMaxAutoAdjust.set(component, false) return images }, }) const updateColorRangeMax = (context, e) => { const { data: { name, component, value }, } = e const { colorRanges } = context.images.actorContext.get(name) const [min] = colorRanges.get(component) context.service.send({ type: 'IMAGE_COLOR_RANGE_CHANGED', data: { name, component, range: [min, value], keepAutoAdjusting: true, // let min change }, }) } const assignUpdateRenderedName = assign({ images: (context, event) => { const images = context.images images.updateRenderedName = event.data.name return images }, }) const assignUpdateRenderedNameToSelectedName = assign({ images: context => { const images = context.images images.updateRenderedName = images.selectedName return images }, }) const assignFinerScale = assign({ targetScale: ({ targetScale }) => { return Math.max(0, targetScale - 1) }, }) const assignCoarserScale = assign({ targetScale: ({ images, targetScale }) => { const actorContext = images.actorContext.get(images.updateRenderedName) if (targetScale < getLoadedImage(actorContext).coarsestScale) { return targetScale + 1 } return targetScale }, hasScaledCoarser: true, }) const assignLoadedScale = assign({ images: ({ images }, { data }) => { const name = data.name ?? data const actorContext = images.actorContext.get(name) actorContext.loadedScale = data.loadedScale return images }, }) const assignClearHistograms = assign({ images: ({ images }, { data }) => { const name = data.name ?? data const actorContext = images.actorContext.get(name) actorContext.histograms = new Map() return images }, }) const LOW_FPS = 10.0 const HIGH_FPS = 30.0 // Return true if finest scale or already backed off coarser or FPS is in Goldilocks zone function finestScaleOrScaleJustRight(context) { const { loadedScale } = context.images.actorContext.get( context.images.updateRenderedName ) return ( loadedScale === 0 || context.hasScaledCoarser || (LOW_FPS < context.main.fps && context.main.fps < HIGH_FPS) ) } function isFpsLow(context) { return context.main.fps <= LOW_FPS } function isLoadedScaleMostCoarse(context) { const actorContext = context.images.actorContext.get( context.images.updateRenderedName ) return getLoadedImage(actorContext).coarsestScale === actorContext.loadedScale } const assignIsFramerateScalePickingOn = assign({ images: ({ images }, { type }) => { const actorContext = images.actorContext.get(images.updateRenderedName) actorContext.isFramerateScalePickingOn = type !== 'SET_IMAGE_SCALE' return images }, }) const assignCinematic = assign({ images: ({ images }, { data: { params, name } }) => { const actorContext = images.actorContext.get(name) actorContext.cinematicParameters = { ...actorContext.cinematicParameters, ...params, } return images }, }) const sendCinematicChanged = context => { const actorContext = context.images.actorContext.get( context.images.selectedName ) context.service.send({ type: 'CINEMATIC_CHANGED', actorContext, }) } const computeIsCinematicPossible = (context, { data: { itkImage, name } }) => { const isCinematicPossible = itkImage.imageType.components === 1 context.service.send({ type: 'SET_CINEMATIC_PARAMETERS', data: { name, params: { isCinematicPossible }, }, }) } // force rebuilding image const forceUpdate = c => c.service.send({ type: 'UPDATE_RENDERED_IMAGE', data: { name: c.actorName }, }) const sendCompareUpdated = (c, { data: { name } }) => { c.service.send({ type: 'COMPARE_UPDATED', data: { name }, }) } const updateCompare = ( { service, use2D, images: { actorContext: actorMap } }, name ) => { const actorContext = actorMap.get(name) const { method, imageMix } = actorContext.compare const { method: lastMethod, imageMix: lastImageMix } = actorContext.lastCompare ?? {} if (lastMethod !== method) { let colorMaps if (method === 'green-magenta') { colorMaps = ['BkGn', 'BkMa'] } if (method === 'cyan-magenta') { colorMaps = ['BkCy', 'BkMa'] } if (method === 'cyan-red') { colorMaps = ['BkCy', 'BkRd'] } if (colorMaps) { // besides setting the color maps, event adds another entry in colormap Map for the possibly new second component const [firstColorMap, secondColorMap] = colorMaps service.send({ type: 'IMAGE_COLOR_MAP_CHANGED', data: { name, component: 0, colorMap: firstColorMap }, }) service.send({ type: 'IMAGE_COLOR_MAP_CHANGED', data: { name, component: 1, colorMap: secondColorMap }, }) } if (method === 'blend' || method === 'checkerboard') { const colorForSecondComp = actorContext.colorMaps.get(0) service.send({ type: 'IMAGE_COLOR_MAP_CHANGED', data: { name, component: 1, colorMap: colorForSecondComp }, }) } } if (method !== 'disabled' && imageMix !== lastImageMix) { const mix0 = 1 - imageMix const mix1 = imageMix for (let component = 0; component < 2; component++) { const startPoints = actorContext.piecewiseFunctionPoints.get(component) const firstX = (startPoints && startPoints[0][0]) ?? 0 const lastX = (startPoints && startPoints[startPoints.length - 1][0]) ?? 1 const onePoint = startPoints && startPoints.length === 1 const mix = component ? mix1 : mix0 const points = use2D ? [...(onePoint ? [] : [[firstX, mix]]), [lastX, mix]] : [...(onePoint ? [] : [[firstX, 0]]), [lastX, mix]] service.send({ type: 'IMAGE_PIECEWISE_FUNCTION_POINTS_CHANGED', data: { name, component, points }, }) // clamp points to color range to respect window and level const range = actorContext.colorRanges.get(component) ?? actorContext.colorRanges.get(0) if (range) { service.send({ type: 'IMAGE_COLOR_RANGE_CHANGED', data: { name, component, range }, }) } } } } const computeCheckerboard = (currentCompare, lastCompare) => { if ( lastCompare.method !== 'checkerboard' && currentCompare.method === 'checkerboard' ) return true if ( lastCompare.method === 'checkerboard' && currentCompare.method !== 'checkerboard' ) return false return currentCompare.checkerboard } const computeImageMix = (currentCompare, lastCompare) => { if (currentCompare.method !== lastCompare.method) { if (currentCompare.checkerboard) return currentCompare.swapImageOrder ? 1 : 0 return 0.5 } if (currentCompare.swapImageOrder !== lastCompare.swapImageOrder) return currentCompare.swapImageOrder ? 1 : 0 return currentCompare.imageMix } const assignCompare = assign({ images: (context, { data: { name, fixedImageName, options } }) => { const actorContext = context.images.actorContext.get(name) actorContext.lastCompare = { ...actorContext.compare } // for diffing later by updateCompare, UI, etc. const updatedCompare = { ...defaultCompare, ...actorContext.compare, ...options, } const computedCheckerboard = { ...updatedCompare, checkerboard: computeCheckerboard( updatedCompare, actorContext.lastCompare ), // after computed values to let explicit set of values to take precedence ...options, } const computedImageMix = { ...computedCheckerboard, imageMix: computeImageMix(computedCheckerboard, actorContext.lastCompare), } actorContext.compare = { fixedImageName, ...computedImageMix, // after computed values to let explicit set of values to take precedence ...options, } updateCompare(context, name) return context.images }, }) const dirtyColorRanges = c => { const actorContext = c.images.actorContext.get(c.actorName) actorContext.dirtyColorRanges = true } const cleanColorRanges = (c, { data: { name } }) => { const actorContext = c.images.actorContext.get(name) if (actorContext.dirtyColorRanges) { // let applyRenderedImage update colorRanges and colorRangeBounds actorContext.colorRanges = new Map() const componentCount = getOutputIntensityComponentCount(actorContext) actorContext.colorRangeBoundsAutoAdjust = new Map( [...Array(componentCount).keys()].map(c => [c, true]) ) actorContext.colorRangeMinAutoAdjust = new Map( [...Array(componentCount).keys()].map(c => [c, true]) ) actorContext.colorRangeMaxAutoAdjust = new Map( [...Array(componentCount).keys()].map(c => [c, true]) ) actorContext.piecewiseFunctionPointsAutoAdjust = new Map( [...Array(componentCount).keys()].map(c => [c, true]) ) actorContext.dirtyColorRanges = false } } const afterCompareMaybeForceUpdate = context => { const { actorName, images: { actorContext: actorMap }, } = context const actorContext = actorMap.get(actorName) // eslint-disable-next-line no-unused-vars const { imageMix, swapImageOrder, ...compare } = actorContext.compare // eslint-disable-next-line no-unused-vars const { imageMix: _, swapImageOrder: __, ...lastCompare } = actorContext.lastCompare ?? {} if (JSON.stringify(compare) === JSON.stringify(lastCompare)) return dirtyColorRanges(context) forceUpdate(context) } const applyAnimateImageMix = ( { images: { actorContext: actorMap }, itkVtkView, service }, { data: { play, name } } ) => { const actorContext = actorMap.get(name) if (play) { if (!actorContext.imageMixAnimationDirection) actorContext.imageMixAnimationDirection = 1 itkVtkView.getInteractor().requestAnimation('animateImageMix') actorContext.imageMixAnimation = itkVtkView .getInteractor() .onAnimation(() => { const { imageMix, fixedImageName } = actorContext.compare let newMix = imageMix + 0.02 * actorContext.imageMixAnimationDirection if (newMix > 1) { newMix = 1 actorContext.imageMixAnimationDirection *= -1 } if (newMix < 0) { newMix = 0 actorContext.imageMixAnimationDirection *= -1 } service.send({ type: 'COMPARE_IMAGES', data: { name, fixedImageName: fixedImageName, options: { imageMix: newMix }, }, }) }) } else if (actorContext.imageMixAnimation) { actorContext.imageMixAnimation.unsubscribe() actorContext.imageMixAnimation = null itkVtkView.getInteractor().cancelAnimation('animateImageMix') } } const KNOWN_ERRORS = [ 'Voxel count over max at scale', 'Image byte count over max at scale', "Failed to execute 'postMessage' on 'Worker': Data cannot be cloned, out of memory.", // Chrome "Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope': Data cannot be cloned, out of memory.", // Firefox 'Array buffer allocation failed', 'Aborted(). Build with -sASSERTIONS for more info', ] const checkIsKnownErrorOrThrow = (c, { data: error }) => { if ( KNOWN_ERRORS.some(knownMessage => error.message?.startsWith(knownMessage)) ) { console.warn('Could not update image', error.stack) } else { throw error } } const sendRenderedImageAssigned = ( context, { data: { name, loadedScale } } ) => { context.service.send({ type: 'RENDERED_IMAGE_ASSIGNED', data: name, loadedScale, }) } const sendStartDataUpdate = context => { context.service.send({ type: 'START_DATA_UPDATE', name: context.actorName, }) } const sendFinishDataUpdate = context => { context.service.send({ type: 'FINISH_DATA_UPDATE', name: context.actorName, }) } const assignHigherErrorCountIfMostCoarse = assign({ errorCountAtScale: ({ errorCountAtScale, images: { actorContext: actorMap }, actorName, targetScale, }) => { const actorContext = actorMap.get(actorName) if (getLoadedImage(actorContext).coarsestScale !== targetScale) return errorCountAtScale return errorCountAtScale + 1 }, }) const assignResetErrorCount = assign({ errorCountAtScale: 0, }) const eventResponses = { IMAGE_ASSIGNED: { target: 'updatingImage', actions: [assignUpdateRenderedNameToSelectedName, assignLoadedScale], }, LABEL_IMAGE_ASSIGNED: { target: 'updatingImage', actions: assignUpdateRenderedNameToSelectedName, }, UPDATE_RENDERED_IMAGE: { target: 'updatingImage', actions: assignUpdateRenderedName, }, TOGGLE_LAYER_VISIBILITY: { actions: 'toggleLayerVisibility', }, SET_GRADIENT_OPACITY: { actions: 'applyGradientOpacity', }, TOGGLE_IMAGE_INTERPOLATION: { actions: 'toggleInterpolation', }, IMAGE_COMPONENT_VISIBILITY_CHANGED: { actions: 'applyComponentVisibility', }, IMAGE_PIECEWISE_FUNCTION_CHANGED: { actions: 'applyPiecewiseFunction', }, IMAGE_PIECEWISE_FUNCTION_POINTS_CHANGED: { actions: 'mapToPiecewiseFunctionNodes', }, IMAGE_COLOR_RANGE_CHANGED: { actions: [assignColorRange, 'applyColorRange'], }, IMAGE_COLOR_RANGE_MIN_CHANGED: { actions: [assignColorRangeMin, updateColorRangeMin], }, IMAGE_COLOR_RANGE_MAX_CHANGED: { actions: [assignColorRangeMax, updateColorRangeMax], }, IMAGE_COLOR_RANGE_POINTS_CHANGED: { actions: 'mapToColorFunctionRange', }, IMAGE_COLOR_RANGE_BOUNDS_CHANGED: { actions: ['applyColorRangeBounds'], }, IMAGE_COLOR_MAP_CHANGED: { actions: 'applyColorMap', }, TOGGLE_IMAGE_SHADOW: { actions: 'applyShadow', }, IMAGE_GRADIENT_OPACITY_CHANGED: { actions: 'applyGradientOpacity', }, IMAGE_GRADIENT_OPACITY_SCALE_CHANGED: { actions: 'applyGradientOpacity', }, IMAGE_VOLUME_SAMPLE_DISTANCE_CHANGED: { actions: 'applyVolumeSampleDistance', }, IMAGE_BLEND_MODE_CHANGED: { actions: 'applyBlendMode', }, UPDATE_IMAGE_HISTOGRAM: { target: 'updatingHistogram', }, LABEL_IMAGE_LOOKUP_TABLE_CHANGED: { actions: 'applyLookupTable', }, LABEL_IMAGE_BLEND_CHANGED: { actions: 'applyLabelImageBlend', }, LABEL_IMAGE_WEIGHTS_CHANGED: { actions: 'applyLabelImageWeights', }, LABEL_IMAGE_LABEL_NAMES_CHANGED: { actions: 'applyLabelNames', }, LABEL_IMAGE_SELECTED_LABEL_CHANGED: { actions: 'applySelectedLabel', }, SET_IMAGE_SCALE: { target: 'updatingImage', actions: assignIsFramerateScalePickingOn, }, ADJUST_SCALE_FOR_FRAMERATE: { target: 'updatingImage', actions: assignIsFramerateScalePickingOn, }, RENDERED_BOUNDS_CHANGED: { target: 'updatingImage', }, SET_CINEMATIC_PARAMETERS: { actions: [assignCinematic, sendCinematicChanged], }, CINEMATIC_CHANGED: { actions: 'applyCinematicChanged', }, COMPARE_IMAGES: { actions: [assignCompare, sendCompareUpdated, afterCompareMaybeForceUpdate], }, ANIMATE_IMAGE_MIX: { actions: applyAnimateImageMix, }, TOGGLE_LAYER_BBOX: { actions: 'toggleLayerBBox', }, } const CHANGE_BOUNDS_EVENTS = [ 'CROPPING_PLANES_CHANGED_BY_USER', 'CAMERA_MODIFIED', ] const createUpdatingImageMachine = options => { return createMachine( { id: 'updatingImageMachine', predictableActionArguments: true, initial: 'checkingUpdateNeeded', states: { checkingUpdateNeeded: { always: [ { cond: 'isImageUpdateNeeded', target: 'preLoadingImage' }, { target: '#updatingImageMachine.loadedImage' }, ], exit: assign({ isUpdateForced: false }), }, preLoadingImage: { entry: sendStartDataUpdate, invoke: { id: 'preLoadingImage', src: async () => { // Give spinner chance to start. Waiting 2 frames works better in cached image case =| await new Promise(requestAnimationFrame) await new Promise(requestAnimationFrame) }, onDone: { target: 'loadingImage', }, }, }, loadingImage: { invoke: { id: 'updateRenderedImage', src: 'updateRenderedImage', onDone: { target: '#updatingImageMachine.loadedImage', actions: [ 'assignRenderedImage', assignLoadedScale, assignClearHistograms, cleanColorRanges, 'applyRenderedImage', sendRenderedImageAssigned, computeIsCinematicPossible, assignResetErrorCount, ], }, onError: { actions: [ checkIsKnownErrorOrThrow, assignHigherErrorCountIfMostCoarse, assignCoarserScale, ], target: 'afterError', }, }, }, afterError: { entry: sendFinishDataUpdate, always: [ { cond: c => c.errorCountAtScale >= 2, actions: () => console.warn('Too many errors building image. Giving up.'), target: 'finished', }, { target: 'checkingUpdateNeeded' }, ], }, loadedImage: { entry: sendFinishDataUpdate, always: [ { cond: 'isFramerateScalePickingOn', target: 'checkingFramerate', }, { target: 'finished', }, ], }, checkingFramerate: { entry: [c => c.service.send('UPDATE_FPS')], on: { FPS_UPDATED: [ { cond: c => [isFpsLow, isLoadedScaleMostCoarse].every(cond => cond(c)), target: 'finished', // FPS too slow but nothing to do about it }, { cond: isFpsLow, actions: assignCoarserScale, // back off target: 'checkingUpdateNeeded', }, { cond: finestScaleOrScaleJustRight, target: 'finished', // found good scale }, { actions: assignFinerScale, // try harder target: 'checkingUpdateNeeded', }, ], }, }, finished: { type: 'final', }, }, }, options ) } const createImageRenderingActor = (options, context, name) => { const machineContext = { ...context, actorName: name } return createMachine( { id: 'imageRendering', predictableActionArguments: true, context: machineContext, type: 'parallel', states: { imageLoader: { initial: 'idle', states: { idle: { on: { COMPARE_IMAGES: { actions: [assignCompare, sendCompareUpdated], }, }, invoke: { id: 'createImageRenderer', src: 'createImageRenderer', onDone: { target: 'updatingImage', actions: assignUpdateRenderedNameToSelectedName, }, }, }, updatingImage: { on: { ...eventResponses, FPS_UPDATED: { actions: forwardTo('updatingImageMachine'), }, UPDATE_IMAGE_HISTOGRAM: {}, RENDERED_BOUNDS_CHANGED: {}, }, entry: 'assignVisualizedComponents', invoke: { id: 'updatingImageMachine', src: createUpdatingImageMachine(options), data: { ...machineContext, hasScaledCoarser: false, errorCountAtScale: 0, targetScale: ({ images }, event) => { if (event.type === 'SET_IMAGE_SCALE') return event.targetScale const actorContext = images.actorContext.get( images.updateRenderedName ) if ( actorContext.loadedScale || actorContext.loadedScale === 0 ) return actorContext.loadedScale // nothing loaded, start at coarsest return getLoadedImage(actorContext).coarsestScale }, isUpdateForced: (c, event) => [ 'UPDATE_RENDERED_IMAGE', 'IMAGE_ASSIGNED', 'LABEL_IMAGE_ASSIGNED', ].some(forcedEvent => event.type === forcedEvent), }, onDone: [ { in: '#imageRendering.imageLoader.updatingImage.noUpdateNeeded', target: 'updatingHistogram', }, { target: 'updatingImage' }, ], }, initial: 'noUpdateNeeded', states: { noUpdateNeeded: { on: { RENDERED_BOUNDS_CHANGED: 'loopback' } }, loopback: {}, }, }, updatingHistogram: { invoke: { id: 'updateHistogram', src: 'updateHistogram', onDone: { target: 'active', }, }, on: { ...eventResponses, }, }, active: { entry: context => context.service.send({ type: 'IMAGE_RENDERING_ACTIVE', data: { name: context.images.updateRenderedName }, }), on: { ...eventResponses, }, }, }, }, debouncedImageUpdate: { after: { 500: { actions: send('RENDERED_BOUNDS_CHANGED'), }, }, on: makeTransitions(CHANGE_BOUNDS_EVENTS, 'debouncedImageUpdate'), }, }, }, options ) } export default createImageRenderingActor ================================================ FILE: src/Rendering/Images/createImagesRenderingMachine.js ================================================ import { Machine, assign, spawn, send, actions } from 'xstate' import { makeTransitions } from './makeTransitions' import createImageRenderingActor from './createImageRenderingActor' function spawnImageRenderingActor(options) { return assign({ images: (context, event) => { const images = context.images const name = event.data if (!images.imageRenderingActors.has(name)) { images.imageRenderingActors.set( name, spawn( createImageRenderingActor(options, context, name), `imageRenderingActor-${name}` ) ) } else { const actor = images.imageRenderingActors.get(name) actor.send(event) } return images }, }) } const forwardToNamedActor = send((_, e) => e, { to: (c, e) => `imageRenderingActor-${e.name ?? e.data.name}`, }) const sendEventToAllActors = actions.pure( ({ images: { imageRenderingActors } }, event) => Array.from(imageRenderingActors.values()).map(actor => send(event, { to: actor, }) ) ) function createImagesRenderingMachine(options, context) { const { imageRenderingActor } = options return Machine( { id: 'imagesRendering', initial: 'idle', context, states: { idle: { on: { IMAGE_ASSIGNED: { target: 'active', actions: [spawnImageRenderingActor(imageRenderingActor)], }, LABEL_IMAGE_ASSIGNED: { target: 'active', actions: [spawnImageRenderingActor(imageRenderingActor)], }, }, }, active: { invoke: { id: 'cameraModifiedWatcher', src: context => send => context.itkVtkView .getRenderer() .getActiveCamera() .onModified(() => send('CAMERA_MODIFIED')).unsubscribe, // return cleanup func }, on: { IMAGE_ASSIGNED: { actions: spawnImageRenderingActor(imageRenderingActor), }, LABEL_IMAGE_ASSIGNED: { actions: spawnImageRenderingActor(imageRenderingActor), }, SELECT_LAYER: { actions: 'selectImageLayer', }, UPDATE_RENDERED_IMAGE: { actions: send((_, e) => e, { to: (c, e) => `imageRenderingActor-${e.data.name}`, }), }, FPS_UPDATED: { actions: send((_, e) => e, { to: c => `imageRenderingActor-${c.images.updateRenderedName}`, }), }, SET_IMAGE_SCALE: { actions: send((_, e) => e, { to: c => `imageRenderingActor-${c.images.updateRenderedName}`, }), }, SET_CINEMATIC_PARAMETERS: { actions: send((_, e) => e, { to: c => `imageRenderingActor-${c.images.selectedName}`, }), }, CINEMATIC_CHANGED: { actions: send((_, e) => e, { to: c => `imageRenderingActor-${c.images.selectedName}`, }), }, TOGGLE_LAYER_VISIBILITY: { actions: send((_, e) => e, { to: c => `imageRenderingActor-${c.images.selectedName}`, }), }, TOGGLE_IMAGE_INTERPOLATION: { actions: send((_, e) => e, { to: (c, e) => `imageRenderingActor-${e.data}`, }), }, IMAGE_COMPONENT_VISIBILITY_CHANGED: { actions: send((_, e) => e, { to: (c, e) => `imageRenderingActor-${e.data.name}`, }), }, IMAGE_PIECEWISE_FUNCTION_CHANGED: { actions: send((_, e) => e, { to: (c, e) => `imageRenderingActor-${e.data.name}`, }), }, IMAGE_PIECEWISE_FUNCTION_POINTS_CHANGED: { actions: send((_, e) => e, { to: (c, e) => `imageRenderingActor-${e.data.name}`, }), }, IMAGE_COLOR_RANGE_BOUNDS_CHANGED: { actions: send((_, e) => e, { to: (c, e) => `imageRenderingActor-${e.data.name}`, }), }, IMAGE_COLOR_RANGE_CHANGED: { actions: send((_, e) => e, { to: (c, e) => `imageRenderingActor-${e.data.name}`, }), }, IMAGE_COLOR_RANGE_POINTS_CHANGED: { actions: send((_, e) => e, { to: (c, e) => `imageRenderingActor-${e.data.name}`, }), }, IMAGE_COLOR_MAP_CHANGED: { actions: send((_, e) => e, { to: (c, e) => `imageRenderingActor-${e.data.name}`, }), }, TOGGLE_IMAGE_SHADOW: { actions: send((_, e) => e, { to: (c, e) => `imageRenderingActor-${e.data}`, }), }, DOWNLOAD_IMAGE: { actions: ['downloadImage'], }, ...makeTransitions( [ 'IMAGE_GRADIENT_OPACITY_CHANGED', 'IMAGE_GRADIENT_OPACITY_SCALE_CHANGED', 'IMAGE_VOLUME_SAMPLE_DISTANCE_CHANGED', 'IMAGE_BLEND_MODE_CHANGED', 'UPDATE_IMAGE_HISTOGRAM', 'LABEL_IMAGE_LOOKUP_TABLE_CHANGED', 'LABEL_IMAGE_BLEND_CHANGED', 'LABEL_IMAGE_WEIGHTS_CHANGED', 'LABEL_IMAGE_SELECTED_LABEL_CHANGED', 'LABEL_IMAGE_LABEL_NAMES_CHANGED', 'COMPARE_IMAGES', 'WINDOW_LEVEL_TOGGLED', 'IMAGE_COLOR_RANGE_RESET', 'ANIMATE_IMAGE_MIX', 'TOGGLE_LAYER_BBOX', 'IMAGE_COLOR_RANGE_MIN_CHANGED', 'IMAGE_COLOR_RANGE_MAX_CHANGED', ], { actions: forwardToNamedActor } ), ...makeTransitions( ['CROPPING_PLANES_CHANGED_BY_USER', 'CAMERA_MODIFIED'], { actions: sendEventToAllActors } ), }, }, }, }, options ) } export default createImagesRenderingMachine ================================================ FILE: src/Rendering/Images/makeTransitions.js ================================================ export const makeTransitions = (events, transition) => events.reduce( (onEvents, e) => ({ ...onEvents, [e]: transition, }), {} ) ================================================ FILE: src/Rendering/Layers/createLayersRenderingMachine.js ================================================ import { Machine } from 'xstate' function createLayersRenderingMachine(options, context) { return Machine( { id: 'layersRendering', initial: 'idle', context, states: { idle: { always: { target: 'active', }, }, active: {}, }, }, options ) } export default createLayersRenderingMachine ================================================ FILE: src/Rendering/Main/backgroundIsDark.js ================================================ function backgroundIsDark(context) { const backgroundColor = context.main.backgroundColor const isDark = backgroundColor[0] + backgroundColor[1] + backgroundColor[2] < 1.5 return isDark } export default backgroundIsDark ================================================ FILE: src/Rendering/Main/backgroundIsLight.js ================================================ function backgroundIsLight(context, event) { const backgroundColor = context.main.backgroundColor const isLight = backgroundColor[0] + backgroundColor[1] + backgroundColor[2] >= 1.5 return isLight } export default backgroundIsLight ================================================ FILE: src/Rendering/Main/createMainRenderingMachine.js ================================================ import { Machine, assign, sendParent, send } from 'xstate' import backgroundIsDark from './backgroundIsDark' import backgroundIsLight from './backgroundIsLight' const assignFps = assign({ main: (context, event) => { const main = context.main main.fps = event.data return main }, }) const eventToAxis = { X_SLICE_CHANGED: 'x', Y_SLICE_CHANGED: 'y', Z_SLICE_CHANGED: 'z', } const assignSlicePosition = assign({ main: ({ main }, { type, data: position }) => { main.slicingPlanes[eventToAxis[type]].position = position return main }, }) function createMainRenderingMachine(options, context) { let initialViewMode = 'volume' switch (context.main.viewMode) { case 'XPlane': initialViewMode = 'xPlane' break case 'YPlane': initialViewMode = 'yPlane' break case 'ZPlane': initialViewMode = 'zPlane' break case 'Volume': initialViewMode = 'volume' break default: throw new Error(`Invalid initial view mode: ${context.main.viewMode}`) } return Machine( { id: 'main', initial: 'idle', context, states: { idle: { always: { target: 'active', }, }, active: { entry: ['setBackgroundColor', 'setUnits'], type: 'parallel', on: { TAKE_SCREENSHOT: { actions: 'takeScreenshot', }, UPDATE_FPS: { actions: 'updateFps', }, FPS_UPDATED: { actions: assignFps, }, SET_BACKGROUND_COLOR: { actions: [ 'setBackgroundColor', send('CHECK_BACKGROUND_CONTRAST'), ], }, TOGGLE_BACKGROUND_COLOR: { actions: [ 'setBackgroundColor', send('CHECK_BACKGROUND_CONTRAST'), ], }, SET_UNITS: { actions: 'setUnits', }, RESET_CAMERA: { actions: 'resetCamera', }, RESET_CROPPING_PLANES: { actions: 'resetCroppingPlanes', }, CROPPING_PLANES_CHANGED: { actions: ['applyCroppingPlanes', 'updateSlicingPlanes'], }, SLICING_PLANES_CHANGED: { actions: 'applySlicingPlanes', }, X_SLICE_CHANGED: { actions: [assignSlicePosition, 'applyXSlice'], }, Y_SLICE_CHANGED: { actions: [assignSlicePosition, 'applyYSlice'], }, Z_SLICE_CHANGED: { actions: [assignSlicePosition, 'applyZSlice'], }, }, states: { background: { initial: backgroundIsLight(context) ? 'light' : 'dark', states: { dark: { entry: [ context => (context.uiDarkMode = true), sendParent('BACKGROUND_TURNED_DARK'), ], on: { CHECK_BACKGROUND_CONTRAST: { target: ['light'], cond: backgroundIsLight, }, }, }, light: { entry: [ context => (context.uiDarkMode = false), sendParent('BACKGROUND_TURNED_LIGHT'), ], on: { CHECK_BACKGROUND_CONTRAST: { target: ['dark'], cond: backgroundIsDark, }, }, }, }, }, annotations: { initial: context.main.annotationsEnabled ? 'enabled' : 'disabled', states: { enabled: { entry: 'toggleAnnotations', on: { TOGGLE_ANNOTATIONS: 'disabled', }, }, disabled: { entry: 'toggleAnnotations', on: { TOGGLE_ANNOTATIONS: 'enabled', }, }, }, }, rotate: { initial: context.main.rotateEnabled ? 'enabled' : 'disabled', states: { enabled: { entry: 'toggleRotate', on: { TOGGLE_ROTATE: 'disabled', }, }, disabled: { entry: 'toggleRotate', on: { TOGGLE_ROTATE: 'enabled', }, }, }, }, axes: { initial: context.main.axesEnabled ? 'enabled' : 'disabled', states: { enabled: { entry: 'toggleAxes', on: { TOGGLE_AXES: 'disabled', }, }, disabled: { entry: 'toggleAxes', on: { TOGGLE_AXES: 'enabled', }, }, }, }, croppingPlanes: { initial: context.main.croppingPlanesEnabled ? 'enabled' : 'disabled', states: { enabled: { entry: 'toggleCroppingPlanes', on: { TOGGLE_CROPPING_PLANES: 'disabled', }, }, disabled: { entry: 'toggleCroppingPlanes', on: { TOGGLE_CROPPING_PLANES: 'enabled', }, }, }, }, viewMode: { initial: initialViewMode, states: { xPlane: { entry: 'viewModeXPlane', }, yPlane: { entry: 'viewModeYPlane', }, zPlane: { entry: 'viewModeZPlane', }, volume: { entry: 'viewModeVolume', }, }, on: { VIEW_MODE_CHANGED: [ { target: '.xPlane', cond: (c, e) => e.data === 'XPlane' }, { target: '.yPlane', cond: (c, e) => e.data === 'YPlane' }, { target: '.zPlane', cond: (c, e) => e.data === 'ZPlane' }, { target: '.volume', cond: (c, e) => e.data === 'Volume', }, ], }, }, }, }, }, }, options ) } export default createMainRenderingMachine ================================================ FILE: src/Rendering/VTKJS/Images/ComposeImage.worker.js ================================================ import registerWebworker from 'webworker-promise/lib/register' import vtkITKHelper from 'vtk.js/Sources/Common/DataModel/ITKHelper' import vtkBoundingBox from 'vtk.js/Sources/Common/DataModel/BoundingBox' import { computeRanges } from '../../../IO/Analyze/computeRanges' import { createRangeHelper } from '../../../IO/Analyze/createRangeHelper' import { createCompareImage } from '../../../IO/Compare/createCompareImage' import { compareImageSpaces, resampleLabelImage, } from '../../../IO/ResampleLabelImage/resampleLabelImage' import { composeComponents, parseByComponent, pickAndFuseComponents, } from '../../../IO/composeComponents' import itkConfig from '../itkConfig.js' const checkOverlap = (imageA, imageB) => { const [vtkA, vtkB] = [imageA, imageB].map(vtkITKHelper.convertItkToVtkImage) if (!vtkBoundingBox.intersects(vtkA.getBounds(), vtkB.getBounds())) { console.warn( 'Trying to compare images but bounds do not intersect. Moving image will be empty.' ) } } const pickRanges = compInfos => compInfos .map(({ image: { ranges }, fromComponent }) => ranges?.[fromComponent]) // no ranges in label // if missing any range, return undefined .reduce((ranges, range) => { if (!ranges || !range) return undefined return [...ranges, range] }, []) ?.map(([min, max]) => ({ min, max })) const ensureRanges = async image => { const componentInfo = parseByComponent(image) const componentRanges = pickRanges(componentInfo) ?? (await computeRanges(image.data, componentInfo.length)) image.ranges = componentRanges.map(({ min, max }) => [min, max]) return image } const ensureSameImageSpace = async ({ targetImage, resampleImage }) => { if (!resampleImage || !targetImage) return resampleImage if (compareImageSpaces(targetImage, resampleImage)) return resampleImage return resampleLabelImage(targetImage, resampleImage) } const pickIntensityComponents = (image, components) => { // 4+ component and ConglomerateImage processing // fuseLabelImage assumes label is on the last component const withoutLabel = components.filter(componentIndex => componentIndex >= 0) return pickAndFuseComponents({ image, components: withoutLabel, }) } const fuseConglomerate = image => { if (!Array.isArray(image)) return image return composeComponents(image) } const makeCompareImage = async ({ image, fixedImage, options }) => { if (!options.method || options.method === 'disabled') return image if (!fixedImage) { console.error('No fixed image') return } const itkImage = await fuseConglomerate(image) const itkFixedImage = await fuseConglomerate(fixedImage) // check if there is overlap in physical space. If none, resample of image to fixedImage will be empty. checkOverlap(itkImage, itkFixedImage) const { ranges } = await ensureRanges(itkFixedImage) const rangeHelper = createRangeHelper() ranges.flat().forEach(v => rangeHelper.add(v)) const { min, max } = rangeHelper.getRange() return createCompareImage(itkImage, itkFixedImage, { minMax: [min, max], ...options, }) } const fuseLabelImage = async (image, labelImage) => { const components = [...Array(image.imageType.components).keys(), -1] const imageWithLabel = await pickAndFuseComponents({ image, labelImage, components, }) return imageWithLabel } const configureItkWasm = ({ pipelinesUrl, pipelineWorkerUrl }) => { itkConfig.pipelinesUrl = pipelinesUrl itkConfig.pipelineWorkerUrl = pipelineWorkerUrl } registerWebworker( async ({ image: inImage, labelImage, visualizedComponents, fixedImage, compare, itkWasmConfig, }) => { configureItkWasm(itkWasmConfig) let image = await makeCompareImage({ image: inImage, fixedImage, options: compare, }) image = await pickIntensityComponents(image, visualizedComponents) // Label processing const labelResampled = await ensureSameImageSpace({ targetImage: image, resampleImage: labelImage, }) image = labelResampled ? await fuseLabelImage(image, labelResampled) : image image = await ensureRanges(image) return new registerWebworker.TransferableResponse({ image }, [ image.data.buffer, ]) } ) ================================================ FILE: src/Rendering/VTKJS/Images/applyBlendMode.js ================================================ function applyBlendMode(context, event) { const name = event.data.name const blendMode = event.data.blendMode if (!!context.images.representationProxy) { const volumeMapper = context.images.representationProxy.getMapper() const blendModeLower = blendMode.toLowerCase() switch (blendModeLower) { case 'composite': volumeMapper.setBlendMode(0) break case 'maximum': volumeMapper.setBlendMode(1) break case 'minimum': volumeMapper.setBlendMode(2) break case 'average': volumeMapper.setBlendMode(3) break default: throw new Error(`Invalid blend mode: ${blendMode}`) } context.service.send('RENDER') } } export default applyBlendMode ================================================ FILE: src/Rendering/VTKJS/Images/applyCinematicChanged.js ================================================ import vtkLight from 'vtk.js/Sources/Rendering/Core/Light' import { VOLUME_AMBIENT_DEFAULT, VOLUME_DIFFUSE_DEFAULT, } from '../vtk/ItkVtkViewProxy' export function applyCinematicChanged(context, { actorContext }) { if (!context.images.representationProxy) return const { cinematicParameters: { isCinematicPossible, scatteringBlend, diffuse, ambient, }, } = actorContext const renderer = context.itkVtkView.getRenderer() const mapper = context.images.representationProxy.getMapper() renderer.removeAllLights() const isCinematicOn = isCinematicPossible && scatteringBlend > 0 if (isCinematicOn) { const light = vtkLight.newInstance() light.setLightTypeToSceneLight() light.setColor(1, 1, 1) light.setIntensity(1) light.setDirection([1, 1, 1]) renderer.addLight(light) } else { renderer.createLight() // headlight } renderer.setTwoSidedLighting(!isCinematicOn) mapper.setVolumetricScatteringBlending(scatteringBlend) const volumeProps = context.images.representationProxy.getVolumes() volumeProps.forEach(volume => { const vProperty = volume.getProperty() if (isCinematicOn) { vProperty.setDiffuse(diffuse) vProperty.setAmbient(ambient) } else { vProperty.setDiffuse(VOLUME_DIFFUSE_DEFAULT) vProperty.setAmbient(VOLUME_AMBIENT_DEFAULT) } }) context.service.send('RENDER') } ================================================ FILE: src/Rendering/VTKJS/Images/applyColorMap.js ================================================ import { getColorMap } from 'itk-viewer-color-maps' // We want an offset so there is contrast with label image colors const COLOR_OFFSET = 146 function applyColorMap(context, { data: { name, colorMap, component } }) { const actorContext = context.images.actorContext.get(name) // Optional chain on colorTransferFunctions in case compare set in createViewer const colorTransferFunction = context.images.colorTransferFunctions?.get( component ) // if number of components increased after compare set and applyRenderedImage has not happened yet if (!colorTransferFunction) return colorTransferFunction.applyColorMap( getColorMap(colorMap, component + COLOR_OFFSET) ) colorTransferFunction.modified() // applyColorMap does not always trigger modified() if (actorContext.colorRanges.has(component)) { const range = actorContext.colorRanges.get(component) colorTransferFunction.setMappingRange(range[0], range[1]) colorTransferFunction.updateRange() } // update UI context.service.send('IMAGE_COLOR_MAP_DEPENDENCIES_UPDATE', { data: { name, component, }, }) context.service.send('RENDER') } export default applyColorMap ================================================ FILE: src/Rendering/VTKJS/Images/applyColorRange.js ================================================ function applyColorRange(context, e) { const { data: { component, range }, } = e if (!context.images.colorTransferFunctions) { return } const colorTransferFunction = context.images.colorTransferFunctions.get( component ) colorTransferFunction.setMappingRange(range[0], range[1]) colorTransferFunction.updateRange() context.service.send('RENDER') } export default applyColorRange ================================================ FILE: src/Rendering/VTKJS/Images/applyColorRangeBounds.js ================================================ const assignColorRangeBounds = ( { images }, { data: { name, component, range, keepAutoAdjusting = false } } ) => { const { colorRangeBounds, colorRangeBoundsAutoAdjust, } = images.actorContext.get(name) colorRangeBounds.set(component, range) colorRangeBoundsAutoAdjust.set( component, colorRangeBoundsAutoAdjust.get(component) && keepAutoAdjusting ) return images } export const applyColorRangeBounds = (context, event) => { assignColorRangeBounds(context, event) } ================================================ FILE: src/Rendering/VTKJS/Images/applyComponentVisibility.js ================================================ import { applyComponentWeights } from './applyComponentWeights' function applyComponentVisibility(context, event) { const name = event.data.name const index = event.data.component const visibility = event.data.visibility const actorContext = context.images.actorContext.get(name) const componentVisibilities = actorContext.componentVisibilities const visualizedComponents = actorContext.visualizedComponents if (visibility && visualizedComponents.indexOf(index) < 0) { // add component to visualizedComponents visualizedComponents.push(index) for (let i = 0; i < visualizedComponents.length; i++) { if (!componentVisibilities[visualizedComponents[i]]) { visualizedComponents.splice(i, 1) break } } context.service.send({ type: 'UPDATE_RENDERED_IMAGE', data: { name } }) } applyComponentWeights(context, name) } export default applyComponentVisibility ================================================ FILE: src/Rendering/VTKJS/Images/applyComponentWeights.js ================================================ export function applyComponentWeights(context, name) { const actorContext = context.images.actorContext.get(name) const { labelImageBlend } = actorContext if (context.images.representationProxy) { const volume = context.images.representationProxy.getVolumes()[0] const volumeProperty = volume.getProperty() const sliceActor = context.images.representationProxy.getActors()[0] const sliceProperty = sliceActor.getProperty() const visualizedComponents = actorContext.visualizedComponents const componentVisibilities = actorContext.componentVisibilities const weight = 1.0 visualizedComponents.forEach((componentIdx, fusedImgIdx) => { let compWeight = componentVisibilities[componentIdx] ? weight : 0 if (componentIdx < 0) { compWeight = labelImageBlend } volumeProperty.setComponentWeight(fusedImgIdx, compWeight) sliceProperty.setComponentWeight(fusedImgIdx, compWeight) }) context.service.send('RENDER') } } ================================================ FILE: src/Rendering/VTKJS/Images/applyGradientOpacity.js ================================================ function applyGradientOpacity(context, event) { const name = event.data.name const actorContext = context.images.actorContext.get(name) const gradientOpacity = actorContext.gradientOpacity const gradientOpacityScale = actorContext.gradientOpacityScale const visualizedComponents = actorContext.visualizedComponents if (!!context.images.representationProxy) { const dataArray = context.images.representationProxy.getDataArray() const volume = context.images.representationProxy.getVolumes()[0] if (gradientOpacity === 0) { visualizedComponents.forEach((componentIdx, fusedImgIdx) => { componentIdx >= 0 && volume.getProperty().setUseGradientOpacity(fusedImgIdx, false) }) } else { visualizedComponents.forEach((componentIdx, fusedImgIdx) => { if (componentIdx < 0) { return } const dataRange = dataArray.getRange(componentIdx) volume.getProperty().setUseGradientOpacity(fusedImgIdx, true) const minV = Math.max(0.0, gradientOpacity - 0.3) / 0.7 if (minV > 0.0) { volume .getProperty() .setGradientOpacityMinimumValue( fusedImgIdx, Math.exp( Math.log((dataRange[1] - dataRange[0]) * 0.2) * 7 * gradientOpacityScale * minV * minV ) ) } else { volume.getProperty().setGradientOpacityMinimumValue(fusedImgIdx, 0.0) } volume .getProperty() .setGradientOpacityMaximumValue( fusedImgIdx, Math.exp( Math.log((dataRange[1] - dataRange[0]) * 1.0) * 7 * gradientOpacityScale * gradientOpacity * gradientOpacity ) ) }) } context.service.send('RENDER') } } export default applyGradientOpacity ================================================ FILE: src/Rendering/VTKJS/Images/applyIndependentComponents.js ================================================ function applyIndependentComponents(context) { const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const independentComponents = actorContext.independentComponents if (!!context.images.representationProxy) { const sliceActors = context.images.representationProxy.getActors() sliceActors.forEach((actor, actorIdx) => { const actorProp = actor.getProperty() actorProp.setIndependentComponents(independentComponents) }) } } export default applyIndependentComponents ================================================ FILE: src/Rendering/VTKJS/Images/applyLabelImageBlend.js ================================================ import { applyComponentWeights } from './applyComponentWeights' function applyLabelImageBlend(context, event) { applyComponentWeights(context, event.data.name) } export default applyLabelImageBlend ================================================ FILE: src/Rendering/VTKJS/Images/applyLabelImageWeights.js ================================================ import vtkPiecewiseFunction from 'vtk.js/Sources/Common/DataModel/PiecewiseFunction' import transformLabelImageWeight from './transformLabelImageWeight' function applyLabelImageWeights(context, event) { const name = event.data.name const actorContext = context.images.actorContext.get(name) const labelImageWeights = actorContext.labelImageWeights let piecewiseFunction = null if (!context.images.piecewiseFunctions.has('labelImage')) { piecewiseFunction = vtkPiecewiseFunction.newInstance() context.images.piecewiseFunctions.set('labelImage', piecewiseFunction) } else { piecewiseFunction = context.images.piecewiseFunctions.get('labelImage') } const haveBackground = labelImageWeights.keys().next().value === 0 ? true : false let minLabelWeight = 0.0 let maxLabelWeight = 1.0 if (!actorContext.image) { maxLabelWeight = 0.05 if (context.main.viewMode !== 'Volume') { maxLabelWeight = 1.0 minLabelWeight = 0.4 } } piecewiseFunction.removeAllPoints() const weightIter = labelImageWeights.entries() let entry = weightIter.next() if (haveBackground) { piecewiseFunction.addPointLong(0, 0.0, 0.5, 1.0) } else { piecewiseFunction.addPointLong( entry.value[0], transformLabelImageWeight(entry.value[1], minLabelWeight, maxLabelWeight), 0.5, 1.0 ) } while (!entry.done) { piecewiseFunction.addPointLong( entry.value[0], transformLabelImageWeight(entry.value[1], minLabelWeight, maxLabelWeight), 0.5, 1.0 ) entry = weightIter.next() } const volume = context.images.representationProxy.getVolumes()[0] const volumeProperty = volume.getProperty() const component = actorContext.visualizedComponents.length - 1 volumeProperty.setScalarOpacity(component, piecewiseFunction) // The slice shows the same piecewise function as the volume for label map const sliceActors = context.images.representationProxy.getActors() sliceActors.forEach(actor => { const actorProp = actor.getProperty() actorProp.setPiecewiseFunction(component, piecewiseFunction) }) context.service.send('RENDER') } export default applyLabelImageWeights ================================================ FILE: src/Rendering/VTKJS/Images/applyLabelNames.js ================================================ function applyLabelNames(context, event) { const name = event.data.name const labelNames = event.data.labelNames context.itkVtkView.setLabelNames(labelNames) } export default applyLabelNames ================================================ FILE: src/Rendering/VTKJS/Images/applyLookupTable.js ================================================ import vtkColorTransferFunction from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction' import { OpacityMode } from 'vtk.js/Sources/Rendering/Core/VolumeProperty/Constants' import applyCategoricalColorToColorTransferFunction from '../../../UI/reference-ui/src/applyCategoricalColorToColorTransferFunction' function applyLookupTable(context, event) { const name = event.data.name const actorContext = context.images.actorContext.get(name) const lookupTable = event.data.lookupTable let colorTransferFunction = null if (context.images.colorTransferFunctions.has('labelImage')) { colorTransferFunction = context.images.colorTransferFunctions.get( 'labelImage' ) } else { colorTransferFunction = vtkColorTransferFunction.newInstance() context.images.colorTransferFunctions.set( 'labelImage', colorTransferFunction ) } // wait for assignRenderedImage which computes uniqueLabels, then applyRenderedImage calls applyLookupTable if (!actorContext.uniqueLabels) return const uniqueLabels = Array.from(actorContext.uniqueLabels) applyCategoricalColorToColorTransferFunction( colorTransferFunction, uniqueLabels, lookupTable ) const volume = context.images.representationProxy.getVolumes()[0] const volumeProperty = volume.getProperty() const component = actorContext.visualizedComponents.length - 1 volumeProperty.setRGBTransferFunction(component, colorTransferFunction) volumeProperty.setIndependentComponents(true) volumeProperty.setOpacityMode(component, OpacityMode.PROPORTIONAL) // The slice shows the same lut as the volume for label map const sliceActors = context.images.representationProxy.getActors() sliceActors.forEach(actor => { const actorProp = actor.getProperty() actorProp.setIndependentComponents(true) actorProp.setRGBTransferFunction(component, colorTransferFunction) }) context.service.send('RENDER') } export default applyLookupTable ================================================ FILE: src/Rendering/VTKJS/Images/applyPiecewiseFunction.js ================================================ function applyPiecewiseFunction(context, event) { const name = event.data.name const component = event.data.component // const range = event.data.range const nodes = event.data.nodes const actorContext = context.images.actorContext.get(name) const image = actorContext.image const pwf = context.images.piecewiseFunctions?.get(component) if (pwf && image) { const slicePiecewiseFunction = pwf.slice const volumePiecewiseFunction = pwf.volume volumePiecewiseFunction.setNodes(nodes) const sliceNodes = nodes.length > 2 ? nodes.slice(1, -1) : nodes // if more than 2, remove "window" nodes with y = 0 slicePiecewiseFunction.setNodes(sliceNodes) context.service.send('RENDER') } } export default applyPiecewiseFunction ================================================ FILE: src/Rendering/VTKJS/Images/applyRenderedImage.js ================================================ import vtkColorTransferFunction from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction' import vtkPiecewiseFunction from 'vtk.js/Sources/Common/DataModel/PiecewiseFunction' import { OpacityMode } from 'vtk.js/Sources/Rendering/Core/VolumeProperty/Constants' import applyGradientOpacity from './applyGradientOpacity' import applyLabelImageBlend from './applyLabelImageBlend' import applyVolumeSampleDistance from './applyVolumeSampleDistance' import { makeCroppable, updateCroppingParametersFromImage, } from '../Main/croppingPlanes' import applyLookupTable from './applyLookupTable' import toggleInterpolation from './toggleInterpolation' const ANNOTATION_DEFAULT = '
Index:${iIndex},${jIndex},${kIndex}
Position:${xPosition},${yPosition},${zPosition}
Value:${value}
Label:${annotation}
' const ANNOTATION_CUSTOM_PREFIX = '' const ANNOTATION_CUSTOM_POSTFIX = '
Scale:
Position:${xPosition},${yPosition},${zPosition}
Value:${value}
Label:${annotation}
' const getDefaultRangeByDataType = dataType => { const range = [] switch (dataType) { case 'Uint8Array': range[0] = 0 range[1] = 255 break case 'Int8Array': range[0] = -128 range[1] = 127 break case 'Uint16Array': range[0] = 0 range[1] = 65535 break case 'Int16Array': range[0] = -32768 range[1] = 32767 break case 'Uint32Array': range[0] = 0 range[1] = 4294967295 break case 'Int32Array': range[0] = -2147483648 range[1] = 2147483647 break case 'BigInt64Array': range[0] = BigInt(-9223372036854775808) range[1] = BigInt(9223372036854775808) break case 'Float32Array': range[0] = 0.0 range[1] = 1.0 break default: console.error('Unsupported data type') } return range } function applyRenderedImage(context, { data: { name } }) { const actorContext = context.images.actorContext.get(name) if (!actorContext.fusedImage) { return } const image = actorContext.image const labelImage = actorContext.labelImage const labelImageComponentFactor = labelImage ? -1 : 0 // component count minus label image const numberOfComponents = actorContext.fusedImage ? actorContext.fusedImage .getPointData() .getScalars() .getNumberOfComponents() + labelImageComponentFactor : 0 context.images.source.setInputData(actorContext.fusedImage) // VTK.js currently only supports a single image if (!context.images.representationProxy) { context.proxyManager.createRepresentationInAllViews(context.images.source) context.images.representationProxy = context.proxyManager.getRepresentation( context.images.source, context.itkVtkView ) const { representationProxy } = context.images makeCroppable(context, representationProxy) if (context.use2D) { context.itkVtkView.setViewMode('ZPlane') context.itkVtkView.setOrientationAxesVisibility(false) } else { context.itkVtkView.setViewMode('Volume') } context.itkVtkView.setAxesNames(image?.scaleInfo[0].axesNames) // ? for no image, only imageLabel case const annotationContainer = context.renderingViewContainers .get('volume') .querySelector('.js-se') annotationContainer.style.fontFamily = 'monospace' toggleInterpolation(context, { data: name }) const { widgetCroppingPlanes } = context.main const sliceActors = representationProxy.getActors() sliceActors.forEach((actor, actorIdx) => { const sliceMapper = actor.getMapper() switch (actorIdx) { case 0: sliceMapper.addClippingPlane(widgetCroppingPlanes[2]) sliceMapper.addClippingPlane(widgetCroppingPlanes[3]) sliceMapper.addClippingPlane(widgetCroppingPlanes[4]) sliceMapper.addClippingPlane(widgetCroppingPlanes[5]) break case 1: sliceMapper.addClippingPlane(widgetCroppingPlanes[0]) sliceMapper.addClippingPlane(widgetCroppingPlanes[1]) sliceMapper.addClippingPlane(widgetCroppingPlanes[4]) sliceMapper.addClippingPlane(widgetCroppingPlanes[5]) break case 2: sliceMapper.addClippingPlane(widgetCroppingPlanes[0]) sliceMapper.addClippingPlane(widgetCroppingPlanes[1]) sliceMapper.addClippingPlane(widgetCroppingPlanes[2]) sliceMapper.addClippingPlane(widgetCroppingPlanes[3]) break default: console.error('Unexpected slice actor') } }) } else { context.images.representationProxy.setInput(context.images.source) } const { representationProxy } = context.images // undo representationProxy.setInput calling volume.setVisibility(false) if it finds dimensions === 2 (may have just been cropped) representationProxy.setVolumeVisibility( !context.use2D && context.main.viewMode === 'Volume' ) // triggers update of ImageSliceOutlines if fusedImage size changed representationProxy.getActors().forEach(actor => actor.getMapper().modified()) // Create color map and piecewise function objects as needed if (typeof context.images.colorTransferFunctions === 'undefined') { context.images.colorTransferFunctions = new Map() } if (typeof context.images.piecewiseFunctions === 'undefined') { context.images.piecewiseFunctions = new Map() } // Create color map and piecewise function objects as needed for (let component = 0; component < numberOfComponents; component++) { const colorTransferFunction = context.images.colorTransferFunctions.get(component) ?? vtkColorTransferFunction.newInstance() context.images.colorTransferFunctions.set(component, colorTransferFunction) if (!context.images.piecewiseFunctions.has(component)) { const piecewiseFunction = { slice: vtkPiecewiseFunction.newInstance(), volume: vtkPiecewiseFunction.newInstance(), } context.images.piecewiseFunctions.set(component, piecewiseFunction) } // Compare may have increased number of components. // send({ type: 'IMAGE_COLOR_MAP_CHANGED' }) called below after representations updated } // Visualized components may have updated -> set color transfer function, piecewise function, component visibility, independent components in slices const sliceActors = context.images.representationProxy.getActors() sliceActors.forEach(actor => { const actorProperty = actor.getProperty() actorProperty.setIndependentComponents(actorContext.independentComponents) actor.getMapper().setInputData(actorContext.fusedImage) actorContext.visualizedComponents .filter(componentIndex => componentIndex >= 0) // skip label map .forEach((componentIndex, fusedImageIndex) => { const colorTransferFunction = context.images.colorTransferFunctions.get( componentIndex ) actorProperty.setRGBTransferFunction( fusedImageIndex, colorTransferFunction ) const piecewiseFunction = context.images.piecewiseFunctions.get( componentIndex ).slice actorProperty.setPiecewiseFunction(fusedImageIndex, piecewiseFunction) const componentVisibility = actorContext.componentVisibilities[componentIndex] actorProperty.setComponentWeight( fusedImageIndex, componentVisibility ? 1.0 : 0.0 ) actorProperty.setUseLookupTableScalarRange(true) }) }) // Visualized components may have updated -> set color transfer function, piecewise function, component visibility, independent components in volumes const volumeProps = context.images.representationProxy.getVolumes() volumeProps.forEach(volume => { const volumeProperty = volume.getProperty() volume.getMapper().setInputData(actorContext.fusedImage) volumeProperty.setIndependentComponents(actorContext.independentComponents) let componentsVisible = false actorContext.visualizedComponents.forEach( (componentIndex, fusedImageIndex) => { // Set for intensity and label map components. Components count may have changed. const mode = componentIndex < 0 ? OpacityMode.PROPORTIONAL : OpacityMode.FRACTIONAL volumeProperty.setOpacityMode(fusedImageIndex, mode) if (!context.images.colorTransferFunctions.has(componentIndex)) { return } const colorTransferFunction = context.images.colorTransferFunctions.get( componentIndex ) const piecewiseFunction = context.images.piecewiseFunctions.get( componentIndex ).volume volumeProperty.setScalarOpacity(fusedImageIndex, piecewiseFunction) volumeProperty.setRGBTransferFunction( fusedImageIndex, colorTransferFunction ) const componentVisibility = actorContext.componentVisibilities[componentIndex] componentsVisible = componentVisibility ? true : componentsVisible volumeProperty.setComponentWeight( fusedImageIndex, componentVisibility ? 1.0 : 0.0 ) } ) }) // Todo: results in necessary side-effect? applyGradientOpacity(context, { data: { name, gradientOpacity: actorContext.gradientOpacity }, }) applyVolumeSampleDistance(context, { data: { name, volumeSampleDistance: actorContext.volumeSampleDistance }, }) representationProxy.getMapper().setMaximumSamplesPerRay(2814) // update color ranges actorContext.visualizedComponents .map((componentIndex, fusedImageIndex) => [componentIndex, fusedImageIndex]) .filter(([componentIndex]) => componentIndex >= 0) // skip label map .forEach(([componentIndex, fusedImageIndex]) => { const { colorRangeBoundsAutoAdjust, colorRangeBounds, colorRanges, colorRangeMinAutoAdjust, colorRangeMaxAutoAdjust, } = actorContext const dataArray = actorContext.fusedImage.getPointData().getScalars() const [dataMin, dataMax] = dataArray.getRange(fusedImageIndex) const [newMin, newMax] = !actorContext.independentComponents || dataMin - dataMax === 0 ? getDefaultRangeByDataType(dataArray.getDataType()) : [dataMin, dataMax] const storedColorRange = colorRanges.get(componentIndex) const minAutoAdjust = colorRangeMinAutoAdjust.get(componentIndex) const maxAutoAdjust = colorRangeMaxAutoAdjust.get(componentIndex) if (minAutoAdjust || maxAutoAdjust || !storedColorRange) { const fullRange = actorContext.colorRangeBounds.get(componentIndex) ?? [ 0, 1, ] const range = storedColorRange ?? [0.2, 0.8] // rescale to new range const diff = fullRange[1] - fullRange[0] const colorRangeNormalized = [ (range[0] - fullRange[0]) / diff, (range[1] - fullRange[0]) / diff, ] const newDelta = newMax - newMin const newRange = colorRangeNormalized.map(x => { return x * newDelta + newMin }) if (!minAutoAdjust) newRange[0] = range[0] if (!maxAutoAdjust) newRange[1] = range[1] context.service.send({ type: 'IMAGE_COLOR_RANGE_CHANGED', data: { name, component: componentIndex, range: newRange, keepAutoAdjusting: true, }, }) } if (colorRangeBoundsAutoAdjust.get(componentIndex)) { const oldRange = colorRangeBounds.get(componentIndex) ?? [ Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, ] // only grow range const newRange = [ Math.min(newMin, oldRange[0]), Math.max(newMax, oldRange[1]), ] const hasChanged = newRange.some((value, i) => value !== oldRange[i]) if (hasChanged) { context.service.send({ type: 'IMAGE_COLOR_RANGE_BOUNDS_CHANGED', data: { name, component: componentIndex, range: newRange, keepAutoAdjusting: true, }, }) } } }) if (labelImage) { const uniqueLabels = actorContext.uniqueLabels const labelNames = actorContext.labelNames let labelNameAdded = false const labelImageWeights = actorContext.labelImageWeights let labelImageWeightAdded = false for (let index = 0; index < uniqueLabels.length; index++) { const label = uniqueLabels[index] if (!labelNames.has(label)) { labelNames.set(label, label.toString()) labelNameAdded = true } if (!labelImageWeights.has(label)) { // 0 is usually the background label -- suppress it label === 0 ? labelImageWeights.set(label, 0.0) : labelImageWeights.set(label, 1.0) labelImageWeightAdded = true } } if (labelNameAdded) { context.service.send({ type: 'LABEL_IMAGE_LABEL_NAMES_CHANGED', data: { name, labelNames }, }) } if (labelImageWeightAdded) { context.service.send({ type: 'LABEL_IMAGE_WEIGHTS_CHANGED', data: { name, labelImageWeights }, }) context.service.send({ type: 'LABEL_IMAGE_LOOKUP_TABLE_CHANGED', data: { name, lookupTable: actorContext.lookupTable }, }) } // Always call if component count changes. // Apply synchronously to avoid error on render in use2D=true case. applyLookupTable(context, { data: { name, lookupTable: actorContext.lookupTable }, }) applyLabelImageBlend(context, { data: { name, labelImageBlend: actorContext.labelImageBlend }, }) } // Call after representations have been updated with possibly more or less components. // IMAGE_COLOR_MAP_CHANGED triggers a render which errors if a slice representation is missing a transfer function for a component for (let component = 0; component < numberOfComponents; component++) { const colorMap = actorContext.colorMaps.get(component) context.service.send({ type: 'IMAGE_COLOR_MAP_CHANGED', data: { name, component, colorMap }, }) // Update piecewise function nodes after we have data range // Without this piecewise functions are never "applied" const points = context.images.actorContext .get(name) .piecewiseFunctionPoints.get(component) context.service.send({ type: 'IMAGE_PIECEWISE_FUNCTION_POINTS_CHANGED', data: { name, component, points }, }) } // call after representations are created updateCroppingParametersFromImage(context, actorContext.fusedImage) if (actorContext.image) { context.itkVtkView.updateLabelBoundingBox( name, actorContext.image.getWorldBounds(actorContext.loadedScale) ) } if (actorContext.labelImage) { context.itkVtkView.updateLabelBoundingBox( actorContext.labelImageName, actorContext.labelImage.getWorldBounds(actorContext.loadedScale) ) } const loadedImage = actorContext.image ?? actorContext.labelImage const hasOneScale = loadedImage.scaleInfo.length === 1 if (context.itkVtkView.setSeCornerAnnotation) { if (hasOneScale) { context.itkVtkView.setSeCornerAnnotation(ANNOTATION_DEFAULT) } else { context.itkVtkView.setSeCornerAnnotation( `${ANNOTATION_CUSTOM_PREFIX}${actorContext.loadedScale}${ANNOTATION_CUSTOM_POSTFIX}` ) } } } export default applyRenderedImage ================================================ FILE: src/Rendering/VTKJS/Images/applySelectedLabel.js ================================================ function applySelectedLabel(context, event) {} export default applySelectedLabel ================================================ FILE: src/Rendering/VTKJS/Images/applyShadow.js ================================================ function applyShadow(context, event) { const name = event.data const actorContext = context.images.actorContext.get(name) const useShadow = actorContext.shadowEnabled if (!!context.images.representationProxy) { context.images.representationProxy.setUseShadow(useShadow) context.service.send('RENDER') } } export default applyShadow ================================================ FILE: src/Rendering/VTKJS/Images/applyVolumeSampleDistance.js ================================================ function applyVolumeSampleDistance(context, event) { if (context.images.representationProxy) { const { volumeSampleDistance } = event.data context.images.representationProxy.setSampleDistance(volumeSampleDistance) const sourceDS = context.images.representationProxy.getInputDataSet() const sampleDistance = 0.7 * Math.sqrt( sourceDS .getSpacing() .map(v => v * v) .reduce((a, b) => a + b, 0) ) context.images.representationProxy .getMapper() .setSampleDistance( sampleDistance * 2 ** (volumeSampleDistance * 3.0 - 1.5) ) context.service.send('RENDER') } } export default applyVolumeSampleDistance ================================================ FILE: src/Rendering/VTKJS/Images/assignRenderedImage.js ================================================ import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray' import { assign } from 'xstate' import numericalSort from '../numericalSort' const updateContextWithLabelImage = (actorContext, scaleLabelImage) => { const uniqueLabelsSet = new Set(scaleLabelImage.data) const uniqueLabels = Array.from(uniqueLabelsSet) // The volume mapper currently only supports ColorTransferFunction's, // not LookupTable's // lut.setAnnotations(uniqueLabels, uniqueLabels); uniqueLabels.sort(numericalSort) actorContext.uniqueLabels = uniqueLabels actorContext.renderedLabelImage = scaleLabelImage } const assignRenderedImage = assign({ images: ( { images }, { data: { name, componentRanges, itkImage, vtkImage, labelAtScale } } ) => { const actorContext = images.actorContext.get(name) if (labelAtScale) updateContextWithLabelImage(actorContext, labelAtScale) if (actorContext.fusedImage) { // re-use fusedImage actorContext.fusedImage.setOrigin(vtkImage.getOrigin()) actorContext.fusedImage.setSpacing(vtkImage.getSpacing()) actorContext.fusedImage.setDirection(vtkImage.getDirection()) actorContext.fusedImage.setDimensions(vtkImage.getDimensions()) } else { actorContext.fusedImage = vtkImage } const { fusedImage } = actorContext // for areBoundsBigger guard actorContext.loadedBounds = fusedImage.getBounds() actorContext.fusedImageData = itkImage.data const imageScalars = vtkImage.getPointData().getScalars() const numberOfComponents = itkImage.imageType.components const fusedImageScalars = vtkDataArray.newInstance({ name: imageScalars.getName() || 'Scalars', values: actorContext.fusedImageData, numberOfComponents, }) fusedImage.getPointData().setScalars(fusedImageScalars) componentRanges.forEach((range, comp) => fusedImageScalars.setRange(range, comp) ) // Keeps ProxyRepresentation's call of fusedImageScalars.getRange() from slow computation of "magnitude" combination of components // We don't use. fusedImageScalars.setRange({ min: 0, max: 1 }, numberOfComponents) // Trigger VolumeMapper scalarTexture update fusedImage.modified() return images }, }) export default assignRenderedImage ================================================ FILE: src/Rendering/VTKJS/Images/assignVisualizedComponents.js ================================================ import { assign } from 'xstate' import { getOutputIntensityComponentCount } from '../../Images/createImageRenderingActor' const assignVisualizedComponents = assign({ images: context => { const name = context.actorName const actorContext = context.images.actorContext.get(name) const { image, labelImage, editorLabelImage } = actorContext if (image) { const imageComponents = getOutputIntensityComponentCount(actorContext) actorContext.visualizedComponents = Array(imageComponents) .fill(0) .map((_, idx) => idx) .filter(i => actorContext.componentVisibilities[i]) actorContext.visualizedComponents = actorContext.visualizedComponents.slice( 0, getOutputIntensityComponentCount(actorContext) ) actorContext.maxIntensityComponents = 4 if (labelImage) { actorContext.maxIntensityComponents -= 1 } if (editorLabelImage) { actorContext.maxIntensityComponents -= 1 } const numVizComps = Math.min( actorContext.visualizedComponents.length, actorContext.maxIntensityComponents ) if (actorContext.visualizedComponents.length > numVizComps) { // turn off unrenderable components actorContext.visualizedComponents = actorContext.visualizedComponents.slice( 0, numVizComps ) const offComps = [...Array(imageComponents).keys()].filter( comp => !actorContext.visualizedComponents.includes(comp) ) offComps.forEach(comp => context.service.send({ type: 'IMAGE_COMPONENT_VISIBILITY_CHANGED', data: { name, component: comp, visibility: false }, }) ) } } if (labelImage) { actorContext.visualizedComponents = actorContext.visualizedComponents ?? [] actorContext.visualizedComponents.push(-1) } return context.images }, }) export default assignVisualizedComponents ================================================ FILE: src/Rendering/VTKJS/Images/createImageRenderer.js ================================================ import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData' import applyIndependentComponents from './applyIndependentComponents' import applyXSlice from '../Main/applyXSlice' import applyYSlice from '../Main/applyYSlice' import applyZSlice from '../Main/applyZSlice' import '../vtk/OpenGLImageMapperFractional' // calls registerOverride('vtkImageMapper', newInstance) async function createImageRenderer(context) { if (!context.images.source) { context.images.source = context.proxyManager.createProxy( 'Sources', 'TrivialProducer', { name: 'Image' } ) } const actorContext = context.images.actorContext.get( context.images.selectedName ) actorContext.fusedImage = vtkImageData.newInstance() applyIndependentComponents(context) applyXSlice(context, { data: context.main.xSlice }) applyYSlice(context, { data: context.main.ySlice }) applyZSlice(context, { data: context.main.zSlice }) } export default createImageRenderer ================================================ FILE: src/Rendering/VTKJS/Images/fuseImages.js ================================================ import WebworkerPromise from 'webworker-promise' import ComposeImageWorker from './ComposeImage.worker.js' import itkConfig from '../itkConfig.js' export const fuseImages = async ({ imageAtScale, //could be array if Conglomerate labelAtScale, visualizedComponents, fixedImageAtScale, compare, }) => { // When testing, itkConfig is not full URL, so ensure it's absolute // deep copy const itkWasmConfig = JSON.parse(JSON.stringify(itkConfig)) itkWasmConfig.pipelineWorkerUrl = new URL( itkWasmConfig.pipelineWorkerUrl, window.location.href ).href itkWasmConfig.pipelinesUrl = new URL( itkWasmConfig.pipelinesUrl, window.location.href ).href const worker = new WebworkerPromise(new ComposeImageWorker()) const { image } = await worker.postMessage({ image: imageAtScale, labelImage: labelAtScale, visualizedComponents, fixedImage: fixedImageAtScale, compare, itkWasmConfig, }) worker.terminate() const componentRanges = image.ranges.map(([min, max]) => ({ min, max })) return { itkImage: image, componentRanges, } } ================================================ FILE: src/Rendering/VTKJS/Images/imagesRenderingMachineOptions.js ================================================ import { convertVtkToItkImage } from 'vtk.js/Sources/Common/DataModel/ITKHelper' import { writeImageArrayBuffer, copyImage } from 'itk-wasm' import createImageRenderer from './createImageRenderer' import toggleLayerVisibility from './toggleLayerVisibility' import toggleLayerBBox from './toggleLayerBBox' import applyComponentVisibility from './applyComponentVisibility' import updateRenderedImage from './updateRenderedImage' import updateHistogram from './updateHistogram' import selectImageLayer from './selectImageLayer' import toggleInterpolation from './toggleInterpolation' import applyColorRange from './applyColorRange' import { applyColorRangeBounds } from './applyColorRangeBounds' import applyColorMap from './applyColorMap' import applyRenderedImage from './applyRenderedImage' import assignRenderedImage from './assignRenderedImage' import applyPiecewiseFunction from './applyPiecewiseFunction' import applyShadow from './applyShadow' import applyGradientOpacity from './applyGradientOpacity' import applyVolumeSampleDistance from './applyVolumeSampleDistance' import applyBlendMode from './applyBlendMode' import applyLookupTable from './applyLookupTable' import applyLabelImageBlend from './applyLabelImageBlend' import applyLabelNames from './applyLabelNames' import applyLabelImageWeights from './applyLabelImageWeights' import applySelectedLabel from './applySelectedLabel' import mapToPiecewiseFunctionNodes from './mapToPiecewiseFunctionNodes' import mapToColorFunctionRange from './mapToColorFunctionRange' import { getBoundsOfFullImage } from '../Main/croppingPlanes' import { computeRenderedBounds } from '../Main/computeRenderedBounds' import { applyCinematicChanged } from './applyCinematicChanged' import assignVisualizedComponents from './assignVisualizedComponents' const EPSILON = 0.000001 const areBoundsBiggerThanLoaded = context => { const { images: { actorContext }, actorName, } = context const { loadedBounds } = actorContext.get(actorName) if (!loadedBounds) return true const fullImage = getBoundsOfFullImage(context) const current = computeRenderedBounds(context) // clamp rendered bounds to max size of image current.forEach((b, i) => { current[i] = i % 2 ? Math.min(b, fullImage[i]) // high bound case : Math.max(b, fullImage[i]) // low bound case }) return loadedBounds.some((loaded, i) => { return i % 2 ? current[i] - loaded > EPSILON // high bound case: currentBounds[i] > loadedBound : loaded - current[i] > EPSILON // low bound case: currentBounds[i] < loadedBound }) } const isTargetScaleLoaded = context => { const { images: { actorContext }, targetScale, actorName, } = context const { loadedScale } = actorContext.get(actorName) return loadedScale === targetScale } function downloadArray(content, filename = 'download') { const url = URL.createObjectURL(new Blob([content])) const a = document.createElement('a') a.href = url a.download = filename document.body.appendChild(a) function clickHandler() { setTimeout(() => { URL.revokeObjectURL(url) a.removeEventListener('click', clickHandler) }, 200) } a.addEventListener('click', clickHandler, false) a.click() return a } const imagesRenderingMachineOptions = { imageRenderingActor: { services: { createImageRenderer, updateRenderedImage, updateHistogram, }, actions: { applyRenderedImage, assignRenderedImage, assignVisualizedComponents, toggleLayerVisibility, applyComponentVisibility, applyPiecewiseFunction, applyColorRange, applyColorRangeBounds, applyColorMap, mapToPiecewiseFunctionNodes, mapToColorFunctionRange, toggleInterpolation, applyShadow, applyGradientOpacity, applyVolumeSampleDistance, applyBlendMode, applyLookupTable, applyLabelImageBlend, applyLabelNames, applyLabelImageWeights, applySelectedLabel, applyCinematicChanged, toggleLayerBBox, }, guards: { isFramerateScalePickingOn: ({ images, actorName }) => images.actorContext.get(actorName).isFramerateScalePickingOn, isImageUpdateNeeded: context => context.isUpdateForced || (context.images.selectedName === context.actorName && // only update if rendering (aka selected) (!isTargetScaleLoaded(context) || areBoundsBiggerThanLoaded(context))), }, }, actions: { selectImageLayer, downloadImage: (context, event) => { const { name, format } = event.data const fileName = `${name}.${format}` const actorContext = context.images.actorContext.get(name) const fusedImage = actorContext.fusedImage if (!fusedImage) { console.warn('No image to download') return } const itkImage = copyImage(convertVtkToItkImage(fusedImage, true)) writeImageArrayBuffer(null, itkImage, fileName).then( ({ arrayBuffer }) => { downloadArray(arrayBuffer, fileName) } ) }, }, } export default imagesRenderingMachineOptions ================================================ FILE: src/Rendering/VTKJS/Images/mapToColorFunctionRange.js ================================================ function mapToColorFunctionRange( context, { data: { name, component, points } } ) { const actorContext = context.images.actorContext.get(name) const dataRange = actorContext.colorRangeBounds.get(component) if (!dataRange) return // viewer.setImagePiecewiseFunctionPoints called at start const rangeDelta = dataRange[1] - dataRange[0] const range = points.map(v => v * rangeDelta + dataRange[0]) // compare with current values to see if updated needed const { colorRanges } = context.images.actorContext.get(name) if (colorRanges.has(component)) { const currentRange = colorRanges.get(component) if (currentRange[0] === range[0] && currentRange[1] === range[1]) { return } } context.service.send({ type: 'IMAGE_COLOR_RANGE_CHANGED', data: { name, component, range, }, }) } export default mapToColorFunctionRange ================================================ FILE: src/Rendering/VTKJS/Images/mapToPiecewiseFunctionNodes.js ================================================ import { getNodes } from 'itk-viewer-transfer-function-editor' // grab head and tail or fallback to data range if 1 or less points const getRange = nodes => nodes.length > 1 ? [nodes[0].x, nodes[nodes.length - 1].x] : undefined function mapToPiecewiseFunctionNodes( context, { data: { name, component, points } } ) { const actorContext = context.images.actorContext.get(name) const dataRange = actorContext.colorRangeBounds.get(component) if (!dataRange) return // viewer.setImagePiecewiseFunctionPoints called at start const nodes = getNodes(dataRange, points) const range = getRange(nodes) ?? dataRange context.service.send({ type: 'IMAGE_PIECEWISE_FUNCTION_CHANGED', data: { name, component, range, nodes, }, }) } export default mapToPiecewiseFunctionNodes ================================================ FILE: src/Rendering/VTKJS/Images/selectImageLayer.js ================================================ function selectImageLayer(context, event) { // This may be highlight the selected image with an outline. // For VTK.js, it currently only supports a single image, so we have to // switch. // Todo } export default selectImageLayer ================================================ FILE: src/Rendering/VTKJS/Images/toggleInterpolation.js ================================================ function toggleInterpolation(context, event) { const name = event.data const actorContext = context.images.actorContext.get(name) const interpolation = actorContext.interpolationEnabled context.itkVtkView.setPlanesUseLinearInterpolation(interpolation) } export default toggleInterpolation ================================================ FILE: src/Rendering/VTKJS/Images/toggleLayerBBox.js ================================================ function toggleLayerBBox(context, event) { const name = event.data.layerName const actorContext = context.layers.actorContext.get(name) actorContext.bbox = !actorContext.bbox context.itkVtkView.setEnableBBox(name, actorContext.bbox) context.service.send('RENDER') } export default toggleLayerBBox ================================================ FILE: src/Rendering/VTKJS/Images/toggleLayerVisibility.js ================================================ import applySlicingPlanes from '../Main/applySlicingPlanes' function toggleLayerVisibility(context, event) { const name = event.data const actorContext = context.layers.actorContext.get(name) const visible = actorContext.visible context.itkVtkView.setImageVisibility(visible) if (visible) { applySlicingPlanes(context, { data: context.main.slicingPlanes }) } // Toggle the visibility on the corresponding label image or image layer if ( actorContext.type === 'labelImage' && actorContext.imageName && context.layers.actorContext.has(actorContext.imageName) ) { const imageLayerContext = context.layers.actorContext.get( actorContext.imageName ) if (imageLayerContext.visible !== visible) { context.service.send({ type: 'TOGGLE_LAYER_VISIBILITY', data: actorContext.imageName, }) } } else if (actorContext.type === 'image') { const imageActorContext = context.images.actorContext.get(name) if (context.layers.actorContext.has(imageActorContext.labelImageName)) { const labelImageLayerContext = context.layers.actorContext.get( imageActorContext.labelImageName ) if (labelImageLayerContext.visible !== visible) { context.service.send({ type: 'TOGGLE_LAYER_VISIBILITY', data: imageActorContext.labelImageName, }) } } } context.service.send('RENDER') } export default toggleLayerVisibility ================================================ FILE: src/Rendering/VTKJS/Images/transformLabelImageWeight.js ================================================ function transformLabelImageWeight(weight, minWeight, maxWeight) { return weight * (maxWeight - minWeight) + minWeight } export default transformLabelImageWeight ================================================ FILE: src/Rendering/VTKJS/Images/updateHistogram.js ================================================ import { computeHistogram } from '../../../IO/Analyze/computeHistograms' function makeHistogram(actorContext, component) { const dataArray = actorContext.fusedImage.getPointData().getScalars() if (!dataArray) return undefined const numberOfComponents = dataArray.getNumberOfComponents() const fusedImageComponent = actorContext.visualizedComponents.indexOf( component ) if (fusedImageComponent === -1) return undefined const [min, max] = actorContext.colorRangeBounds.get(component) ?? [0, 0] // [0, 0] default for no image, only imageLabel case return computeHistogram( dataArray.getData(), fusedImageComponent, numberOfComponents, [min, max] ) } async function updateHistogram(context) { const actorContext = context.images.actorContext.get(context.actorName) const component = actorContext.selectedComponent const histogram = actorContext.histograms.get(component) ?? // histogram may have been cleared after loading new data (await makeHistogram(actorContext, component)) if (histogram) actorContext.histograms.set(component, histogram) // component or image may not be loaded context.service.send({ type: 'IMAGE_HISTOGRAM_UPDATED', data: { name: context.actorName, component, histogram }, }) } export default updateHistogram ================================================ FILE: src/Rendering/VTKJS/Images/updateLabelImagePiecewiseFunction.js ================================================ function transformUserWeight(userWeight, minWeight, maxWeight) { return userWeight * (maxWeight - minWeight) + minWeight } function updateLabelImagePiecewiseFunction( context, actorContext, selectedIndices = null ) { if (!!!actorContext.labelImage) { return } const piecewiseFunction = context.images.piecewiseFunctions.get('labelImage') const labelImageWeights = actorContext.labelImageWeights let minLabelWeight = 0.0 let maxLabelWeight = 1.0 if (!actorContext.image) { maxLabelWeight = 0.05 if (context.main.viewMode !== 'Volume') { maxLabelWeight = 1.0 minLabelWeight = 0.4 } } if (selectedIndices === null || selectedIndices === 'all') { const uniqueLabels = context.images.uniqueLabels // Update all values from the context const maxOpacity = 1.0 const haveBackground = uniqueLabels[0] === 0 ? true : false piecewiseFunction.removeAllPoints() if (haveBackground) { piecewiseFunction.addPointLong(uniqueLabels[0], 0.0, 0.5, 1.0) } else { piecewiseFunction.addPointLong( uniqueLabels[0], transformUserWeight( labelImageWeights[0], minLabelWeight, maxLabelWeight ), 0.5, 1.0 ) } for (let i = 1; i < uniqueLabels.length; i++) { piecewiseFunction.addPointLong( uniqueLabels[i], transformUserWeight( labelImageWeights[i], minLabelWeight, maxLabelWeight ), 0.5, 1.0 ) } } else { // Otherwise, just update specific values selectedIndices.forEach(value => { const weight = transformUserWeight( labelImageWeights[value], minLabelWeight, maxLabelWeight ) piecewiseFunction.setNodeValue(value, [value, weight, 0.5, 1.0]) }) } } export default updateLabelImagePiecewiseFunction ================================================ FILE: src/Rendering/VTKJS/Images/updateRenderedImage.js ================================================ import vtkITKHelper from 'vtk.js/Sources/Common/DataModel/ITKHelper' import { mat4 } from 'gl-matrix' import { fuseImages } from './fuseImages' import { computeRenderedBounds } from '../Main/computeRenderedBounds' import { worldBoundsToIndexBounds } from '../../../IO/MultiscaleSpatialImage' import componentTypeToTypedArray from '../../../IO/componentTypeToTypedArray' export const RENDERED_VOXEL_MAX = 512 * 512 * 512 * 2 const RENDERED_IMAGE_BYTES_MAX = RENDERED_VOXEL_MAX * 2 // 2 byte pixel type = 1073741824 const getVoxelCount = async (image, bounds, scale) => { const scaleInfo = image.scaleInfo[scale] if (!bounds) { return ['x', 'y', 'z'] .map(dim => scaleInfo.arrayShape.get(dim)) .reduce((voxels, dimSize) => voxels * dimSize, 1) } const indexToWorld = await image.scaleIndexToWorld(scale) const fullIndexBounds = image.getIndexBounds(scale) const indexBounds = worldBoundsToIndexBounds({ bounds, fullIndexBounds, worldToIndex: mat4.invert([], indexToWorld), }) return ['x', 'y', 'z'] .map(dim => { const [start, end] = indexBounds.get(dim) return end - start + 1 // plus 1 as bounds are inclusive }) .reduce((voxels, dimSize) => voxels * dimSize, 1) } const computeBytes = async ( { imageType: { componentType, components } }, voxelCount ) => { const bytesPerElement = componentTypeToTypedArray.get(componentType) .BYTES_PER_ELEMENT return bytesPerElement * components * voxelCount } const pickVisualized = (preComputedRanges, visualizedComponents) => visualizedComponents .map( sourceIdx => preComputedRanges[sourceIdx] ?? [0, 1] // fallback for label component ) .map(([min, max]) => ({ min, max, })) async function updateRenderedImage(context) { const name = context.actorName const actorContext = context.images.actorContext.get(name) const { image, labelImage, editorLabelImage, visualizedComponents, compare, } = actorContext if (!image && !labelImage && !editorLabelImage) { return } const compareEnabled = compare.method && compare.method !== 'disabled' const fixedImage = compareEnabled ? context.images.actorContext.get(compare.fixedImageName)?.image : undefined if (compareEnabled && !fixedImage) console.error( `Did not find image to compare with name: ${compare.fixedImageName}` ) const baseImage = fixedImage ?? image ?? labelImage const { targetScale } = context const baseImageClampedScale = Math.min(baseImage.coarsestScale, targetScale) // always load full image if least detailed scale const isCoarsestScale = baseImage.coarsestScale === baseImageClampedScale const boundsToLoad = isCoarsestScale ? undefined : computeRenderedBounds(context) const voxelCount = await getVoxelCount( baseImage, boundsToLoad, baseImageClampedScale ) if (voxelCount > RENDERED_VOXEL_MAX) throw new Error( `Voxel count over max at scale ${baseImageClampedScale}. Requested: ${voxelCount} Max: ${RENDERED_VOXEL_MAX}` ) const imageByteSize = await computeBytes(baseImage, voxelCount) if (!isCoarsestScale && imageByteSize > RENDERED_IMAGE_BYTES_MAX) throw new Error( `Image byte count over max at scale ${targetScale}. Requested: ${imageByteSize} Max: ${RENDERED_IMAGE_BYTES_MAX}` ) const [imageAtScale, labelAtScale, fixedImageAtScale] = await Promise.all( [image, labelImage, fixedImage].map(image => image?.getImage(targetScale, boundsToLoad) ) ) const imageOrLabelAtScale = imageAtScale ?? labelAtScale const preComputedRanges = baseImage?.scaleInfo[baseImageClampedScale].ranges ?? imageOrLabelAtScale?.ranges const isFuseNeeded = (labelAtScale && imageAtScale) || // fuse with label image fixedImageAtScale || Array.isArray(imageAtScale) || // is conglomerate imageOrLabelAtScale?.imageType.components !== visualizedComponents.length // more components in image than renderable const { itkImage, componentRanges } = isFuseNeeded ? await fuseImages({ imageAtScale, labelAtScale, fixedImageAtScale, visualizedComponents, compare, }) : { itkImage: imageOrLabelAtScale, componentRanges: pickVisualized( preComputedRanges, visualizedComponents ), } const vtkImage = vtkITKHelper.convertItkToVtkImage(itkImage) return { itkImage, vtkImage, labelAtScale, componentRanges, loadedScale: targetScale, name, } } export default updateRenderedImage ================================================ FILE: src/Rendering/VTKJS/Layers/layersRenderingMachineOptions.js ================================================ const layersRenderingMachineOptions = { actions: {}, } export default layersRenderingMachineOptions ================================================ FILE: src/Rendering/VTKJS/Main/applyCroppingPlanes.js ================================================ import vtkMath from 'vtk.js/Sources/Common/Core/Math' import { transformVec3 } from 'vtk.js/Sources/Widgets/Widgets3D/ImageCroppingWidget/helpers' function applyCroppingPlanes(context, event) { if (event.data) { const planes = event.data planes.forEach((plane, idx) => { context.main.widgetCroppingPlanes[idx].setOriginFrom(plane.origin) context.main.widgetCroppingPlanes[idx].setNormalFrom(plane.normal) }) // update widget if (planes.length === 6) { const worldToIndex = context.main.croppingVirtualImage.getWorldToIndex() const cropIndexes = context.main.croppingWidget .getWidgetState() .getCroppingPlanes() .getPlanes() const newCropIndexes = planes .map(({ origin }) => transformVec3(origin, worldToIndex)) .map((ijk, idx) => ijk[Math.trunc(idx / 2)]) // index is 0, 0, 1, 1, 2, 2 if (!vtkMath.areEquals(cropIndexes, newCropIndexes, 1e-8)) { context.main.croppingWidget .getWidgetState() .getCroppingPlanes() .setPlanes(newCropIndexes) } } context.service.send('RENDER') } } export default applyCroppingPlanes ================================================ FILE: src/Rendering/VTKJS/Main/applySlicingPlanes.js ================================================ function applySlicingPlanes(context, event) { const slicingPlanes = event.data const volumeRep = context.images.representationProxy if (volumeRep) { const outlineActors = context.itkVtkView.getSliceOutlineActors() if (context.use2D) { volumeRep.getActors()[0].setVisibility(false) volumeRep.getActors()[1].setVisibility(false) volumeRep.getActors()[2].setVisibility(true) outlineActors[0].setVisibility(false) outlineActors[1].setVisibility(false) outlineActors[2].setVisibility(false) return } const name = context.images.selectedName const imageVisible = context.layers.actorContext.get(name).visible if (imageVisible) { const annotations = context.main.annotationsEnabled switch (context.main.viewMode) { case 'Volume': volumeRep.setXSliceVisibility(slicingPlanes.x.visible) volumeRep.setYSliceVisibility(slicingPlanes.y.visible) volumeRep.setZSliceVisibility(slicingPlanes.z.visible) if (annotations) { outlineActors[0].setVisibility(slicingPlanes.x.visible) outlineActors[1].setVisibility(slicingPlanes.y.visible) outlineActors[2].setVisibility(slicingPlanes.z.visible) } break case 'XPlane': volumeRep.getActors()[0].setVisibility(true) volumeRep.getActors()[1].setVisibility(false) volumeRep.getActors()[2].setVisibility(false) if (annotations) { outlineActors[0].setVisibility(true) outlineActors[1].setVisibility(false) outlineActors[2].setVisibility(false) } break case 'YPlane': volumeRep.getActors()[0].setVisibility(false) volumeRep.getActors()[1].setVisibility(true) volumeRep.getActors()[2].setVisibility(false) if (annotations) { outlineActors[0].setVisibility(false) outlineActors[1].setVisibility(true) outlineActors[2].setVisibility(false) } break case 'ZPlane': volumeRep.getActors()[0].setVisibility(false) volumeRep.getActors()[1].setVisibility(false) volumeRep.getActors()[2].setVisibility(true) if (annotations) { outlineActors[0].setVisibility(false) outlineActors[1].setVisibility(false) outlineActors[2].setVisibility(true) } break } } else { volumeRep.setXSliceVisibility(false) volumeRep.setYSliceVisibility(false) volumeRep.setZSliceVisibility(false) outlineActors.forEach(a => a.setVisibility(false)) } if ( slicingPlanes.x.visible || slicingPlanes.y.visible || slicingPlanes.z.visible ) { context.itkVtkView.setViewPlanes(true) } else { context.itkVtkView.setViewPlanes(false) } if (slicingPlanes.x.scroll) { if (!context.main.xPlaneAnimation) { context.itkVtkView.getInteractor().requestAnimation('xPlaneScroll') context.main.xPlaneAnimation = context.itkVtkView .getInteractor() .onAnimation(() => { let xSlice = context.main.xSlice + slicingPlanes.x.step * slicingPlanes.x.scrollDirection if (xSlice > slicingPlanes.x.max) { xSlice = slicingPlanes.x.max slicingPlanes.x.scrollDirection *= -1 } if (xSlice < slicingPlanes.x.min) { xSlice = slicingPlanes.x.min slicingPlanes.x.scrollDirection *= -1 } context.service.send({ type: 'X_SLICE_CHANGED', data: xSlice, }) }) } } else if (context.main.xPlaneAnimation) { context.main.xPlaneAnimation.unsubscribe() context.itkVtkView.getInteractor().cancelAnimation('xPlaneScroll') context.main.xPlaneAnimation = null } if (slicingPlanes.y.scroll) { if (!context.main.yPlaneAnimation) { context.itkVtkView.getInteractor().requestAnimation('yPlaneScroll') context.main.yPlaneAnimation = context.itkVtkView .getInteractor() .onAnimation(() => { let ySlice = context.main.ySlice + slicingPlanes.y.step * slicingPlanes.y.scrollDirection if (ySlice > slicingPlanes.y.max) { ySlice = slicingPlanes.y.max slicingPlanes.y.scrollDirection *= -1 } if (ySlice < slicingPlanes.y.min) { ySlice = slicingPlanes.y.min slicingPlanes.y.scrollDirection *= -1 } context.service.send({ type: 'Y_SLICE_CHANGED', data: ySlice, }) }) } } else if (context.main.yPlaneAnimation) { context.main.yPlaneAnimation.unsubscribe() context.itkVtkView.getInteractor().cancelAnimation('yPlaneScroll') context.main.yPlaneAnimation = null } if (slicingPlanes.z.scroll) { if (!context.main.zPlaneAnimation) { context.itkVtkView.getInteractor().requestAnimation('zPlaneScroll') context.main.zPlaneAnimation = context.itkVtkView .getInteractor() .onAnimation(() => { let zSlice = context.main.zSlice + slicingPlanes.z.step * slicingPlanes.z.scrollDirection if (zSlice > slicingPlanes.z.max) { zSlice = slicingPlanes.z.max slicingPlanes.z.scrollDirection *= -1 } if (zSlice < slicingPlanes.z.min) { zSlice = slicingPlanes.z.min slicingPlanes.z.scrollDirection *= -1 } context.service.send({ type: 'Z_SLICE_CHANGED', data: zSlice, }) }) } } else if (context.main.zPlaneAnimation) { context.main.zPlaneAnimation.unsubscribe() context.itkVtkView.getInteractor().cancelAnimation('zPlaneScroll') context.main.zPlaneAnimation = null } } context.service.send('RENDER') } export default applySlicingPlanes ================================================ FILE: src/Rendering/VTKJS/Main/applyXSlice.js ================================================ function applyXSlice(context, event) { const position = event.data const volumeRep = context.images.representationProxy if (volumeRep) { volumeRep.setXSlice(Number(position)) context.service.send('RENDER') } } export default applyXSlice ================================================ FILE: src/Rendering/VTKJS/Main/applyYSlice.js ================================================ function applyYSlice(context, event) { const position = event.data const volumeRep = context.images.representationProxy if (volumeRep) { volumeRep.setYSlice(Number(position)) context.service.send('RENDER') } } export default applyYSlice ================================================ FILE: src/Rendering/VTKJS/Main/applyZSlice.js ================================================ function applyZSlice(context, event) { const position = event.data const volumeRep = context.images.representationProxy if (volumeRep) { volumeRep.setZSlice(Number(position)) context.service.send('RENDER') } } export default applyZSlice ================================================ FILE: src/Rendering/VTKJS/Main/computeRenderedBounds.js ================================================ import vtkBoundingBox from 'vtk.js/Sources/Common/DataModel/BoundingBox' const NDC_RANGE = [0, 1] // unproject NDC box const computeFrustumBoundingBox = renderer => { const view = renderer.getRenderWindow().getViews()[0] const dims = view.getViewportSize(renderer) const aspect = dims[0] / dims[1] const frustumBounds = [...vtkBoundingBox.INIT_BOUNDS] for (const x of NDC_RANGE) { for (const y of NDC_RANGE) { for (const z of NDC_RANGE) { const corner = renderer.normalizedDisplayToWorld(x, y, z, aspect) vtkBoundingBox.addPoint(frustumBounds, ...corner) } } } return frustumBounds } export const computeCroppingPlanesBoundingBox = croppingPlanes => { const planeBounds = [...vtkBoundingBox.INIT_BOUNDS] croppingPlanes.forEach(({ origin }) => vtkBoundingBox.addPoint(planeBounds, ...origin) ) return planeBounds } const intersectBoxes = (b1, b2) => b1.map( (bound, i) => i % 2 ? Math.min(bound, b2[i]) // high bound case : Math.max(bound, b2[i]) // low bound case ) export const computeRenderedBounds = ({ main: { croppingPlanes, areCroppingPlanesTouched }, itkVtkView, }) => { const frustumBox = computeFrustumBoundingBox(itkVtkView.getRenderer()) const areCroppingPlanesRelevant = croppingPlanes && croppingPlanes.length === 6 && areCroppingPlanesTouched return areCroppingPlanesRelevant ? intersectBoxes( computeCroppingPlanesBoundingBox(croppingPlanes), frustumBox ) : frustumBox } ================================================ FILE: src/Rendering/VTKJS/Main/createMainRenderer.js ================================================ // Load the rendering pieces we want to use (for both WebGL and WebGPU) import 'vtk.js/Sources/Rendering/Profiles/Geometry' import 'vtk.js/Sources/Rendering/Profiles/Glyph' import 'vtk.js/Sources/Rendering/Profiles/Volume' import { createCropping } from './croppingPlanes' function createMainRenderer(context) { createCropping(context) } export default createMainRenderer ================================================ FILE: src/Rendering/VTKJS/Main/croppingPlanes.js ================================================ import { mat4, vec3, quat, vec4 } from 'gl-matrix' import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData' import { transformVec3 } from 'vtk.js/Sources/Widgets/Widgets3D/ImageCroppingWidget/helpers' import vtkMath from 'vtk.js/Sources/Common/Core/Math' import vtkPlane from 'vtk.js/Sources/Common/DataModel/Plane' import vtkBoundingBox from 'vtk.js/Sources/Common/DataModel/BoundingBox' import toggleCroppingPlanes from './toggleCroppingPlanes' import HandlesInPixelsImageCroppingWidget from '../Widgets/HandlesInPixelsImageCroppingWidget' import { transformBounds } from '../../../transformBounds' import { arraysEqual, makeIndexToWorld } from '../../../internalUtils' export function getCropWidgetBounds(context, bounds = []) { const { croppingWidget } = context.main vtkBoundingBox.reset(bounds) croppingWidget .getWidgetState() .getStatesWithLabel('faces') .map(h => h.getOrigin()) .forEach(point => vtkBoundingBox.addPoint(bounds, ...point)) return bounds } export function getBoundsOfFullImage({ images, actorName }) { const imageActorContext = images.actorContext.get(actorName) if (!imageActorContext || imageActorContext.loadedScale === null) return [...vtkBoundingBox.INIT_BOUNDS] const { compare } = imageActorContext const compareEnabled = compare?.method !== 'disabled' const fixedImage = compareEnabled ? images.actorContext.get(compare.fixedImageName)?.image : undefined const multiScale = fixedImage ?? imageActorContext.image ?? imageActorContext.labelImage return multiScale.getWorldBounds(imageActorContext.loadedScale) } export function createCropping(context) { const croppingWidget = HandlesInPixelsImageCroppingWidget.newInstance() context.main.croppingWidget = croppingWidget context.main.widgetCroppingPlanes = Array.from({ length: 6 }, () => vtkPlane.newInstance() ) context.itkVtkView.addCroppingWidget(croppingWidget) croppingWidget .getWidgetState() .getStatesWithLabel('handles') .forEach(h => h.setScale1(22)) croppingWidget.setFaceHandlesEnabled(true) croppingWidget.setCornerHandlesEnabled(false) croppingWidget.setEdgeHandlesEnabled(false) // These are helper objects to bridge datasets in the scene, images, // geometry, and points sets, the croppingPlanes, and the // ImageCroppingWidget. // // The croppingVirtualImage is used to set the worldToIndex transform and // indexToWorld transform with imageCroppingWidget.copyImageDataDescription. // // The Direction of the virtual image is set to the Direction of the // imagesMachineContext.selectedName's Direction. // // The Origin of the virtual image is set to the lower left of the // croppingBoundingBox // // The Spacing of the virtual image is set to the spacing of the selected // image, if one exists, otherwise the extent of the croppingBoundingBox / // 1000 (is there a better approach for this?). context.main.croppingVirtualImage = vtkImageData.newInstance() const cropState = croppingWidget.getWidgetState().getCroppingPlanes() cropState.onModified(() => { const { croppingWidget } = context.main // updates bounds for camera clipping planes const widgetBounds = getCropWidgetBounds( context, croppingWidget.getWidgetState().getBounds() ) croppingWidget.placeWidget(widgetBounds) const prop = context.itkVtkView.getWidgetProp(croppingWidget) if ( prop && prop.getEnabled() && croppingWidget .getWidgetState() .getStatesWithLabel('handles') .some(h => h.getActive()) ) { const indexes = cropState.getPlanes() const indexToWorld = context.main.croppingVirtualImage.getIndexToWorld() const direction = context.main.croppingVirtualImage.getDirection() const croppingPlanes = [ { origin: Array.from( transformVec3([indexes[0], indexes[2], indexes[4]], indexToWorld) ), normal: Array.from(direction.slice(0, 3)), }, { origin: Array.from( transformVec3([indexes[1], indexes[3], indexes[5]], indexToWorld) ), normal: vtkMath.multiplyScalar(Array.from(direction.slice(0, 3)), -1), }, { origin: Array.from( transformVec3([indexes[0], indexes[2], indexes[4]], indexToWorld) ), normal: Array.from(direction.slice(3, 6)), }, { origin: Array.from( transformVec3([indexes[1], indexes[3], indexes[5]], indexToWorld) ), normal: vtkMath.multiplyScalar(Array.from(direction.slice(3, 6)), -1), }, { origin: Array.from( transformVec3([indexes[0], indexes[2], indexes[4]], indexToWorld) ), normal: Array.from(direction.slice(6, 9)), }, { origin: Array.from( transformVec3([indexes[1], indexes[3], indexes[5]], indexToWorld) ), normal: vtkMath.multiplyScalar(Array.from(direction.slice(6, 9)), -1), }, ] // Don't reset planes after user input context.main.areCroppingPlanesTouched = true context.service.send({ type: 'CROPPING_PLANES_CHANGED', data: croppingPlanes, }) context.service.send({ type: 'CROPPING_PLANES_CHANGED_BY_USER', }) } }) context.itkVtkView.setWidgetManagerInitializedCallback(() => { toggleCroppingPlanes(context) }) } export function updateCroppingParameters(context) { const { croppingVirtualImage, croppingWidget } = context.main // croppingBoundingBox is an axis-aligned bounding box that encapsulates all // objects in the scene. const croppingBoundingBox = [...vtkBoundingBox.INIT_BOUNDS] context.itkVtkView .getRepresentations() .filter(r => r.getClassName() !== 'vtkVolumeRepresentationProxy') // filter out possibly outdated images which may change in size across scales .map(r => r.getBounds()) .concat([getBoundsOfFullImage(context)]) // include latest image .forEach(bounds => { vtkBoundingBox.addBounds(croppingBoundingBox, ...bounds) }) const uninitialized = arraysEqual( croppingBoundingBox, vtkBoundingBox.INIT_BOUNDS ) if (uninitialized) return // Put global bounds in image oriented space const worldToImageDirection = makeIndexToWorld({ direction: croppingVirtualImage.getDirection(), origin: [0, 0, 0], spacing: [1, 1, 1], }) const orientedBox = transformBounds( worldToImageDirection, croppingBoundingBox ) const originWorldSpace = vec3.transformMat4( croppingVirtualImage.getOrigin(), [orientedBox[0], orientedBox[2], orientedBox[4]], worldToImageDirection ) croppingVirtualImage.setOrigin(originWorldSpace) const spacing = croppingVirtualImage.getSpacing() croppingVirtualImage.setDimensions([ (orientedBox[1] - orientedBox[0]) / spacing[0], (orientedBox[3] - orientedBox[2]) / spacing[1], (orientedBox[5] - orientedBox[4]) / spacing[2], ]) const widgetState = croppingWidget.getWidgetState() widgetState.setIndexToWorldT(...croppingVirtualImage.getIndexToWorld()) widgetState.setWorldToIndexT(...croppingVirtualImage.getWorldToIndex()) if (!context.main.areCroppingPlanesTouched) { // fit new actor if planes not changed by user context.service.send('RESET_CROPPING_PLANES') } else { // update widget transforms context.service.send({ type: 'CROPPING_PLANES_CHANGED', data: context.main.croppingPlanes, }) } } export function updateCroppingParametersFromImage(context, image) { const { croppingVirtualImage } = context.main croppingVirtualImage.setSpacing(image.getSpacing()) croppingVirtualImage.setDirection(image.getDirection()) updateCroppingParameters(context, image) } export function addCroppingPlanes(context, actor) { const { widgetCroppingPlanes } = context.main const mapper = actor.getMapper() widgetCroppingPlanes.forEach(plane => { mapper.addClippingPlane(plane) }) } export function makeCroppable(context, representationProxy) { // allows for grabbing crop handles on the other side of volume representationProxy.getVolumes().forEach(v => v.setPickable(false)) addCroppingPlanes(context, representationProxy) } ================================================ FILE: src/Rendering/VTKJS/Main/mainRenderingMachineOptions.js ================================================ import setBackgroundColor from './setBackgroundColor' import setUnits from './setUnits' import takeScreenshot from './takeScreenshot' import toggleRotate from './toggleRotate' import toggleAnnotations from './toggleAnnotations' import toggleAxes from './toggleAxes' import toggleCroppingPlanes from './toggleCroppingPlanes' import resetCroppingPlanes from './resetCroppingPlanes' import applyCroppingPlanes from './applyCroppingPlanes' import updateSlicingPlanes from './updateSlicingPlanes' import viewModeXPlane from './viewModeXPlane' import viewModeYPlane from './viewModeYPlane' import viewModeZPlane from './viewModeZPlane' import viewModeVolume from './viewModeVolume' import resetCamera from './resetCamera' import applySlicingPlanes from './applySlicingPlanes' import applyXSlice from './applyXSlice' import applyYSlice from './applyYSlice' import applyZSlice from './applyZSlice' import updateFps from './updateFps' const mainRenderingMachineOptions = { actions: { setBackgroundColor, setUnits, takeScreenshot, toggleRotate, toggleAnnotations, toggleAxes, resetCroppingPlanes, toggleCroppingPlanes, applyCroppingPlanes, updateSlicingPlanes, viewModeXPlane, viewModeYPlane, viewModeZPlane, viewModeVolume, resetCamera, applySlicingPlanes, applyXSlice, applyYSlice, applyZSlice, updateFps, }, } export default mainRenderingMachineOptions ================================================ FILE: src/Rendering/VTKJS/Main/resetCamera.js ================================================ import { getCropWidgetBounds } from './croppingPlanes' function resetCamera(context) { context.itkVtkView.resetCamera(getCropWidgetBounds(context)) } export default resetCamera ================================================ FILE: src/Rendering/VTKJS/Main/resetCroppingPlanes.js ================================================ import vtkMath from 'vtk.js/Sources/Common/Core/Math' import { transformVec3 } from 'vtk.js/Sources/Widgets/Widgets3D/ImageCroppingWidget/helpers' function resetCroppingPlanes(context) { const dims = context.main.croppingVirtualImage.getDimensions() const direction = context.main.croppingVirtualImage.getDirection() const indexToWorld = context.main.croppingVirtualImage.getIndexToWorld() const croppingPlanes = [ { origin: Array.from(transformVec3([0, 0, 0], indexToWorld)), normal: Array.from(direction.slice(0, 3)), }, { origin: Array.from( transformVec3([dims[0], dims[1], dims[2]], indexToWorld) ), normal: vtkMath.multiplyScalar(Array.from(direction.slice(0, 3)), -1), }, { origin: Array.from(transformVec3([0, 0, 0], indexToWorld)), normal: Array.from(direction.slice(3, 6)), }, { origin: Array.from( transformVec3([dims[0], dims[1], dims[2]], indexToWorld) ), normal: vtkMath.multiplyScalar(Array.from(direction.slice(3, 6)), -1), }, { origin: Array.from(transformVec3([0, 0, 0], indexToWorld)), normal: Array.from(direction.slice(6, 9)), }, { origin: Array.from( transformVec3([dims[0], dims[1], dims[2]], indexToWorld) ), normal: vtkMath.multiplyScalar(Array.from(direction.slice(6, 9)), -1), }, ] context.service.send({ type: 'CROPPING_PLANES_CHANGED', data: croppingPlanes, }) } export default resetCroppingPlanes ================================================ FILE: src/Rendering/VTKJS/Main/setBackgroundColor.js ================================================ function setBackgroundColor(context, event) { if (event.data) { context.main.backgroundColor = event.data } const backgroundColor = context.main.backgroundColor context.itkVtkView.setBackground(backgroundColor) context.service.send('RENDER') } export default setBackgroundColor ================================================ FILE: src/Rendering/VTKJS/Main/setUnits.js ================================================ function setUnits(context, event) { if (event.data) { context.main.units = event.data } context.itkVtkView.setUnits(context.main.units) } export default setUnits ================================================ FILE: src/Rendering/VTKJS/Main/takeScreenshot.js ================================================ async function takeScreenshot(context) { const proxy = context.images.representationProxy let mapper = null let imageSampleDistance = 1.0 if (proxy) { mapper = proxy.getMapper() mapper.setAutoAdjustSampleDistances(false) imageSampleDistance = mapper.getImageSampleDistance() mapper.setImageSampleDistance(0.1) } const image = new Image() const base64PNG = await context.itkVtkView.captureImage().then(imageURL => { image.src = imageURL const w = window.open('', '_blank') w.document.write(image.outerHTML) w.document.title = 'vtk.js Image Capture' window.focus() return imageURL }) context.service.send({ type: 'SCREENSHOT_TAKEN', data: base64PNG }) if (proxy) { mapper.setImageSampleDistance(imageSampleDistance) mapper.setAutoAdjustSampleDistances(true) } } export default takeScreenshot ================================================ FILE: src/Rendering/VTKJS/Main/toggleAnnotations.js ================================================ function toggleAnnotations(context) { let annotationsEnabled = context.main.annotationsEnabled context.itkVtkView.setOrientationAnnotationVisibility(annotationsEnabled) if (annotationsEnabled) { const outlineActors = context.itkVtkView.getSliceOutlineActors() const slicingPlanes = context.main.slicingPlanes switch (context.main.viewMode) { case 'Volume': outlineActors[0].setVisibility(slicingPlanes.x.visible) outlineActors[1].setVisibility(slicingPlanes.y.visible) outlineActors[2].setVisibility(slicingPlanes.z.visible) break case 'XPlane': outlineActors[0].setVisibility(true) outlineActors[1].setVisibility(false) outlineActors[2].setVisibility(false) break case 'YPlane': outlineActors[0].setVisibility(false) outlineActors[1].setVisibility(true) outlineActors[2].setVisibility(false) break case 'ZPlane': outlineActors[0].setVisibility(false) outlineActors[1].setVisibility(false) outlineActors[2].setVisibility(true) break } } else { context.itkVtkView .getSliceOutlineActors() .forEach(a => a.setVisibility(false)) } context.service.send('RENDER') } export default toggleAnnotations ================================================ FILE: src/Rendering/VTKJS/Main/toggleAxes.js ================================================ function toggleAxes(context) { context.itkVtkView.setEnableAxes(context.main.axesEnabled) context.service.send('RENDER') } export default toggleAxes ================================================ FILE: src/Rendering/VTKJS/Main/toggleCroppingPlanes.js ================================================ function toggleCroppingPlanes(context) { const enabled = context.main.croppingPlanesEnabled const prop = context.itkVtkView.getWidgetProp(context.main.croppingWidget) // Only available after the widget manager has been initialized if (prop) { prop.setEnabled(enabled) if (enabled) { context.itkVtkView.getWidgetManager().enablePicking() // Keep handles visible at all angles with fixed directional light in cinematic mode prop.getRepresentations().forEach(rep => { rep.getActors().forEach(actor => { actor.getProperty().setAmbient(1) }) }) } } context.main.croppingWidget.setVisibility(enabled) context.service.send('RENDER') } export default toggleCroppingPlanes ================================================ FILE: src/Rendering/VTKJS/Main/toggleRotate.js ================================================ function toggleRotate(context, event, actionMeta) { context.itkVtkView.setRotate(context.main.rotateEnabled) } export default toggleRotate ================================================ FILE: src/Rendering/VTKJS/Main/updateFps.js ================================================ import numericalSort from '../numericalSort' function updateFps(context, event) { const proxy = context.images.representationProxy let mapper = null if (proxy) { mapper = proxy.getMapper() mapper.setAutoAdjustSampleDistances(false) } const interactor = context.renderWindow.getInteractor() const requestId = `updateFps${performance.now().toString()}` interactor.requestAnimation(requestId) setTimeout(() => { interactor.cancelAnimation(requestId) const fps = interactor.getRecentAnimationFrameRate() if (proxy) { mapper.setAutoAdjustSampleDistances(true) } context.service.send({ type: 'FPS_UPDATED', data: fps }) }, 1100) } export default updateFps ================================================ FILE: src/Rendering/VTKJS/Main/updateSlicingPlanes.js ================================================ import { computeCroppingPlanesBoundingBox } from './computeRenderedBounds' const clampSlice = (old, fallback, { min, max }) => Math.max(min, Math.min(max, old ?? fallback)) const updateSlicingPlanes = ({ main, images: { representationProxy }, service, }) => { if (!representationProxy) return // no image loaded const { slicingPlanes, croppingPlanes } = main const savedSlicePositions = [ slicingPlanes.x.position, slicingPlanes.y.position, slicingPlanes.z.position, ] const volumeRep = representationProxy const xSliceDomain = volumeRep.getPropertyDomainByName('xSlice') const ySliceDomain = volumeRep.getPropertyDomainByName('ySlice') const zSliceDomain = volumeRep.getPropertyDomainByName('zSlice') // copy min max from loaded image data Object.assign(slicingPlanes.x, xSliceDomain) Object.assign(slicingPlanes.y, ySliceDomain) Object.assign(slicingPlanes.z, zSliceDomain) const [xMin, xMax, yMin, yMax, zMin, zMax] = computeCroppingPlanesBoundingBox( croppingPlanes ) slicingPlanes.x.min = Math.max(xMin, slicingPlanes.x.min) slicingPlanes.x.max = Math.min(xMax, slicingPlanes.x.max) slicingPlanes.y.max = Math.min(yMax, slicingPlanes.y.max) slicingPlanes.y.min = Math.max(yMin, slicingPlanes.y.min) slicingPlanes.z.min = Math.max(zMin, slicingPlanes.z.min) slicingPlanes.z.max = Math.min(zMax, slicingPlanes.z.max) service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) const xSlice = clampSlice( savedSlicePositions?.[0], volumeRep.getXSlice(), slicingPlanes.x ) service.send({ type: 'X_SLICE_CHANGED', data: xSlice }) const ySlice = clampSlice( savedSlicePositions?.[1], volumeRep.getYSlice(), slicingPlanes.y ) service.send({ type: 'Y_SLICE_CHANGED', data: ySlice }) const zSlice = clampSlice( savedSlicePositions?.[2], volumeRep.getZSlice(), slicingPlanes.z ) service.send({ type: 'Z_SLICE_CHANGED', data: zSlice }) } export default updateSlicingPlanes ================================================ FILE: src/Rendering/VTKJS/Main/viewModeVolume.js ================================================ function viewModeVolume(context) { context.itkVtkView.setViewMode('Volume') const volumeRep = context.images.representationProxy if (volumeRep) { const slicingPlanes = context.main.slicingPlanes volumeRep.setXSliceVisibility(slicingPlanes.x.visible) volumeRep.setYSliceVisibility(slicingPlanes.y.visible) volumeRep.setZSliceVisibility(slicingPlanes.z.visible) volumeRep.setVolumeVisibility(true) const annotations = context.main.annotationsEnabled const outlineActors = context.itkVtkView.getSliceOutlineActors() if (annotations) { outlineActors[0].setVisibility(slicingPlanes.x.visible) outlineActors[1].setVisibility(slicingPlanes.y.visible) outlineActors[2].setVisibility(slicingPlanes.z.visible) } context.service.send('RENDER') } } export default viewModeVolume ================================================ FILE: src/Rendering/VTKJS/Main/viewModeXPlane.js ================================================ function viewModeXPlane(context) { context.itkVtkView.setViewMode('XPlane') const volumeRep = context.images.representationProxy if (volumeRep) { volumeRep.setXSliceVisibility(true) volumeRep.setYSliceVisibility(false) volumeRep.setZSliceVisibility(false) volumeRep.setVolumeVisibility(false) const annotations = context.main.annotationsEnabled const outlineActors = context.itkVtkView.getSliceOutlineActors() if (annotations) { outlineActors[0].setVisibility(true) outlineActors[1].setVisibility(false) outlineActors[2].setVisibility(false) } context.service.send('RENDER') } } export default viewModeXPlane ================================================ FILE: src/Rendering/VTKJS/Main/viewModeYPlane.js ================================================ function viewModeYPlane(context) { context.itkVtkView.setViewMode('YPlane') const volumeRep = context.images.representationProxy if (volumeRep) { volumeRep.setXSliceVisibility(false) volumeRep.setYSliceVisibility(true) volumeRep.setZSliceVisibility(false) volumeRep.setVolumeVisibility(false) const annotations = context.main.annotationsEnabled const outlineActors = context.itkVtkView.getSliceOutlineActors() if (annotations) { outlineActors[0].setVisibility(false) outlineActors[1].setVisibility(true) outlineActors[2].setVisibility(false) } context.service.send('RENDER') } } export default viewModeYPlane ================================================ FILE: src/Rendering/VTKJS/Main/viewModeZPlane.js ================================================ function viewModeZPlane(context) { context.itkVtkView.setViewMode('ZPlane') const volumeRep = context.images.representationProxy if (volumeRep) { volumeRep.setXSliceVisibility(false) volumeRep.setYSliceVisibility(false) volumeRep.setZSliceVisibility(true) volumeRep.setVolumeVisibility(false) const annotations = context.main.annotationsEnabled const outlineActors = context.itkVtkView.getSliceOutlineActors() if (annotations) { outlineActors[0].setVisibility(false) outlineActors[1].setVisibility(false) outlineActors[2].setVisibility(true) } context.service.send('RENDER') } } export default viewModeZPlane ================================================ FILE: src/Rendering/VTKJS/Widgets/DistanceWidget/DistanceWidget.js ================================================ import macro from 'vtk.js/Sources/macros' import vtkLineWidget from 'vtk.js/Sources/Widgets/Widgets3D/LineWidget' import vtkLineWidgetBehavior from 'vtk.js/Sources/Widgets/Widgets3D/LineWidget/behavior' import stateGenerator from 'vtk.js/Sources/Widgets/Widgets3D/LineWidget/state' // ---------------------------------------------------------------------------- // Factory // ---------------------------------------------------------------------------- function DistanceWidget(publicAPI, model) { model.classHierarchy.push('DistanceWidget') // --- Widget Requirement --------------------------------------------------- model.methodsToLink = [ ...(model.methodsToLink ?? []), 'circleProps', 'lineProps', 'textProps', 'text', 'textStateIndex', ] model.behavior = vtkLineWidgetBehavior model.widgetState = stateGenerator() } // ---------------------------------------------------------------------------- const DEFAULT_VALUES = {} // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues) vtkLineWidget.extend(publicAPI, model, { ...initialValues, useCameraFocalPoint: true, }) DistanceWidget(publicAPI, model) } // ---------------------------------------------------------------------------- export const newInstance = macro.newInstance(extend, 'DistanceWidget') // ---------------------------------------------------------------------------- export default { newInstance, extend } ================================================ FILE: src/Rendering/VTKJS/Widgets/DistanceWidget/state.js ================================================ import vtkStateBuilder from 'vtk.js/Sources/Widgets/Core/StateBuilder' export default function generateState() { return vtkStateBuilder .createBuilder() .addStateFromMixin({ labels: ['moveHandle'], mixins: ['origin', 'color', 'scale1', 'visible', 'manipulator'], name: 'moveHandle', initialValues: { scale1: 20, visible: false, }, }) .addDynamicMixinState({ labels: ['handles'], mixins: ['origin', 'color', 'scale1', 'visible', 'manipulator'], name: 'handle', initialValues: { scale1: 20, }, }) .build() } ================================================ FILE: src/Rendering/VTKJS/Widgets/HandlesInPixelsImageCroppingWidget.js ================================================ import macro from 'vtk.js/Sources/macros' import vtkImageCroppingWidget from 'vtk.js/Sources/Widgets/Widgets3D/ImageCroppingWidget' import vtkSphereHandleRepresentation from 'vtk.js/Sources/Widgets/Representations/SphereHandleRepresentation' import vtkCroppingOutlineRepresentation from 'vtk.js/Sources/Widgets/Representations/CroppingOutlineRepresentation' import { ViewTypes } from 'vtk.js/Sources/Widgets/Core/WidgetManager/Constants' // ---------------------------------------------------------------------------- // Factory // ---------------------------------------------------------------------------- function HandlesInPixelsImageCroppingWidget(publicAPI, model) { model.classHierarchy.push('HandlesInPixelsImageCroppingWidget') publicAPI.getRepresentationsForViewType = viewType => { switch (viewType) { case ViewTypes.DEFAULT: case ViewTypes.GEOMETRY: case ViewTypes.SLICE: case ViewTypes.VOLUME: default: return [ // Describes constructing a vtkSphereHandleRepresentation, and every // time the widget state updates, we will give the representation // a list of all handle states (which have the label "handles"). { builder: vtkSphereHandleRepresentation, labels: ['handles'], initialValues: { scaleInPixels: true, }, }, { builder: vtkCroppingOutlineRepresentation, // outline is defined by corner points labels: ['corners'], }, ] } } } // ---------------------------------------------------------------------------- const DEFAULT_VALUES = {} // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues) vtkImageCroppingWidget.extend(publicAPI, model, initialValues) HandlesInPixelsImageCroppingWidget(publicAPI, model) } // ---------------------------------------------------------------------------- export const newInstance = macro.newInstance( extend, 'HandlesInPixelsImageCroppingWidget' ) // ---------------------------------------------------------------------------- export default { newInstance, extend } ================================================ FILE: src/Rendering/VTKJS/Widgets/createWidgets.js ================================================ function createWidgets(/*context, events*/) {} export default createWidgets ================================================ FILE: src/Rendering/VTKJS/Widgets/toggleDistanceWidget.js ================================================ import DistanceWidget from './DistanceWidget/DistanceWidget' let valueChangedSubscription function toggleDistanceWidget(context) { const { widgets: { distanceWidget }, } = context const widgetManager = context.itkVtkView.getWidgetManager() if (context.widgets.distanceEnabled) { const distanceWidget = DistanceWidget.newInstance() context.widgets.distanceWidget = distanceWidget widgetManager.addWidget(distanceWidget) valueChangedSubscription = distanceWidget .getWidgetState() .onModified(() => { context.service.send({ type: 'DISTANCE_WIDGET_VALUE_CHANGED', data: distanceWidget.getDistance().toFixed(3), }) }) widgetManager.grabFocus(distanceWidget) // image loaded, not just geometry? if (context.images.representationProxy) { // Avoid appearing under the x slice const firstSlice = context.images.representationProxy.getActors()[0] const xCoord = firstSlice.getBoundsForSlice()[0] distanceWidget.getManipulator().setHandleOrigin([xCoord, 0, 0]) } } else { valueChangedSubscription.unsubscribe() widgetManager.removeWidget(distanceWidget) distanceWidget.delete() context.service.send({ type: 'DISTANCE_WIDGET_VALUE_CHANGED', data: 0, }) } context.service.send('RENDER') } export default toggleDistanceWidget ================================================ FILE: src/Rendering/VTKJS/Widgets/widgetsRenderingMachineOptions.js ================================================ import createWidgets from './createWidgets' import toggleDistanceWidget from './toggleDistanceWidget' const widgetsRenderingMachineOptions = { actions: { createWidgets, toggleDistanceWidget, }, } export default widgetsRenderingMachineOptions ================================================ FILE: src/Rendering/VTKJS/cancelAnimation.js ================================================ function cancelAnimation(context, event) { const identifier = event.data context.renderWindow.getInteractor().cancelAnimation(identifier) } export default cancelAnimation ================================================ FILE: src/Rendering/VTKJS/createRenderer.js ================================================ import vtkGestureCameraManipulator from 'vtk.js/Sources/Interaction/Manipulators/GestureCameraManipulator' import createMainRenderer from './Main/createMainRenderer' // Load the rendering pieces we want to use (for both WebGL and WebGPU) import 'vtk.js/Sources/Rendering/Profiles/Geometry' import 'vtk.js/Sources/Rendering/Profiles/Glyph' import 'vtk.js/Sources/Rendering/Profiles/Volume' function createRenderer(context) { context.itkVtkView.setContainer(context.renderingViewContainers.get('volume')) context.itkVtkView.setXyLowerLeft(context.xyLowerLeft) createMainRenderer(context) const interactor = context.itkVtkView.getInteractor() interactor.onRenderEvent(() => context.service.send('POST_RENDER')) const gestureManipulator = vtkGestureCameraManipulator.newInstance({ pinchEnabled: true, rotateEnabled: true, panEnabled: true, }) context.itkVtkView .getInteractorStyle2D() .addGestureManipulator(gestureManipulator) context.itkVtkView .getInteractorStyle3D() .addGestureManipulator(gestureManipulator) } export default createRenderer ================================================ FILE: src/Rendering/VTKJS/numericalSort.js ================================================ function numericalSort(eltA, eltB) { if (eltA < eltB) { return -1 } else if (eltB < eltA) { return 1 } return 0 } export default numericalSort ================================================ FILE: src/Rendering/VTKJS/proxyManagerConfiguration.js ================================================ import vtkSourceProxy from 'vtk.js/Sources/Proxy/Core/SourceProxy' import vtkGeometryRepresentationProxy from 'vtk.js/Sources/Proxy/Representations/GeometryRepresentationProxy' import vtkVolumeRepresentationProxy from 'vtk.js/Sources/Proxy/Representations/VolumeRepresentationProxy' import vtkSliceRepresentationProxy from 'vtk.js/Sources/Proxy/Representations/SliceRepresentationProxy' import vtkLookupTableProxy from 'vtk.js/Sources/Proxy/Core/LookupTableProxy' import vtkPiecewiseFunctionProxy from 'vtk.js/Sources/Proxy/Core/PiecewiseFunctionProxy' import vtkPointSetRepresentationProxy from './vtk/PointSetRepresentationProxy' import ItkVtkView from './vtk/ItkVtkViewProxy' const commonInteractor = [ { type: 'pan', options: { button: 3 } }, // Pan on Right button drag { type: 'pan', options: { button: 1, shift: true } }, // Pan on Shift + Left button drag { type: 'zoom', options: { button: 1, control: true } }, // Zoom on Ctrl + Left button drag { type: 'zoom', options: { dragEnabled: false, scrollEnabled: true } }, // Zoom on scroll ] const interactorStyle3D = commonInteractor.concat([ { type: 'rotate', options: { button: 1 } }, // Rotate on Left button drag ]) const interactorStyle2D = commonInteractor.concat([ { type: 'pan', options: { button: 1 } }, // Pan on Left button drag ]) const proxyManagerConfiguration = { definitions: { Proxy: { LookupTable: { class: vtkLookupTableProxy, }, PiecewiseFunction: { class: vtkPiecewiseFunctionProxy, }, }, Sources: { TrivialProducer: { class: vtkSourceProxy, options: {}, }, }, Representations: { Geometry: { class: vtkGeometryRepresentationProxy, options: {}, }, Slice: { class: vtkSliceRepresentationProxy, options: {}, }, Volume: { class: vtkVolumeRepresentationProxy, options: {}, }, PointSet: { class: vtkPointSetRepresentationProxy, options: {}, }, }, Views: { ItkVtkView: { class: ItkVtkView, options: { axis: 1, // Y orientation: -1, // Y- (A) viewUp: [0, 0, 1], // Z+ (S) useParallelRendering: false, }, props: { presetToInteractor3D: interactorStyle3D, presetToInteractor2D: interactorStyle2D, }, }, }, }, representations: { ItkVtkView: { vtkPolyData: { name: 'Geometry' }, vtkImageData: { name: 'Volume' }, }, }, } export default proxyManagerConfiguration ================================================ FILE: src/Rendering/VTKJS/render.js ================================================ function render(context) { if (!context.renderWindow.getInteractor().isAnimating()) { context.renderWindow.render() } } export default render ================================================ FILE: src/Rendering/VTKJS/requestAnimation.js ================================================ function requestAnimation(context, event) { const identifier = event.data context.renderWindow.getInteractor().requestAnimation(identifier) } export default requestAnimation ================================================ FILE: src/Rendering/VTKJS/vtk/AxesLabelsWidget/behavior.js ================================================ export default function widgetBehavior(publicAPI, model) { model.classHierarchy.push('vtkAxesLabelsWidgetProp') } ================================================ FILE: src/Rendering/VTKJS/vtk/AxesLabelsWidget/index.js ================================================ import macro from 'vtk.js/Sources/macros' import vtkAbstractWidgetFactory from 'vtk.js/Sources/Widgets/Core/AbstractWidgetFactory' import vtkPolyLineRepresentation from 'vtk.js/Sources/Widgets/Representations/PolyLineRepresentation' import vtkSVGMarkerTextRepresentation from '../SVGMarkerTextRepresentation' import widgetBehavior from './behavior' import stateGenerator from './state' import { ViewTypes } from 'vtk.js/Sources/Widgets/Core/WidgetManager/Constants' // ---------------------------------------------------------------------------- // Factory // ---------------------------------------------------------------------------- function vtkAxesLabelsWidget(publicAPI, model) { model.classHierarchy.push('vtkAxesLabelsWidget') // --- Widget Requirement --------------------------------------------------- model.methodsToLink = ['circleProps', 'textProps', 'closePolyLine'] model.behavior = widgetBehavior model.widgetState = stateGenerator() publicAPI.getRepresentationsForViewType = viewType => { switch (viewType) { case ViewTypes.DEFAULT: case ViewTypes.GEOMETRY: case ViewTypes.SLICE: case ViewTypes.VOLUME: default: return [ { builder: vtkSVGMarkerTextRepresentation, labels: ['handles'] }, { builder: vtkPolyLineRepresentation, labels: ['handles'], }, ] } } // -------------------------------------------------------------------------- // initialization // -------------------------------------------------------------------------- model.widgetState.onBoundsChange(bounds => { const center = [ (bounds[0] + bounds[1]) * 0.5, (bounds[2] + bounds[3]) * 0.5, (bounds[4] + bounds[5]) * 0.5, ] }) } // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { manipulator: null, } // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues) vtkAbstractWidgetFactory.extend(publicAPI, model, initialValues) vtkAxesLabelsWidget(publicAPI, model) } // ---------------------------------------------------------------------------- export const newInstance = macro.newInstance(extend, 'vtkAxesLabelsWidget') // ---------------------------------------------------------------------------- export default { newInstance, extend } ================================================ FILE: src/Rendering/VTKJS/vtk/AxesLabelsWidget/state.js ================================================ import vtkStateBuilder from 'vtk.js/Sources/Widgets/Core/StateBuilder' export default function generateState() { return vtkStateBuilder .createBuilder() .addDynamicMixinState({ labels: ['handles'], mixins: ['origin', 'color', 'text'], name: 'handle', initialValues: { origin: [-1, -1, -1], }, }) .build() } ================================================ FILE: src/Rendering/VTKJS/vtk/ItkVtkViewProxy.js ================================================ import macro from 'vtk.js/Sources/macros' import vtkViewProxy from 'vtk.js/Sources/Proxy/Core/ViewProxy' import vtkPointPicker from 'vtk.js/Sources/Rendering/Core/PointPicker' import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor' import vtkCubeSource from 'vtk.js/Sources/Filters/Sources/CubeSource' import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper' import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate' import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData' import vtkBoundingBox from 'vtk.js/Sources/Common/DataModel/BoundingBox' import vtkAxesLabelsWidget from './AxesLabelsWidget' import WidgetManagerPickWhileAnimating from './WidgetManagerPickWhileAnimating' import vtkSliceOutlineFilter from './SliceOutlineFilter' import vtkPoints from 'vtk.js/Sources/Common/Core/Points' import vtkCellArray from 'vtk.js/Sources/Common/Core/CellArray' export const VOLUME_DIFFUSE_DEFAULT = 1.0 export const VOLUME_AMBIENT_DEFAULT = 0.4 const CursorCornerAnnotation = '
Index:${iIndex},${jIndex},${kIndex}
Position:${xPosition},${yPosition},${zPosition}
Value:${value}
Label:${annotation}
' const { vtkErrorMacro } = macro function numberToText(number, precision) { let text = Number.parseFloat(number).toPrecision(precision) if (number > 1) { text = Number.parseInt(Number.parseFloat(text)) } return text } // ---------------------------------------------------------------------------- // ItkVtkViewProxy methods // ---------------------------------------------------------------------------- function ItkVtkViewProxy(publicAPI, model) { // Set our className model.classHierarchy.push('ItkVtkViewProxy') // Private -------------------------------------------------------------------- // function updateAxesVisibility() { if (!model.axesOriginWidget) { return } if (!model.enableAxes) { model.axesGridActor.setVisibility(false) model.axesOriginWidget.setVisibility(false) model.axesXWidget.setVisibility(false) model.axesXActor.setVisibility(false) model.axesYWidget.setVisibility(false) model.axesYActor.setVisibility(false) model.axesZWidget.setVisibility(false) model.axesZActor.setVisibility(false) return } model.axesOriginWidget.setVisibility(true) switch (model.viewMode) { case 'XPlane': model.axesGridActor.setVisibility(false) model.axesOriginHandle.setText(model.axesOriginXText) model.axesYHandle.setText(model.axesYXText) model.axesZHandle.setText(model.axesZXText) model.axesXWidget.setVisibility(false) model.axesXActor.setVisibility(false) model.axesYWidget.setVisibility(true) model.axesYActor.setVisibility(true) model.axesZWidget.setVisibility(true) model.axesZActor.setVisibility(true) break case 'YPlane': model.axesGridActor.setVisibility(false) model.axesOriginHandle.setText(model.axesOriginYText) model.axesXHandle.setText(model.axesXYText) model.axesZHandle.setText(model.axesZYText) model.axesXWidget.setVisibility(true) model.axesXActor.setVisibility(true) model.axesYWidget.setVisibility(false) model.axesYActor.setVisibility(false) model.axesZWidget.setVisibility(true) model.axesZActor.setVisibility(true) break case 'ZPlane': model.axesGridActor.setVisibility(false) model.axesOriginHandle.setText(model.axesOriginZText) model.axesXHandle.setText(model.axesXZText) model.axesYHandle.setText(model.axesYZText) model.axesXWidget.setVisibility(true) model.axesXActor.setVisibility(true) model.axesYWidget.setVisibility(true) model.axesYActor.setVisibility(true) model.axesZWidget.setVisibility(false) model.axesZActor.setVisibility(false) break case 'Volume': model.axesGridActor.setVisibility(true) model.axesOriginHandle.setText(model.axesOriginVText) model.axesXHandle.setText(model.axesXVText) model.axesYHandle.setText(model.axesYVText) model.axesZHandle.setText(model.axesZVText) model.axesXWidget.setVisibility(true) model.axesXActor.setVisibility(true) model.axesYWidget.setVisibility(true) model.axesYActor.setVisibility(true) model.axesZWidget.setVisibility(true) model.axesZActor.setVisibility(true) break default: vtkErrorMacro('Unexpected view mode') } } function setVisualizationMode(axisIndex) { if (model.enableAxes) { updateAxesVisibility() } if (axisIndex === -1) { // volume rendering model.interactor.setInteractorStyle(model.interactorStyle3D) if (model.rotate && !model.rotateAnimationCallback) { model.rotateAnimationCallback = model.interactor.onAnimation( rotateAzimuth ) model.interactor.requestAnimation('itk-vtk-view-rotate') } if (model.volumeCameraState) { model.camera.setFocalPoint(...model.volumeCameraState.focalPoint) model.camera.setPosition(...model.volumeCameraState.position) model.camera.setViewUp(...model.volumeCameraState.viewUp) model.camera.setViewAngle(model.volumeCameraState.viewAngle) model.camera.setParallelScale(model.volumeCameraState.parallelScale) model.camera.setPhysicalTranslation( ...model.volumeCameraState.physicalTranslation ) } model.camera.setParallelProjection(false) if (model.volumeRepresentation) { if (model.viewPlanes) { publicAPI.setCornerAnnotation('ne', model.neCornerAnnotation) } else { publicAPI.setCornerAnnotation('ne', '') } if (model.imageVisibility) { model.volumeRepresentation.setVolumeVisibility(true) } } model.croppingWidget.setFaceHandlesEnabled(true) model.croppingWidget.setCornerHandlesEnabled(false) } else { // slice views model.camera.setParallelProjection(true) publicAPI.setCornerAnnotation('ne', model.neCornerAnnotation) model.interactor.setInteractorStyle(model.interactorStyle2D) if (model.rotate && !!model.rotateAnimationCallback) { model.interactor.cancelAnimation('itk-vtk-view-rotate') model.rotateAnimationCallback.unsubscribe() model.rotateAnimationCallback = null } if (model.volumeRepresentation) { model.volumeRepresentation.setVolumeVisibility(false) model.volumeRepresentation.getActors().forEach((actor, index) => { if (index === axisIndex) { model.imageVisibility && actor.setVisibility(true) } else { actor.setVisibility(false) } }) } // Disable to avoid Warning: Resetting view-up since view plane normal is parallel const previousState = model.orientationWidget.getEnabled() model.orientationWidget.setEnabled(false) switch (axisIndex) { case 0: publicAPI.updateOrientation(0, 1, [0, 0, 1]) break case 1: publicAPI.updateOrientation(1, -1, [0, 0, 1]) break case 2: if (model.xyLowerLeft) { publicAPI.updateOrientation(2, 1, [0, 1, 0]) } else { publicAPI.updateOrientation(2, -1, [0, -1, 0]) } break default: vtkErrorMacro('Unexpected view mode') } model.orientationWidget.setEnabled(previousState) model.croppingWidget.setFaceHandlesEnabled(false) model.croppingWidget.setCornerHandlesEnabled(true) } } function getAnnotationText(value) { const labelValue = value[model.labelIndex] if (model.labelNames !== null && model.labelNames.has(labelValue)) { return model.labelNames.get(labelValue) } return labelValue } function getAnnotationLabelStyle() { return model.labelIndex === null ? 'style="display: none;"' : '' } function updateAnnotations(callData) { const renderPosition = callData.position model.annotationPicker.pick( [renderPosition.x, renderPosition.y, 0.0], callData.pokedRenderer ) const ijk = model.annotationPicker.getPointIJK() if (model.volumeRepresentation) { publicAPI.setCornerAnnotation('ne', model.neCornerAnnotation) const imageData = model.volumeRepresentation.getInputDataSet() const size = imageData.getDimensions() const scalarData = imageData.getPointData().getScalars() const fusedValue = scalarData.getTuple( size[0] * size[1] * ijk[2] + size[0] * ijk[1] + ijk[0] ) const annotation = getAnnotationText(fusedValue) const worldPositions = model.annotationPicker.getPickedPositions() if (ijk.length > 0 && worldPositions.length > 0) { const worldPosition = worldPositions[0] model.dataProbeCubeSource.setCenter(worldPosition) model.dataProbeActor.setVisibility(true) model.dataProbeFrameActor.setVisibility(true) model.lastPickedValues = { iIndex: ijk[0], jIndex: ijk[1], kIndex: ijk[2], xPosition: Number.parseFloat(worldPosition[0]).toPrecision(4), yPosition: Number.parseFloat(worldPosition[1]).toPrecision(4), zPosition: Number.parseFloat(worldPosition[2]).toPrecision(4), value: model.labelIndex === null ? fusedValue : fusedValue.slice(0, model.labelIndex), label: model.labelIndex === null ? null : fusedValue[model.labelIndex], annotation, annotationLabelStyle: getAnnotationLabelStyle(), } publicAPI.updateCornerAnnotation(model.lastPickedValues) } else { publicAPI.setCornerAnnotation('ne', '') model.dataProbeActor.setVisibility(false) model.dataProbeFrameActor.setVisibility(false) model.lastPickedValues = null } } else { model.lastPickedValues = null } } function updateAxes() { vtkBoundingBox.reset(model.axesBoundingBox) model.representations.forEach(representation => { vtkBoundingBox.addBounds( model.axesBoundingBox, ...representation.getBounds() ) }) const minPoint = vtkBoundingBox.getMinPoint(model.axesBoundingBox) const maxPoint = vtkBoundingBox.getMaxPoint(model.axesBoundingBox) const axisTicks = model.numberOfAxisTicks const xDelta = (maxPoint[0] - minPoint[0]) / (axisTicks - 1) const yDelta = (maxPoint[1] - minPoint[1]) / (axisTicks - 1) const zDelta = (maxPoint[2] - minPoint[2]) / (axisTicks - 1) const axesPoints = new Float32Array(axisTicks * axisTicks * 3 * 3) let offset = 0 // x-y plane for (let i = 0; i < axisTicks; i++) { for (let j = 0; j < axisTicks; j++) { axesPoints[offset] = minPoint[0] + i * xDelta axesPoints[offset + 1] = minPoint[1] + j * yDelta axesPoints[offset + 2] = minPoint[2] offset += 3 } } // y-z plane for (let i = 0; i < axisTicks; i++) { for (let j = 0; j < axisTicks; j++) { axesPoints[offset] = minPoint[0] axesPoints[offset + 1] = minPoint[1] + i * yDelta axesPoints[offset + 2] = minPoint[2] + j * zDelta offset += 3 } } // x-z plane for (let i = 0; i < axisTicks; i++) { for (let j = 0; j < axisTicks; j++) { axesPoints[offset] = minPoint[0] + i * xDelta axesPoints[offset + 1] = minPoint[1] axesPoints[offset + 2] = minPoint[2] + j * zDelta offset += 3 } } function addLines(linesArray, offset, axisTicks) { for (let i = 0; i < axisTicks - 1; i++) { for (let j = 0; j < axisTicks - 1; j++) { const start = i * axisTicks + j + offset linesArray.push( 5, start, start + 1, (i + 1) * axisTicks + j + 1 + offset, (i + 1) * axisTicks + j + offset, start ) } } return linesArray } const axesLines = [] // x-y plane offset = 0 addLines(axesLines, offset, axisTicks) // y-z plane offset += axisTicks * axisTicks addLines(axesLines, offset, axisTicks) // x-z plane offset += axisTicks * axisTicks addLines(axesLines, offset, axisTicks) const verts = new Uint32Array(axesPoints.length) verts.fill(1) for (let i = 0; i < axesPoints.length; i++) { verts[i * 2 + 1] = i } model.axesPolyData.getPoints().setData(axesPoints, 3) model.axesPolyData.getVerts().setData(verts) model.axesPolyData.getLines().setData(new Uint32Array(axesLines)) const axesNames = Object.fromEntries( ['x', 'y', 'z'].map(axis => [ axis, model.axesNames?.get(axis) ?? axis.toUpperCase(), ]) ) const minPointText = minPoint.map(point => numberToText(point, 2)) const maxPointText = maxPoint.map(point => numberToText(point, 2)) model.axesOriginHandle.setOrigin(minPoint) model.axesOriginXText = `Origin: ${minPointText[1]}, ${minPointText[2]}` model.axesOriginYText = `Origin: ${minPointText[0]}, ${minPointText[2]}` model.axesOriginZText = `Origin: ${minPointText[0]}, ${minPointText[1]}` model.axesOriginVText = `Origin: ${minPointText[0]}, ${minPointText[1]}, ${minPointText[2]}` model.axesOriginHandle.setText(model.axesOriginVText) model.axesXOriginHandle.setOrigin(minPoint) model.axesXOriginHandle.setText('') model.axesXHandle.setOrigin([maxPoint[0], minPoint[1], minPoint[2]]) model.axesXYText = `${axesNames.x}: ${maxPointText[0]}, ${axesNames.z}: ${minPointText[2]}` model.axesXZText = `${axesNames.x}: ${maxPointText[0]}, ${axesNames.y}: ${minPointText[1]}` model.axesXVText = `${axesNames.x}: ${maxPointText[0]}, ${minPointText[1]}, ${minPointText[2]}` model.axesXHandle.setText(model.axesXVText) model.axesYOriginHandle.setOrigin(minPoint) model.axesYOriginHandle.setText('') model.axesYHandle.setOrigin([minPoint[0], maxPoint[1], minPoint[2]]) model.axesYXText = `${axesNames.y}: ${maxPointText[1]}, ${axesNames.z}: ${minPointText[2]}` model.axesYZText = `${axesNames.x}: ${minPointText[0]}, ${axesNames.y}: ${maxPointText[1]}` model.axesYVText = `${axesNames.y}: ${minPointText[0]}, ${maxPointText[1]}, ${minPointText[2]}` model.axesYHandle.setText(model.axesYVText) model.axesZOriginHandle.setOrigin(minPoint) model.axesZOriginHandle.setText('') model.axesZHandle.setOrigin([minPoint[0], minPoint[1], maxPoint[2]]) model.axesZXText = `${axesNames.y}: ${minPointText[1]}, ${axesNames.z}: ${maxPointText[2]}` model.axesZYText = `${axesNames.x}: ${minPointText[0]}, ${axesNames.z}: ${maxPointText[2]}` model.axesZVText = `${axesNames.z}: ${minPointText[0]}, ${minPointText[1]}, ${maxPointText[2]}` model.axesZHandle.setText(model.axesZVText) } publicAPI.setAxesNames = axes => { model.axesNames = axes updateAxes() } // Setup -------------------------------------------------------------------- publicAPI.setCornerAnnotation('ne', '') publicAPI.updateCornerAnnotation({ iIndex: ' N/A', jIndex: ' N/A', kIndex: ' N/A', xPosition: ' N/A', yPosition: ' N/A', zPosition: ' N/A', value: 'N/A ', annotation: 'N/A ', annotationLabelStyle: getAnnotationLabelStyle(), }) publicAPI.setAnnotationOpacity(0.0) model.annotationPicker = vtkPointPicker.newInstance() model.annotationPicker.setPickFromList(1) model.annotationPicker.initializePickList() model.interactor.onLeftButtonPress(() => { if (model.clickCallback && model.lastPickedValues) { model.clickCallback(model.lastPickedValues) } }) model.interactor.onMouseMove(event => { updateAnnotations(event) }) model.interactor.onStartMouseMove(() => { if (model.viewMode !== 'Volume' || model.viewPlanes) { publicAPI.getInteractor().requestAnimation('annotationMouseMove') } }) model.interactor.onEndMouseMove(() => { if (model.viewMode !== 'Volume' || model.viewPlanes) { publicAPI.getInteractor().cancelAnimation('annotationMouseMove') } }) model.interactor.onEndMouseWheel(() => { updateDataProbeSize() }) // use the same color map in the planes // colormap changes with window / level // window / level changes piecewise =jk model.dataProbeCubeSource = vtkCubeSource.newInstance() model.dataProbeMapper = vtkMapper.newInstance() model.dataProbeMapper.setInputConnection( model.dataProbeCubeSource.getOutputPort() ) model.dataProbeActor = vtkActor.newInstance() model.dataProbeActor.setMapper(model.dataProbeMapper) model.dataProbeFrameActor = vtkActor.newInstance() model.dataProbeFrameActor.setMapper(model.dataProbeMapper) model.renderer.addActor(model.dataProbeActor) const dataProbeProperty = model.dataProbeActor.getProperty() dataProbeProperty.setLighting(false) dataProbeProperty.setColor(1.0, 1.0, 1.0) const dataProbeFrameProperty = model.dataProbeFrameActor.getProperty() dataProbeFrameProperty.setRepresentation(1) dataProbeFrameProperty.setColor(0.0, 0.0, 0.0) model.renderer.addActor(model.dataProbeFrameActor) model.dataProbeActor.setVisibility(false) model.dataProbeFrameActor.setVisibility(false) model.dataProbeActor.setPickable(false) model.dataProbeFrameActor.setPickable(false) function updateDataProbeSize() { if (model.volumeRepresentation) { const image = model.volumeRepresentation.getInputDataSet() const spacing = image.getSpacing() let viewableScale = null if (model.camera.getParallelProjection()) { viewableScale = model.camera.getParallelScale() / 40 } else { const distance = model.camera.getDistance() // Heuristic assuming a constant view angle viewableScale = distance / 150 } model.dataProbeCubeSource.setXLength(Math.max(spacing[0], viewableScale)) model.dataProbeCubeSource.setYLength(Math.max(spacing[1], viewableScale)) model.dataProbeCubeSource.setZLength(Math.max(spacing[2], viewableScale)) } } model.camera.pitch(-30.0) model.camera.azimuth(30.0) model.xSliceOutliner = vtkSliceOutlineFilter.newInstance() model.xSliceMapper = vtkMapper.newInstance() model.xSliceMapper.setInputConnection(model.xSliceOutliner.getOutputPort()) model.xSliceActor = vtkActor.newInstance() model.xSliceActor.getProperty().setColor([0.94, 0.32, 0.31]) model.xSliceActor.setMapper(model.xSliceMapper) model.xSliceActor.setVisibility(false) model.ySliceOutliner = vtkSliceOutlineFilter.newInstance() model.ySliceMapper = vtkMapper.newInstance() model.ySliceMapper.setInputConnection(model.ySliceOutliner.getOutputPort()) model.ySliceActor = vtkActor.newInstance() model.ySliceActor.getProperty().setColor([0.99, 0.84, 0.21]) model.ySliceActor.setMapper(model.ySliceMapper) model.ySliceActor.setVisibility(false) model.zSliceOutliner = vtkSliceOutlineFilter.newInstance() model.zSliceMapper = vtkMapper.newInstance() model.zSliceMapper.setInputConnection(model.zSliceOutliner.getOutputPort()) model.zSliceActor = vtkActor.newInstance() model.zSliceActor.getProperty().setColor([0.3, 0.67, 0.31]) model.zSliceActor.setMapper(model.zSliceMapper) model.zSliceActor.setVisibility(false) publicAPI.getSliceOutlineActors = () => { return [model.xSliceActor, model.ySliceActor, model.zSliceActor] } publicAPI.getSliceOutlineFilters = () => { return [model.zSliceOutliner, model.zSliceOutliner, model.zSliceOutliner] } // Must be called before the initial render. publicAPI.addWidgetToRegister = widget => { model.widgetsToRegister.push(widget) } publicAPI.addCroppingWidget = widget => { model.croppingWidget = widget publicAPI.addWidgetToRegister(widget) } publicAPI.getWidgetProp = widget => { return model.widgetProps.get(widget) } model.orientationWidget.setViewportSize(0.1) const superRenderLater = publicAPI.renderLater publicAPI.renderLater = () => { superRenderLater() if (!model.widgetManagerInitialized) { // Needs to come after initial render model.widgetManager.setRenderer(model.renderer) model.widgetManager.disablePicking() model.widgetsToRegister.forEach(widget => { model.widgetProps.set(widget, model.widgetManager.addWidget(widget)) }) model.axesOriginWidget = model.widgetProps.get(model.axesOriginLabel) model.axesXWidget = model.widgetProps.get(model.axesXLabels) model.axesYWidget = model.widgetProps.get(model.axesYLabels) model.axesZWidget = model.widgetProps.get(model.axesZLabels) const color = model.axesGridActor.getProperty().getColor()[0] === 0.0 ? 'black' : 'white' model.axesOriginWidget.setCircleProps({ r: model.axesCircleRadius, stroke: color, fill: color, }) model.axesOriginWidget.setTextProps({ fill: color, dx: -10 * model.axesTextOffset, dy: 2 * model.axesTextOffset, }) model.axesXWidget.setCircleProps({ r: model.axesCircleRadius, stroke: color, fill: color, }) model.axesXWidget.setTextProps({ fill: color, dx: model.axesTextOffset, dy: 2 * model.axesTextOffset, }) model.axesYWidget.setCircleProps({ r: model.axesCircleRadius, stroke: color, fill: color, }) model.axesYWidget.setTextProps({ fill: color, dx: model.axesTextOffset, dy: 2 * model.axesTextOffset, }) model.axesZWidget.setCircleProps({ r: model.axesCircleRadius, stroke: color, fill: color, }) model.axesZWidget.setTextProps({ fill: color, dx: model.axesTextOffset, dy: -1 * model.axesTextOffset, }) let widgetState = model.axesOriginLabel.getWidgetState() model.axesOriginHandle = widgetState.addHandle() widgetState = model.axesXLabels.getWidgetState() model.axesXOriginHandle = widgetState.addHandle() model.axesXHandle = widgetState.addHandle() model.axesXActor = model.axesXWidget .getRepresentations()[1] .getActors()[0] const rgbColor = model.axesGridActor.getProperty().getColor() model.axesXActor.getProperty().setColor(...rgbColor) widgetState = model.axesYLabels.getWidgetState() model.axesYOriginHandle = widgetState.addHandle() model.axesYHandle = widgetState.addHandle() model.axesYActor = model.axesYWidget .getRepresentations()[1] .getActors()[0] model.axesYActor.getProperty().setColor(...rgbColor) widgetState = model.axesZLabels.getWidgetState() model.axesZOriginHandle = widgetState.addHandle() model.axesZHandle = widgetState.addHandle() model.axesZActor = model.axesZWidget .getRepresentations()[1] .getActors()[0] model.axesZActor.getProperty().setColor(...rgbColor) model.widgetManagerInitialized = true updateAxes() updateAxesVisibility() if (model.widgetManagerInitializedCallback) { model.widgetManagerInitializedCallback() } } updateScaleBar() } publicAPI.setWidgetManagerInitializedCallback = callback => { model.widgetManagerInitializedCallback = callback } model.scaleBarCanvas = document.createElement('canvas') model.scaleBarCanvas.style.position = 'absolute' model.scaleBarCanvas.style.right = '3em' model.scaleBarCanvas.style.top = '12px' model.scaleBarCanvas.style.width = '100px' model.scaleBarCanvas.style.height = '30px' model.scaleBarCanvas.width = 100 * window.devicePixelRatio model.scaleBarCanvas.height = 30 * window.devicePixelRatio model.scaleBarCenterCoord = vtkCoordinate.newInstance() model.scaleBarCenterCoord.setRenderer(model.renderer) model.scaleBarCenterCoord.setCoordinateSystemToNormalizedViewport() model.scaleBarCenterCoord.setValue(0.5, 0.5) model.scaleBarCoordWidth = vtkCoordinate.newInstance() model.scaleBarCoordWidth.setReferenceCoordinate(model.scaleBarCenterCoord) model.scaleBarCoordWidth.setCoordinateSystemToViewport() model.scaleBarCoordWidth.setRenderer(model.renderer) model.scaleBarCoordWidth.setValue(model.scaleBarCanvas.width, 0) function updateScaleBar() { const devicePixelRatio = window.devicePixelRatio || 1 const scaleBarCtx = model.scaleBarCanvas.getContext('2d') const dims = { width: model.scaleBarCanvas.clientWidth * devicePixelRatio, height: model.scaleBarCanvas.clientHeight * devicePixelRatio, } scaleBarCtx.clearRect(0, 0, dims.width, dims.height) scaleBarCtx.fillStyle = model.cornerAnnotation.getAnnotationContainer().style.color scaleBarCtx.fillRect(0, 0, dims.width, 2 * devicePixelRatio) // try catch block to work around Firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=941146 try { scaleBarCtx.font = `${16 * devicePixelRatio}px arial` scaleBarCtx.textAlign = 'center' scaleBarCtx.textBaseline = 'top' model.scaleBarCoordWidth.setValue(dims.width, 0) const cw = model.scaleBarCoordWidth.getComputedWorldValue() const cc = model.scaleBarCenterCoord.getComputedWorldValue() const length = Math.sqrt( (cw[0] - cc[0]) * (cw[0] - cc[0]) + (cw[1] - cc[1]) * (cw[1] - cc[1]), (cw[2] - cc[2]) * (cw[2] - cc[2]) ) model.lengthPixelRatio = length / dims.width const text = numberToText(length, 1) scaleBarCtx.fillText( `${text} ${model.units}`, dims.width * 0.5, 6 * devicePixelRatio, dims.width * 0.9 ) } catch (e) { console.error(e) } } model.interactor.onEndMouseWheel(updateScaleBar) model.interactor.onEndPinch(updateScaleBar) model.widgetManagerInitialized = false model.widgetManager = WidgetManagerPickWhileAnimating.newInstance() model.widgetsToRegister = [] model.widgetProps = new Map() model.axesPolyData = vtkPolyData.newInstance() model.axesMapper = vtkMapper.newInstance() model.axesMapper.setInputData(model.axesPolyData) model.axesGridActor = vtkActor.newInstance() model.axesGridActor.setMapper(model.axesMapper) model.axesGridActor.getProperty().setOpacity(0.5) model.axesGridActor.setVisibility(false) model.renderer.addActor(model.axesGridActor) model.numberOfAxisTicks = 7 model.axesBoundingBox = [...vtkBoundingBox.INIT_BOUNDS] model.axesOriginLabel = vtkAxesLabelsWidget.newInstance() model.widgetsToRegister.push(model.axesOriginLabel) model.axesXLabels = vtkAxesLabelsWidget.newInstance() model.widgetsToRegister.push(model.axesXLabels) model.axesYLabels = vtkAxesLabelsWidget.newInstance() model.widgetsToRegister.push(model.axesYLabels) model.axesZLabels = vtkAxesLabelsWidget.newInstance() model.widgetsToRegister.push(model.axesZLabels) model.axesCircleRadius = 4 model.axesTextOffset = 14 model.layerBBoxes = new Map() // API ---------------------------------------------------------------------- publicAPI.updateDataProbeSize = updateDataProbeSize publicAPI.updateScaleBar = updateScaleBar const superSetBackground = publicAPI.setBackground publicAPI.setBackground = color => { superSetBackground(color) if (color[0] + color[1] + color[2] <= 1.5) { model.axesGridActor.getProperty().setColor([1.0, 1.0, 1.0]) if (model.widgetManagerInitialized) { model.axesXActor.getProperty().setColor(1.0, 1.0, 1.0) model.axesOriginWidget.setCircleProps({ r: model.axesCircleRadius, stroke: 'white', fill: 'white', }) model.axesOriginWidget.setTextProps({ fill: 'white', dx: -10 * model.axesTextOffset, dy: 2 * model.axesTextOffset, }) model.axesXWidget.setCircleProps({ r: model.axesCircleRadius, stroke: 'white', fill: 'white', }) model.axesXWidget.setTextProps({ fill: 'white', dx: model.axesTextOffset, dy: 2 * model.axesTextOffset, }) model.axesYActor.getProperty().setColor(1.0, 1.0, 1.0) model.axesYWidget.setCircleProps({ r: model.axesCircleRadius, stroke: 'white', fill: 'white', }) model.axesYWidget.setTextProps({ fill: 'white', dx: model.axesTextOffset, dy: 2 * model.axesTextOffset, }) model.axesZActor.getProperty().setColor(1.0, 1.0, 1.0) model.axesZWidget.setCircleProps({ r: model.axesCircleRadius, stroke: 'white', fill: 'white', }) model.axesZWidget.setTextProps({ fill: 'white', dx: model.axesTextOffset, dy: -1 * model.axesTextOffset, }) } } else { model.axesGridActor.getProperty().setColor([0.0, 0.0, 0.0]) if (model.widgetManagerInitialized) { model.axesXActor.getProperty().setColor(0.0, 0.0, 0.0) model.axesOriginWidget.setCircleProps({ r: model.axesCircleRadius, stroke: 'black', fill: 'black', }) model.axesOriginWidget.setTextProps({ fill: 'black', dx: -10 * model.axesTextOffset, dy: 2 * model.axesTextOffset, }) model.axesXWidget.setCircleProps({ r: model.axesCircleRadius, stroke: 'black', fill: 'black', }) model.axesXWidget.setTextProps({ fill: 'black', dx: model.axesTextOffset, dy: 2 * model.axesTextOffset, }) model.axesYActor.getProperty().setColor(0.0, 0.0, 0.0) model.axesYWidget.setCircleProps({ r: model.axesCircleRadius, stroke: 'black', fill: 'black', }) model.axesYWidget.setTextProps({ fill: 'black', dx: model.axesTextOffset, dy: 2 * model.axesTextOffset, }) model.axesZActor.getProperty().setColor(0.0, 0.0, 0.0) model.axesZWidget.setCircleProps({ r: model.axesCircleRadius, stroke: 'black', fill: 'black', }) model.axesZWidget.setTextProps({ fill: 'black', dx: model.axesTextOffset, dy: -1 * model.axesTextOffset, }) } } } publicAPI.setViewMode = mode => { if (model.viewMode === 'Volume') { model.volumeCameraState = model.camera.getState() } switch (mode) { case 'XPlane': if (model.viewMode === 'XPlane') { break } model.viewMode = mode model.axesZWidget.setVisibility(false) model.axesZActor.setVisibility(false) setVisualizationMode(0) break case 'YPlane': if (model.viewMode === 'YPlane') { break } model.viewMode = mode setVisualizationMode(1) break case 'ZPlane': if (model.viewMode === 'ZPlane') { break } model.viewMode = mode setVisualizationMode(2) break case 'Volume': if (model.viewMode === 'Volume') { break } model.viewMode = mode setVisualizationMode(-1) break default: vtkErrorMacro('Unexpected view mode') } publicAPI.resetCamera() updateDataProbeSize() } publicAPI.setImageVisibility = visible => { if (model.imageVisibility === visible || !model.volumeRepresentation) { return } model.imageVisibility = visible publicAPI.modified() if (visible) { switch (model.viewMode) { case 'Volume': { model.volumeRepresentation.setVolumeVisibility(true) break } case 'XPlane': model.volumeRepresentation.setXSliceVisibility(true) break case 'YPlane': model.volumeRepresentation.setYSliceVisibility(true) break case 'ZPlane': model.volumeRepresentation.setZSliceVisibility(true) break } } else { model.volumeRepresentation.setXSliceVisibility(false) model.volumeRepresentation.setYSliceVisibility(false) model.volumeRepresentation.setZSliceVisibility(false) model.volumeRepresentation.setVolumeVisibility(false) } } publicAPI.setViewPlanes = viewPlanes => { model.viewPlanes = viewPlanes if (model.viewMode === 'Volume' && model.volumeRepresentation) { if (viewPlanes) { publicAPI.setCornerAnnotation('ne', model.neCornerAnnotation) } } } publicAPI.setOrientationAnnotationVisibility = visible => { if (visible) { model.scaleBarCanvas.style.display = 'block' if (model.volumeRepresentation) { publicAPI.setAnnotationOpacity(1.0) model.orientationWidget.setEnabled(true) if (!model.renderWindow.getInteractor().isAnimating()) { model.renderWindow.render() } } } else { model.scaleBarCanvas.style.display = 'none' publicAPI.setAnnotationOpacity(0.0) model.orientationWidget.setEnabled(false) if (!model.renderWindow.getInteractor().isAnimating()) { model.renderWindow.render() } } } publicAPI.setPlanesUseLinearInterpolation = interpolate => { if (model.volumeRepresentation) { if (interpolate) { model.volumeRepresentation.getActors().forEach(actor => { const property = actor.getProperty() property.setInterpolationTypeToLinear() if (property.getRGBTransferFunction()) { property.getRGBTransferFunction().modified() } }) if (!model.renderWindow.getInteractor().isAnimating()) { model.renderWindow.render() } } else { model.volumeRepresentation.getActors().forEach(actor => { const property = actor.getProperty() property.setInterpolationTypeToNearest() if (property.getRGBTransferFunction()) { property.getRGBTransferFunction().modified() } }) if (!model.renderWindow.getInteractor().isAnimating()) { model.renderWindow.render() } } } } publicAPI.setEnableAxes = enable => { if (enable != model.enableAxes) { model.enableAxes = enable updateAxesVisibility() publicAPI.modified() if (!model.renderWindow.getInteractor().isAnimating()) { model.renderWindow.render() } } } publicAPI.setEnableBBox = (name, enable) => { const data = model.layerBBoxes.get(name) if (data.actor.getVisibility() !== enable) { data.actor.setVisibility(enable) } } const superAddRepresentation = publicAPI.addRepresentation publicAPI.addRepresentation = representation => { superAddRepresentation(representation) if (!representation) { return } const volumeRepresentations = model.representations.filter(rep => { const isVolumeRepresentation = !!rep.getVolumes().length return isVolumeRepresentation }) if (volumeRepresentations[0]) { model.volumeRepresentation = volumeRepresentations[0] const volume = model.volumeRepresentation.getVolumes()[0] const property = volume.getProperty() property.setAmbient(VOLUME_AMBIENT_DEFAULT) property.setDiffuse(VOLUME_DIFFUSE_DEFAULT) property.setSpecular(0.4) property.setSpecularPower(25) const actors = model.volumeRepresentation.getActors() actors.forEach(model.annotationPicker.addPickList) model.xSliceOutliner.setInputData(actors[0].getMapper()) model.renderer.addActor(model.xSliceActor) model.ySliceOutliner.setInputData(actors[1].getMapper()) model.renderer.addActor(model.ySliceActor) model.zSliceOutliner.setInputData(actors[2].getMapper()) model.renderer.addActor(model.zSliceActor) updateDataProbeSize() publicAPI.setAnnotationOpacity(1.0) } if (model.widgetManagerInitialized) { updateAxes() } } const superRemoveRepresentation = publicAPI.removeRepresentation publicAPI.removeRepresentation = representation => { superRemoveRepresentation(representation) if (!representation) { return } if (representation.getVolumes().length) { model.renderer.removeActor(model.xSliceActor) model.renderer.removeActor(model.ySliceActor) model.renderer.removeActor(model.zSliceActor) } representation.getActors().forEach(model.annotationPicker.deletePickList) } // Continuously rotate in 3D function rotateAzimuth() { model.renderer.getActiveCamera().azimuth(0.25) model.renderer.resetCameraClippingRange() } model.rotateAnimationCallback = null publicAPI.setRotate = rotate => { if (model.rotate === rotate) { return } model.rotate = rotate if (rotate) { model.rotateAnimationCallback = model.interactor.onAnimation( rotateAzimuth ) model.interactor.requestAnimation('itk-vtk-view-rotate') } else { model.interactor.cancelAnimation('itk-vtk-view-rotate') if (model.rotateAnimationCallback) { model.rotateAnimationCallback.unsubscribe() model.rotateAnimationCallback = null } } } const superSetContainer = publicAPI.setContainer publicAPI.setContainer = container => { superSetContainer(container) if (container) { container.appendChild(model.scaleBarCanvas) } } publicAPI.resize = () => { if (model.container) { const dims = model.container.getBoundingClientRect() if (dims.width === dims.height && dims.width === 0) { return } const devicePixelRatio = window.devicePixelRatio || 1 const width = Math.max(10, Math.floor(devicePixelRatio * dims.width)) const height = Math.max(10, Math.floor(devicePixelRatio * dims.height)) model._openGLRenderWindow.setSize(width, height) model.scaleBarCanvas.width = (100 * devicePixelRatio).toFixed() model.scaleBarCanvas.height = (30 * devicePixelRatio).toFixed() publicAPI.invokeResize({ width, height }) publicAPI.renderLater() } } publicAPI.resetCamera = bounds => { model.renderer.resetCamera(bounds) model.renderer.resetCameraClippingRange() model.interactorStyle2D.setCenterOfRotation(model.camera.getFocalPoint()) model.interactorStyle3D.setCenterOfRotation(model.camera.getFocalPoint()) publicAPI.renderLater() } publicAPI.createLayerBoundingBox = name => { const labelBBoxPolyData = vtkPolyData.newInstance() const labelBBoxMapper = vtkMapper.newInstance() labelBBoxMapper.setInputData(labelBBoxPolyData) const labelBBoxGridActor = vtkActor.newInstance() labelBBoxGridActor.setMapper(labelBBoxMapper) labelBBoxGridActor.getProperty().setColor([1.0, 0.0, 0.0]) labelBBoxGridActor.setVisibility(false) const data = { polyData: labelBBoxPolyData, actor: labelBBoxGridActor, } model.layerBBoxes.set(name, data) model.renderer.addActor(data.actor) } publicAPI.updateLabelBoundingBox = (name, bounds) => { if (!model.layerBBoxes.has(name)) { publicAPI.createLayerBoundingBox(name) } const data = model.layerBBoxes.get(name) let points = vtkPoints.newInstance() for (let i = 0; i < 2; i++) { for (let j = 2; j < 4; j++) { for (let k = 4; k < 6; k++) { points.insertNextPoint(bounds[i], bounds[j], bounds[k]) } } } data.polyData.setPoints(points) let lines = vtkCellArray.newInstance() lines.insertNextCell([0, 1, 3, 2, 0]) lines.insertNextCell([4, 5, 7, 6, 4]) lines.insertNextCell([0, 4, 6, 2]) lines.insertNextCell([1, 5, 7, 3]) data.polyData.setLines(lines) } } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { viewMode: 'Volume', viewPlanes: false, imageVisibility: true, rotate: false, units: '', neCornerAnnotation: CursorCornerAnnotation, labelIndex: null, labelNames: null, clickCallback: null, lengthPixelRatio: 1.0, lastPickedValues: { iIndex: null, jIndex: null, kIndex: null, xPosition: null, yPosition: null, zPosition: null, value: null, label: null, }, widgetManagerInitializedCallback: null, enableAxes: false, xyLowerLeft: false, axesNames: null, } // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues) vtkViewProxy.extend(publicAPI, model, initialValues) macro.get(publicAPI, model, [ 'viewMode', 'viewPlanes', 'imageVisibility', 'rotate', 'lengthPixelRatio', 'axesActor', 'enableAxes', 'widgetManager', ]) macro.setGet(publicAPI, model, [ 'units', 'neCornerAnnotation', 'labelNames', 'labelIndex', 'clickCallback', 'xyLowerLeft', 'axesNames', ]) // Object specific methods ItkVtkViewProxy(publicAPI, model) } // ---------------------------------------------------------------------------- export const newInstance = macro.newInstance(extend, 'ItkVtkViewProxy') // ---------------------------------------------------------------------------- export default { newInstance, extend } ================================================ FILE: src/Rendering/VTKJS/vtk/OpenGLImageMapperFractional.js ================================================ // ---------------------------------------------------------------------------- // Additive mixing of components rather than weighted // ---------------------------------------------------------------------------- import * as macro from 'vtk.js/Sources/macros' import vtkShaderProgram from 'vtk.js/Sources/Rendering/OpenGL/ShaderProgram' import vtkOpenGlImageMapper from 'vtk.js/Sources/Rendering/OpenGL/ImageMapper' import { registerOverride } from 'vtk.js/Sources/Rendering/OpenGL/ViewNodeFactory' const { vtkErrorMacro } = macro function OpenGLImageMapperFractional(publicAPI, model) { // Set our className model.classHierarchy.push('OpenGLImageMapperFractional') // Changes from vtk.js code: Add component values together after transfer function and don't normalize to 1 publicAPI.replaceShaderValues = (shaders, ren, actor) => { let VSSource = shaders.Vertex let FSSource = shaders.Fragment VSSource = vtkShaderProgram.substitute(VSSource, '//VTK::Camera::Dec', [ 'uniform mat4 MCPCMatrix;', ]).result VSSource = vtkShaderProgram.substitute( VSSource, '//VTK::PositionVC::Impl', [' gl_Position = MCPCMatrix * vertexMC;'] ).result VSSource = vtkShaderProgram.substitute( VSSource, '//VTK::TCoord::Impl', 'tcoordVCVSOutput = tcoordMC;' ).result VSSource = vtkShaderProgram.substitute( VSSource, '//VTK::TCoord::Dec', 'attribute vec2 tcoordMC; varying vec2 tcoordVCVSOutput;' ).result const tNumComp = model.openGLTexture.getComponents() const iComps = actor.getProperty().getIndependentComponents() let tcoordDec = [ 'varying vec2 tcoordVCVSOutput;', // color shift and scale 'uniform float cshift0;', 'uniform float cscale0;', // pwf shift and scale 'uniform float pwfshift0;', 'uniform float pwfscale0;', 'uniform sampler2D texture1;', 'uniform sampler2D colorTexture1;', 'uniform sampler2D pwfTexture1;', 'uniform float opacity;', ] if (iComps) { for (let comp = 1; comp < tNumComp; comp++) { tcoordDec = tcoordDec.concat([ // color shift and scale `uniform float cshift${comp};`, `uniform float cscale${comp};`, // weighting shift and scale `uniform float pwfshift${comp};`, `uniform float pwfscale${comp};`, ]) } // the heights defined below are the locations // for the up to four components of the tfuns // the tfuns have a height of 2XnumComps pixels so the // values are computed to hit the middle of the two rows // for that component switch (tNumComp) { case 1: tcoordDec = tcoordDec.concat([ 'uniform float mix0;', '#define height0 0.5', ]) break case 2: tcoordDec = tcoordDec.concat([ 'uniform float mix0;', 'uniform float mix1;', '#define height0 0.25', '#define height1 0.75', ]) break case 3: tcoordDec = tcoordDec.concat([ 'uniform float mix0;', 'uniform float mix1;', 'uniform float mix2;', '#define height0 0.17', '#define height1 0.5', '#define height2 0.83', ]) break case 4: tcoordDec = tcoordDec.concat([ 'uniform float mix0;', 'uniform float mix1;', 'uniform float mix2;', 'uniform float mix3;', '#define height0 0.125', '#define height1 0.375', '#define height2 0.625', '#define height3 0.875', ]) break default: vtkErrorMacro('Unsupported number of independent coordinates.') } } FSSource = vtkShaderProgram.substitute( FSSource, '//VTK::TCoord::Dec', tcoordDec ).result if (iComps) { const rgba = ['r', 'g', 'b', 'a'] let tcoordImpl = ['vec4 tvalue = texture2D(texture1, tcoordVCVSOutput);'] for (let comp = 0; comp < tNumComp; comp++) { tcoordImpl = tcoordImpl.concat([ `vec3 tcolor${comp} = mix${comp} * texture2D(colorTexture1, vec2(tvalue.${rgba[comp]} * cscale${comp} + cshift${comp}, height${comp})).rgb;`, `float compWeight${comp} = mix${comp} * texture2D(pwfTexture1, vec2(tvalue.${rgba[comp]} * pwfscale${comp} + pwfshift${comp}, height${comp})).r;`, ]) } switch (tNumComp) { case 1: tcoordImpl = tcoordImpl.concat([ 'gl_FragData[0] = vec4(tcolor0.rgb, opacity);', ]) break case 2: tcoordImpl = tcoordImpl.concat([ // 'float weightSum = compWeight0 + compWeight1;', // vtk.js original code 'float weightSum = 1.0;', // the change 'gl_FragData[0] = vec4(vec3((tcolor0.rgb * (compWeight0 / weightSum)) + (tcolor1.rgb * (compWeight1 / weightSum))), opacity);', ]) break case 3: tcoordImpl = tcoordImpl.concat([ // 'float weightSum = compWeight0 + compWeight1 + compWeight2;', 'float weightSum = 1.0;', 'gl_FragData[0] = vec4(vec3((tcolor0.rgb * (compWeight0 / weightSum)) + (tcolor1.rgb * (compWeight1 / weightSum)) + (tcolor2.rgb * (compWeight2 / weightSum))), opacity);', ]) break case 4: tcoordImpl = tcoordImpl.concat([ // 'float weightSum = compWeight0 + compWeight1 + compWeight2 + compWeight3;', 'float weightSum = 1.0;', 'gl_FragData[0] = vec4(vec3((tcolor0.rgb * (compWeight0 / weightSum)) + (tcolor1.rgb * (compWeight1 / weightSum)) + (tcolor2.rgb * (compWeight2 / weightSum)) + (tcolor3.rgb * (compWeight3 / weightSum))), opacity);', ]) break default: vtkErrorMacro('Unsupported number of independent coordinates.') } FSSource = vtkShaderProgram.substitute( FSSource, '//VTK::TCoord::Impl', tcoordImpl ).result } else { // dependent components switch (tNumComp) { case 1: FSSource = vtkShaderProgram.substitute( FSSource, '//VTK::TCoord::Impl', [ 'float intensity = texture2D(texture1, tcoordVCVSOutput).r;', 'vec3 tcolor = texture2D(colorTexture1, vec2(intensity * cscale0 + cshift0, 0.5)).rgb;', 'float scalarOpacity = texture2D(pwfTexture1, vec2(intensity * pwfscale0 + pwfshift0, 0.5)).r;', 'gl_FragData[0] = vec4(tcolor, scalarOpacity * opacity);', ] ).result break case 2: FSSource = vtkShaderProgram.substitute( FSSource, '//VTK::TCoord::Impl', [ 'vec4 tcolor = texture2D(texture1, tcoordVCVSOutput);', 'float intensity = tcolor.r*cscale0 + cshift0;', 'gl_FragData[0] = vec4(texture2D(colorTexture1, vec2(intensity, 0.5)).rgb, pwfscale0*tcolor.g + pwfshift0);', ] ).result break default: FSSource = vtkShaderProgram.substitute( FSSource, '//VTK::TCoord::Impl', [ 'vec4 tcolor = cscale0*texture2D(texture1, tcoordVCVSOutput.st) + cshift0;', 'gl_FragData[0] = vec4(texture2D(colorTexture1, vec2(tcolor.r,0.5)).r,', ' texture2D(colorTexture1, vec2(tcolor.g,0.5)).r,', ' texture2D(colorTexture1, vec2(tcolor.b,0.5)).r, tcolor.a);', ] ).result } } if (model.haveSeenDepthRequest) { FSSource = vtkShaderProgram.substitute( FSSource, '//VTK::ZBuffer::Dec', 'uniform int depthRequest;' ).result FSSource = vtkShaderProgram.substitute(FSSource, '//VTK::ZBuffer::Impl', [ 'if (depthRequest == 1) {', 'float iz = floor(gl_FragCoord.z*65535.0 + 0.1);', 'float rf = floor(iz/256.0)/255.0;', 'float gf = mod(iz,256.0)/255.0;', 'gl_FragData[0] = vec4(rf, gf, 0.0, 1.0); }', ]).result } shaders.Vertex = VSSource shaders.Fragment = FSSource publicAPI.replaceShaderClip(shaders, ren, actor) publicAPI.replaceShaderCoincidentOffset(shaders, ren, actor) } } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = {} // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues) vtkOpenGlImageMapper.extend(publicAPI, model, initialValues) OpenGLImageMapperFractional(publicAPI, model) } // ---------------------------------------------------------------------------- export const newInstance = macro.newInstance( extend, 'OpenGLImageMapperFractional' ) // ---------------------------------------------------------------------------- export default { newInstance, extend } // Register ourself to OpenGL backend if imported registerOverride('vtkImageMapper', newInstance) ================================================ FILE: src/Rendering/VTKJS/vtk/PointSetRepresentationProxy/index.js ================================================ import macro from 'vtk.js/Sources/macros' import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor' import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper' import vtkSphereSource from 'vtk.js/Sources/Filters/Sources/SphereSource' import vtkGlyph3DMapper from 'vtk.js/Sources/Rendering/Core/Glyph3DMapper' import vtkAbstractRepresentationProxy from 'vtk.js/Sources/Proxy/Core/AbstractRepresentationProxy' const PROPERTIES_STATE = {} const PROPERTIES_DEFAULT = {} // ---------------------------------------------------------------------------- // vtkPointSetRepresentationProxy methods // ---------------------------------------------------------------------------- function vtkPointSetRepresentationProxy(publicAPI, model) { // Set our className model.classHierarchy.push('vtkPointSetRepresentationProxy') // Internals model.mapper = vtkMapper.newInstance({ interpolateScalarsBeforeMapping: true, useLookupTableScalarRange: true, scalarVisibility: false, }) model.actor = vtkActor.newInstance() model.property = model.actor.getProperty() model.glyphMapper = vtkGlyph3DMapper.newInstance({ interpolateScalarsBeforeMapping: true, useLookupTableScalarRange: true, scalarVisibility: false, }) model.sphereSource = vtkSphereSource.newInstance() model.glyphMapper.setInputConnection(model.sphereSource.getOutputPort(), 1) model.glyphMapper.setScaleModeToScaleByConstant() function updateGlyphMapper(inputDataSet) { model.glyphMapper.setInputData(inputDataSet, 0) } // Auto connect mappers model.sourceDependencies.push(model.mapper) model.sourceDependencies.push({ setInputData: updateGlyphMapper }) // connect rendering pipeline model.actor.setMapper(model.mapper) model.actors.push(model.actor) model.glyphActor = vtkActor.newInstance() model.glyphActor.setMapper(model.glyphMapper) model.actors.push(model.glyphActor) model.glyphProperty = model.glyphActor.getProperty() // Initialize state model.property.setRepresentationToPoints() model.glyphActor.setVisibility(false) publicAPI.setRadiusFactor = radiusFactor => { model.radiusFactor = radiusFactor model.sphereSource.setRadius(model.pointSize * radiusFactor) } publicAPI.setVisibility = visible => { if (visible) { switch (model.representation) { case 'Points': model.actor.setVisibility(visible) break case 'Spheres': model.glyphActor.setVisibility(visible) break default: throw Error('Unexpected point set representation') } } else { model.actor.setVisibility(visible) model.glyphActor.setVisibility(visible) } model.visibility = visible } publicAPI.setRepresentation = representation => { if (model.visibility) { switch (representation) { case 'Points': model.actor.setVisibility(true) model.glyphActor.setVisibility(false) break case 'Spheres': model.actor.setVisibility(false) model.glyphActor.setVisibility(true) break default: throw Error('Unexpected point set representation') } } model.representation = representation } publicAPI.setOpacity = opacity => { model.property.setOpacity(opacity) model.glyphProperty.setOpacity(opacity) model.opacity = opacity } publicAPI.setColor = color => { model.property.setDiffuseColor(color) model.glyphProperty.setDiffuseColor(color) model.color = color } publicAPI.setInterpolateScalarsBeforeMapping = interpolate => { model.mapper.setInterpolateScalarsBeforeMapping(interpolate) model.glyphMapper.setInterpolateScalarsBeforeMapping(interpolate) model.interpolateScalarsBeforeMapping = interpolate } publicAPI.setPointSize = pointSize => { model.property.setPointSize(pointSize) model.sphereSource.setRadius(pointSize * model.radiusFactor) model.pointSize = pointSize } publicAPI.setUseShadow = useShadow => { model.property.setLighting(useShadow) model.glyphProperty.setLighting(useShadow) model.useShadow = useShadow } publicAPI.setUseBounds = useBounds => { model.actor.setUseBounds(useBounds) model.glyphActor.setUseBounds(useBounds) model.useBounds = useBounds } const abstractSetColorBy = publicAPI.setColorBy publicAPI.setColorBy = (arrayName, arrayLocation, componentIndex = -1) => { // Configures model.mapper abstractSetColorBy(arrayName, arrayLocation, componentIndex) // Now also configure model.glyphMapper let colorMode = vtkMapper.ColorMode.DEFAULT let scalarMode = vtkMapper.ScalarMode.DEFAULT const colorByArrayName = arrayName const activeArray = publicAPI.getDataArray(arrayName, arrayLocation) const scalarVisibility = !!activeArray const lookupTable = arrayName ? publicAPI.getLookupTableProxy(arrayName).getLookupTable() : null if (scalarVisibility) { colorMode = vtkMapper.ColorMode.MAP_SCALARS scalarMode = arrayLocation === 'pointData' ? vtkMapper.ScalarMode.USE_POINT_FIELD_DATA : vtkMapper.ScalarMode.USE_CELL_FIELD_DATA model.glyphMapper.setLookupTable(lookupTable) } model.glyphMapper.set( { colorByArrayName, colorMode, scalarMode, scalarVisibility, }, true ) } } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { representation: 'Points', radiusFactor: 0.1, visibility: true, opacity: 1.0, color: [1.0, 1.0, 1.0], interpolateScalarsBeforeMapping: true, pointSize: 0.3, useShadow: true, useBounds: true, } // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues) // Object methods vtkAbstractRepresentationProxy.extend(publicAPI, model) // Object specific methods vtkPointSetRepresentationProxy(publicAPI, model) macro.get(publicAPI, model, [ 'radiusFactor', 'representation', 'visibility', 'opacity', 'color', 'interpolateScalarsBeforeMapping', 'pointSize', 'useShadow', 'useBounds', ]) // Map proxy properties macro.proxyPropertyState( publicAPI, model, PROPERTIES_STATE, PROPERTIES_DEFAULT ) macro.proxyPropertyMapping(publicAPI, model, {}) } // ---------------------------------------------------------------------------- export const newInstance = macro.newInstance( extend, 'vtkPointSetRepresentationProxy' ) // ---------------------------------------------------------------------------- export default { newInstance, extend } ================================================ FILE: src/Rendering/VTKJS/vtk/SVGMarkerTextRepresentation/index.js ================================================ import macro from 'vtk.js/Sources/macros' import vtkSVGRepresentation from '../SVGRepresentation' const { createSvgElement } = vtkSVGRepresentation // ---------------------------------------------------------------------------- // vtkSVGMarkerTextRepresentation // ---------------------------------------------------------------------------- function vtkSVGMarkerTextRepresentation(publicAPI, model) { // Set our className model.classHierarchy.push('vtkSVGMarkerTextRepresentation') publicAPI.render = () => { if (!publicAPI.getVisibility()) { return } const list = publicAPI.getRepresentationStates() const coords = [] const textContents = [] for (let i = 0; i < list.length; i++) { coords.push(list[i].getOrigin()) } let index = 0 return publicAPI.worldPointsToPixelSpace(coords).then(pixelSpace => { const points2d = pixelSpace.coords const winHeight = pixelSpace.windowSize[1] const root = createSvgElement('g') for (let i = 0; i < points2d.length; i++) { const xy = points2d[i] const x = xy[0] const y = winHeight - xy[1] const circle = createSvgElement('circle') Object.keys(model.circleProps || {}).forEach(prop => circle.setAttribute(prop, model.circleProps[prop]) ) circle.setAttribute('cx', x) circle.setAttribute('cy', y) const text = createSvgElement('text') Object.keys(model.textProps || {}).forEach(prop => text.setAttribute(prop, model.textProps[prop]) ) text.setAttribute('x', x) text.setAttribute('y', y) text.textContent = list[index].getText() root.appendChild(circle) root.appendChild(text) index++ } return root }) } } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { circleProps: { r: 5, stroke: 'red', fill: 'red', }, textProps: { fill: 'white', dx: 12, dy: -12, }, } // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues) vtkSVGRepresentation.extend(publicAPI, model, initialValues) macro.setGet(publicAPI, model, ['circleProps', 'textProps']) // Object specific methods vtkSVGMarkerTextRepresentation(publicAPI, model) } // ---------------------------------------------------------------------------- export const newInstance = macro.newInstance( extend, 'vtkSVGMarkerTextRepresentation' ) // ---------------------------------------------------------------------------- export default { extend, newInstance } ================================================ FILE: src/Rendering/VTKJS/vtk/SVGRepresentation/index.js ================================================ import macro from 'vtk.js/Sources/macros' import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData' import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor' import vtkPixelSpaceCallbackMapper from 'vtk.js/Sources/Rendering/Core/PixelSpaceCallbackMapper' import vtkWidgetRepresentation from 'vtk.js/Sources/Widgets/Representations/WidgetRepresentation' import { Behavior } from 'vtk.js/Sources/Widgets/Representations/WidgetRepresentation/Constants' import { RenderingTypes } from 'vtk.js/Sources/Widgets/Core/WidgetManager/Constants' const SVG_XMLNS = 'http://www.w3.org/2000/svg' // ---------------------------------------------------------------------------- function createSvgElement(tag) { return { name: tag, attrs: {}, eventListeners: {}, // implies no children if set textContent: null, children: [], setAttribute(attr, val) { this.attrs[attr] = val }, removeAttribute(attr) { delete this.attrs[attr] }, appendChild(n) { this.children.push(n) }, addEventListeners(event, callback) { this.eventListeners[event] = callback }, } } // ---------------------------------------------------------------------------- function createSvgDomElement(tag) { return document.createElementNS(SVG_XMLNS, tag) } // ---------------------------------------------------------------------------- function defer() { let resolve let reject const promise = new Promise((res, rej) => { resolve = res reject = rej }) return { promise, resolve, reject, } } // ---------------------------------------------------------------------------- // vtkSVGRepresentation // ---------------------------------------------------------------------------- function vtkSVGRepresentation(publicAPI, model) { // Set our className model.classHierarchy.push('vtkSVGRepresentation') let deferred = null model.psActor = vtkActor.newInstance({ pickable: false, _parentProp: publicAPI, }) model.psMapper = vtkPixelSpaceCallbackMapper.newInstance() model.points = vtkPolyData.newInstance() model.psMapper.setInputData(model.points) model.psActor.setMapper(model.psMapper) model.psMapper.setCallback((...args) => { if (deferred) { const d = deferred deferred = null d.resolve({ coords: args[0], camera: args[1], aspect: args[2], depthValues: args[3], windowSize: args[4], }) } }) publicAPI.addActor(model.psActor) // -------------------------------------------------------------------------- publicAPI.worldPointsToPixelSpace = points3d => { const pts = new Float32Array(points3d.length * 3) for (let i = 0; i < points3d.length; i++) { pts[i * 3 + 0] = points3d[i][0] pts[i * 3 + 1] = points3d[i][1] pts[i * 3 + 2] = points3d[i][2] } model.points.getPoints().setData(pts) model.points.modified() deferred = defer() return deferred.promise } publicAPI.createListenableSvgElement = (tag, id) => { const element = createSvgElement(tag) if (model.pickable) { element.addEventListeners('mouseenter', () => { publicAPI.setHover(id) }) element.addEventListeners('mouseleave', () => { if (publicAPI.getHover() === id) { publicAPI.setHover(null) } }) } return element } // -------------------------------------------------------------------------- publicAPI.updateActorVisibility = ( renderingType = RenderingTypes.FRONT_BUFFER, ctxVisible = true, handleVisible = true ) => { if (model.behavior === Behavior.CONTEXT) { publicAPI.setVisibility(ctxVisible) } else if (model.behavior === Behavior.HANDLE) { publicAPI.setVisibility(handleVisible) } } // -------------------------------------------------------------------------- // Subclasses must implement this method publicAPI.render = () => { throw new Error('Not implemented') } } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- /** * 'hover' is not null when a pickable SVG element is mouse hovered. */ const DEFAULT_VALUES = { visibility: true, } // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues) // Extend methods vtkWidgetRepresentation.extend(publicAPI, model, initialValues) macro.setGet(publicAPI, model, ['visibility', 'hover']) // Object specific methods vtkSVGRepresentation(publicAPI, model) } // ---------------------------------------------------------------------------- export default { extend, createSvgElement, createSvgDomElement } ================================================ FILE: src/Rendering/VTKJS/vtk/SliceOutlineFilter/index.js ================================================ import macro from 'vtk.js/Sources/macros' import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData' import vtkMath from 'vtk.js/Sources/Common/Core/Math' import { SlicingMode } from 'vtk.js/Sources/Rendering/Core/ImageMapper/Constants' const { vtkErrorMacro } = macro // prettier-ignore export const BOUNDS_MAP = [ 0, 2, 4, // pt 0 1, 2, 4, // pt 1 0, 3, 4, // pt 2 1, 3, 4, // pt 3 0, 2, 5, // pt 4 1, 2, 5, // pt 5 0, 3, 5, // pt 6 1, 3, 5, // pt 7 ]; // prettier-ignore export const LINE_ARRAY = [ 2, 0, 1, 2, 2, 3, 2, 4, 5, 2, 6, 7, 2, 0, 2, 2, 1, 3, 2, 4, 6, 2, 5, 7, 2, 0, 4, 2, 1, 5, 2, 2, 6, 2, 3, 7, ]; // ---------------------------------------------------------------------------- // vtkSliceOutlineFilter methods // ---------------------------------------------------------------------------- function vtkSliceOutlineFilter(publicAPI, model) { // Set our className model.classHierarchy.push('vtkOutlineFilter') const spatialBoundsForSlice = (input, thickness = 0) => { const image = input.getInputData() if (!image) { return vtkMath.createUninitializedBounds() } const extent = image.getSpatialExtent() const { ijkMode } = input.getClosestIJKAxis() let nSlice = input.getSlice() if (ijkMode !== model.slicingMode) { // If not IJK slicing, get the IJK slice from the XYZ position/slice nSlice = input.getSliceAtPosition(nSlice) } switch (ijkMode) { case SlicingMode.I: extent[0] = nSlice - thickness extent[1] = nSlice + thickness break case SlicingMode.J: extent[2] = nSlice - thickness extent[3] = nSlice + thickness break case SlicingMode.K: extent[4] = nSlice - thickness extent[5] = nSlice + thickness break default: break } return image.extentToBounds(extent) } publicAPI.requestData = (inData, outData) => { // implement requestData const input = inData[0] if (!input) { vtkErrorMacro('Invalid or missing input') return } const bounds = spatialBoundsForSlice(input) const output = vtkPolyData.newInstance() output .getPoints() .setData(Float32Array.from(BOUNDS_MAP.map(idx => bounds[idx])), 3) output.getLines().setData(Uint16Array.from(LINE_ARRAY)) outData[0] = output } } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = {} // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues) // Make this a VTK object macro.obj(publicAPI, model) // Also make it an algorithm with one input and one output macro.algo(publicAPI, model, 1, 1) // Object specific methods vtkSliceOutlineFilter(publicAPI, model) } // ---------------------------------------------------------------------------- export const newInstance = macro.newInstance(extend, 'vtkSliceOutlineFilter') // ---------------------------------------------------------------------------- export default { newInstance, extend, BOUNDS_MAP, LINE_ARRAY } ================================================ FILE: src/Rendering/VTKJS/vtk/WidgetManagerPickWhileAnimating.js ================================================ import Macro from 'vtk.js/Sources/macros' import vtkWidgetManager from 'vtk.js/Sources/Widgets/Core/WidgetManager' // Let widgets handleEvents while interactor is animating function WidgetManagerPickWhileAnimating(publicAPI, model) { model.classHierarchy.push('WidgetManagerPickWhileAnimating') const subscriptions = [] const superSetRenderer = publicAPI.setRenderer publicAPI.setRenderer = renderer => { superSetRenderer(renderer) subscriptions.push( model._interactor.onStartAnimation(() => { model.isAnimating = false // undoes super class setting this to true }) ) } const superDelete = publicAPI.delete publicAPI.delete = () => { while (subscriptions.length) { subscriptions.pop().unsubscribe() } superDelete() } } const DEFAULT_VALUES = {} export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues) vtkWidgetManager.extend(publicAPI, model, initialValues) WidgetManagerPickWhileAnimating(publicAPI, model) } // ---------------------------------------------------------------------------- export const newInstance = Macro.newInstance( extend, 'WidgetManagerPickWhileAnimating' ) // ---------------------------------------------------------------------------- export default { newInstance, extend } ================================================ FILE: src/Rendering/VTKJS/vtkJSRenderingMachineOptions.js ================================================ import createRenderer from './createRenderer' import render from './render' import requestAnimation from './requestAnimation' import cancelAnimation from './cancelAnimation' import mainRenderingMachineOptions from './Main/mainRenderingMachineOptions' import layersRenderingMachineOptions from './Layers/layersRenderingMachineOptions' import imagesRenderingMachineOptions from './Images/imagesRenderingMachineOptions' import widgetsRenderingMachineOptions from './Widgets/widgetsRenderingMachineOptions' const isNotAnimating = context => !context.renderWindow.getInteractor().isAnimating() const vtkJSRenderingMachineOptions = { main: mainRenderingMachineOptions, layers: layersRenderingMachineOptions, images: imagesRenderingMachineOptions, widgets: widgetsRenderingMachineOptions, actions: { createRenderer, render, requestAnimation, cancelAnimation, }, guards: { isNotAnimating, }, } export default vtkJSRenderingMachineOptions ================================================ FILE: src/Rendering/Widgets/createWidgetsRenderingMachine.js ================================================ import { Machine, assign, spawn, send } from 'xstate' function createWidgetsRenderingMachine(options, context) { return Machine( { id: 'widgetsRendering', initial: 'idle', context, states: { idle: { always: { target: 'active', actions: 'createWidgets', }, }, active: { on: { TOGGLE_DISTANCE_WIDGET: { actions: 'toggleDistanceWidget', }, }, }, }, }, options ) } export default createWidgetsRenderingMachine ================================================ FILE: src/Rendering/createRenderingMachine.js ================================================ import { Machine, forwardTo, sendParent, send } from 'xstate' import createMainRenderingMachine from './Main/createMainRenderingMachine' import createLayersRenderingMachine from './Layers/createLayersRenderingMachine' import createImagesRenderingMachine from './Images/createImagesRenderingMachine' import createWidgetsRenderingMachine from './Widgets/createWidgetsRenderingMachine' const createRenderingMachine = (options, context) => { const { main, layers, images, widgets } = options const mainMachine = createMainRenderingMachine(main, context) const layersMachine = createLayersRenderingMachine(layers, context) const imagesMachine = createImagesRenderingMachine(images, context) const widgetsMachine = createWidgetsRenderingMachine(widgets, context) return Machine( { id: 'rendering', initial: 'idle', context, states: { idle: { always: { target: 'active', actions: ['createRenderer'], }, }, active: { invoke: [ { id: 'main', src: mainMachine, }, { id: 'layers', src: layersMachine, }, { id: 'images', src: imagesMachine, }, { id: 'widgets', src: widgetsMachine, }, ], initial: 'idle', states: { idle: { on: { RENDER: { cond: 'isNotAnimating', target: 'render', }, }, }, render: { entry: 'render', initial: 'waitingForRequest', states: { waitingForRequest: { on: { RENDER: 'renderRequested' }, }, renderRequested: {}, }, on: { RENDERED: [ { in: '#rendering.active.render.renderRequested', // got render request while rendering? cond: 'isNotAnimating', target: 'render', // loopback }, { target: 'idle' }, ], }, invoke: { id: 'waitForAnimationFrame', src: () => send => { let timeoutId const animationId = requestAnimationFrame(() => { timeoutId = setTimeout(() => send('RENDERED'), 0) // send after paint }) return () => { cancelAnimationFrame(animationId) clearTimeout(timeoutId) } }, }, }, }, on: { BACKGROUND_TURNED_LIGHT: { actions: sendParent('TOGGLE_DARK_MODE'), }, BACKGROUND_TURNED_DARK: { actions: sendParent('TOGGLE_DARK_MODE'), }, UPDATE_FPS: { actions: forwardTo('main'), }, FPS_UPDATED: { actions: [forwardTo('main'), forwardTo('images')], }, SET_IMAGE_SCALE: { actions: forwardTo('images'), }, SET_CINEMATIC_PARAMETERS: { actions: forwardTo('images'), }, CINEMATIC_CHANGED: { actions: forwardTo('images'), }, REQUEST_ANIMATION: { actions: 'requestAnimation', }, CANCEL_ANIMATION: { actions: 'cancelAnimation', }, SET_BACKGROUND_COLOR: { actions: forwardTo('main'), }, TOGGLE_BACKGROUND_COLOR: { actions: forwardTo('main'), }, SET_UNITS: { actions: forwardTo('main'), }, TAKE_SCREENSHOT: { actions: forwardTo('main'), }, TOGGLE_ROTATE: { actions: forwardTo('main'), }, TOGGLE_ANNOTATIONS: { actions: forwardTo('main'), }, TOGGLE_AXES: { actions: forwardTo('main'), }, TOGGLE_CROPPING_PLANES: { actions: forwardTo('main'), }, RESET_CROPPING_PLANES: { actions: forwardTo('main'), }, CROPPING_PLANES_CHANGED: { actions: forwardTo('main'), }, CROPPING_PLANES_CHANGED_BY_USER: { actions: forwardTo('images'), }, VIEW_MODE_CHANGED: { actions: forwardTo('main'), }, SLICING_PLANES_CHANGED: { actions: forwardTo('main'), }, X_SLICE_CHANGED: { actions: forwardTo('main'), }, Y_SLICE_CHANGED: { actions: forwardTo('main'), }, Z_SLICE_CHANGED: { actions: forwardTo('main'), }, RESET_CAMERA: { actions: forwardTo('main'), }, SELECT_LAYER: { actions: forwardTo('images'), }, TOGGLE_LAYER_VISIBILITY: { actions: send((_, e) => e, { to: (c, e) => { switch (c.layers.actorContext.get(e.data).type) { case 'image': return 'images' case 'labelImage': return 'images' default: console.error(`Unsupported layer type for ${e.data}`) } }, }), }, IMAGE_ASSIGNED: { actions: forwardTo('images'), }, UPDATE_RENDERED_IMAGE: { actions: forwardTo('images'), }, TOGGLE_IMAGE_INTERPOLATION: { actions: forwardTo('images'), }, IMAGE_COLOR_RANGE_BOUNDS_CHANGED: { actions: forwardTo('images'), }, IMAGE_COLOR_RANGE_CHANGED: { actions: forwardTo('images'), }, IMAGE_COLOR_RANGE_MIN_CHANGED: { actions: forwardTo('images'), }, IMAGE_COLOR_RANGE_MAX_CHANGED: { actions: forwardTo('images'), }, IMAGE_COLOR_RANGE_POINTS_CHANGED: { actions: forwardTo('images'), }, IMAGE_COLOR_MAP_CHANGED: { actions: forwardTo('images'), }, IMAGE_COMPONENT_VISIBILITY_CHANGED: { actions: forwardTo('images'), }, IMAGE_PIECEWISE_FUNCTION_CHANGED: { actions: forwardTo('images'), }, IMAGE_PIECEWISE_FUNCTION_POINTS_CHANGED: { actions: forwardTo('images'), }, TOGGLE_IMAGE_SHADOW: { actions: forwardTo('images'), }, IMAGE_GRADIENT_OPACITY_CHANGED: { actions: forwardTo('images'), }, IMAGE_GRADIENT_OPACITY_SCALE_CHANGED: { actions: forwardTo('images'), }, IMAGE_VOLUME_SAMPLE_DISTANCE_CHANGED: { actions: forwardTo('images'), }, IMAGE_BLEND_MODE_CHANGED: { actions: forwardTo('images'), }, LABEL_IMAGE_ASSIGNED: { actions: forwardTo('images'), }, TOGGLE_DISTANCE_WIDGET: { actions: forwardTo('widgets'), }, LABEL_IMAGE_LOOKUP_TABLE_CHANGED: { actions: forwardTo('images'), }, LABEL_IMAGE_BLEND_CHANGED: { actions: forwardTo('images'), }, LABEL_IMAGE_WEIGHTS_CHANGED: { actions: forwardTo('images'), }, LABEL_IMAGE_LABEL_NAMES_CHANGED: { actions: forwardTo('images'), }, LABEL_IMAGE_SELECTED_LABEL_CHANGED: { actions: forwardTo('images'), }, UPDATE_IMAGE_HISTOGRAM: { actions: forwardTo('images'), }, SET_FIXED_IMAGE: { actions: forwardTo('images'), }, COMPARE_IMAGES: { actions: forwardTo('images'), }, ANIMATE_IMAGE_MIX: { actions: forwardTo('images'), }, WINDOW_LEVEL_TOGGLED: { actions: forwardTo('images'), }, IMAGE_COLOR_RANGE_RESET: { actions: forwardTo('images'), }, TOGGLE_LAYER_BBOX: { actions: forwardTo('images'), }, DOWNLOAD_IMAGE: { actions: forwardTo('images'), }, }, }, }, }, options ) } export default createRenderingMachine ================================================ FILE: src/Rendering/updateLabelMapComponentWeight.js ================================================ function updateLabelMapComponentWeight(store) { if (!store.imageUI.haveLabelMap) { return } const labelMapBlend = store.imageUI.labelMapBlend const volume = store.imageUI.representationProxy.getVolumes()[0] const volumeProperty = volume.getProperty() const sliceActor = store.imageUI.representationProxy.getActors()[0] const sliceProperty = sliceActor.getProperty() const numberOfComponents = store.imageUI.numberOfComponents const visualizedComponents = store.imageUI.visualizedComponents const componentVisibilities = store.imageUI.componentVisibilities visualizedComponents.forEach((componentIdx, fusedImgIdx) => { const componentVisibility = componentVisibilities[componentIdx] componentVisibility.weight = 1.0 - labelMapBlend if (componentVisibility.visible) { volumeProperty.setComponentWeight(fusedImgIdx, componentVisibility.weight) sliceProperty.setComponentWeight(fusedImgIdx, componentVisibility.weight) } }) volumeProperty.setComponentWeight(numberOfComponents, labelMapBlend) sliceProperty.setComponentWeight(numberOfComponents, labelMapBlend) } export default updateLabelMapComponentWeight ================================================ FILE: src/UI/Images/createImagesUIMachine.js ================================================ import { Machine, assign, forwardTo } from 'xstate' import { createTransferFunctionManipulators } from './transferFunctionManipulators' const assignSelectedComponentIndex = assign({ images: (context, event) => { const images = context.images const name = event.data.name const actorContext = context.images.actorContext.get(name) actorContext.selectedComponent = event.data.component return images }, }) const assignComponentVisibility = assign({ images: (context, event) => { const images = context.images const name = event.data.name const actorContext = context.images.actorContext.get(name) const componentVisibilities = actorContext.componentVisibilities const index = event.data.component const visibility = event.data.visibility if (visibility && !componentVisibilities[index]) { // A component was made visible, and it was not already in the list // of visualized components const currentNumVisualized = componentVisibilities.reduce( (count, isVisible) => count + isVisible, 0 ) if (currentNumVisualized + 1 > actorContext.maxIntensityComponents) { // Replace last touched component with turned on component componentVisibilities[ actorContext.lastComponentVisibilityChanged ] = false } } if (visibility) actorContext.lastComponentVisibilityChanged = index componentVisibilities[index] = visibility return images }, }) const assignPiecewiseFunction = assign({ images: (context, event) => { const images = context.images const name = event.data.name const actorContext = context.images.actorContext.get(name) const component = event.data.component const range = event.data.range const nodes = event.data.nodes actorContext.piecewiseFunctions.set(component, { range, nodes }) return images }, }) const assignPiecewiseFunctionGaussians = assign({ images: (context, event) => { const images = context.images const name = event.data.name const actorContext = context.images.actorContext.get(name) const component = event.data.component const gaussians = event.data.gaussians actorContext.piecewiseFunctionGaussians.set(component, gaussians) return images }, }) const assignPiecewiseFunctionPoints = assign({ images: ( { images }, { data: { component, points, name, keepAutoAdjusting = true } } ) => { const { piecewiseFunctionPoints, piecewiseFunctionPointsAutoAdjust, } = images.actorContext.get(name) piecewiseFunctionPoints.set(component, points) piecewiseFunctionPointsAutoAdjust.set( component, piecewiseFunctionPointsAutoAdjust.get(component) && keepAutoAdjusting ) return images }, }) const assignColorMap = assign({ images: (context, event) => { const images = context.images const name = event.data.name const component = event.data.component const colorMap = event.data.colorMap const actorContext = context.images.actorContext.get(name) actorContext.colorMaps.set(component, colorMap) return images }, }) const assignLookupTable = assign({ images: (context, event) => { const images = context.images const name = event.data.name const lookupTable = event.data.lookupTable const actorContext = context.images.actorContext.get(name) actorContext.lookupTable = lookupTable return images }, }) const assignShadowEnabled = assign({ images: (context, event) => { const images = context.images const name = event.data const actorContext = context.images.actorContext.get(name) actorContext.shadowEnabled = !actorContext.shadowEnabled return images }, }) const assignInterpolationEnabled = assign({ images: (context, event) => { const images = context.images const name = event.data const actorContext = context.images.actorContext.get(name) actorContext.interpolationEnabled = !actorContext.interpolationEnabled return images }, }) const assignGradientOpacity = assign({ images: (context, event) => { const images = context.images const name = event.data.name const gradientOpacity = event.data.gradientOpacity const actorContext = context.images.actorContext.get(name) actorContext.gradientOpacity = gradientOpacity return images }, }) const assignGradientOpacityScale = assign({ images: (context, event) => { const images = context.images const name = event.data.name const gradientOpacityScale = event.data.gradientOpacityScale const actorContext = context.images.actorContext.get(name) actorContext.gradientOpacityScale = gradientOpacityScale return images }, }) const assignVolumeSampleDistance = assign({ images: (context, event) => { const images = context.images const name = event.data.name const volumeSampleDistance = event.data.volumeSampleDistance const actorContext = context.images.actorContext.get(name) actorContext.volumeSampleDistance = volumeSampleDistance return images }, }) const assignBlendMode = assign({ images: (context, event) => { const images = context.images const name = event.data.name const blendMode = event.data.blendMode const actorContext = context.images.actorContext.get(name) actorContext.blendMode = blendMode return images }, }) const assignLabelImageBlend = assign({ images: (context, event) => { const images = context.images const name = event.data.name const labelImageBlend = event.data.labelImageBlend const actorContext = context.images.actorContext.get(name) actorContext.labelImageBlend = labelImageBlend return images }, }) const assignLabelImageWeights = assign({ images: (context, event) => { const images = context.images const name = event.data.name const labelImageWeights = event.data.labelImageWeights const actorContext = context.images.actorContext.get(name) actorContext.labelImageWeights = labelImageWeights return images }, }) const assignLabelNames = assign({ images: (context, event) => { const images = context.images const name = event.data.name const labelNames = event.data.labelNames const actorContext = context.images.actorContext.get(name) actorContext.labelNames = labelNames return images }, }) const assignSelectedLabel = assign({ images: (context, event) => { const images = context.images const name = event.data.name const selectedLabel = event.data.selectedLabel const actorContext = context.images.actorContext.get(name) actorContext.selectedLabel = selectedLabel return images }, }) const assignWindowLevelEnabled = assign({ images: (context, event) => { const images = context.images const name = event.data.name const actorContext = context.images.actorContext.get(name) actorContext.windowLevelEnabled = !actorContext.windowLevelEnabled return images }, }) function createImagesUIMachine(options, context) { return Machine( { id: 'images', initial: 'idle', context, states: { idle: { on: { IMAGE_ASSIGNED: { target: 'active', actions: ['createImagesInterface', 'updateImageInterface'], }, LABEL_IMAGE_ASSIGNED: { target: 'active', actions: ['createImagesInterface', 'updateLabelImageInterface'], }, }, }, active: { invoke: [ { id: 'scaleSelector', src: 'scaleSelector', }, { id: 'transferFunctionManipulators', src: createTransferFunctionManipulators, }, ], on: { IMAGE_ASSIGNED: { actions: [ 'updateImageInterface', 'updateLabelImageInterface', forwardTo('scaleSelector'), ], }, RENDERED_IMAGE_ASSIGNED: { actions: [ 'updateRenderedImageInterface', forwardTo('scaleSelector'), ], }, IMAGE_RENDERING_ACTIVE: { actions: forwardTo('scaleSelector'), }, TOGGLE_IMAGE_INTERPOLATION: { actions: [assignInterpolationEnabled, 'toggleInterpolation'], }, SELECT_IMAGE_COMPONENT: { actions: [ assignSelectedComponentIndex, 'selectImageComponent', forwardTo('transferFunctionManipulators'), ], }, IMAGE_COMPONENT_VISIBILITY_CHANGED: { actions: [assignComponentVisibility, 'applyComponentVisibility'], }, IMAGE_PIECEWISE_FUNCTION_CHANGED: { actions: assignPiecewiseFunction, }, IMAGE_PIECEWISE_FUNCTION_GAUSSIANS_CHANGED: { actions: [ assignPiecewiseFunctionGaussians, 'applyPiecewiseFunctionGaussians', ], }, IMAGE_PIECEWISE_FUNCTION_POINTS_CHANGED: { actions: [ assignPiecewiseFunctionPoints, 'applyPiecewiseFunctionPointsToEditor', ], }, IMAGE_COLOR_RANGE_CHANGED: { actions: [ 'applyColorRange', forwardTo('transferFunctionManipulators'), ], }, IMAGE_COLOR_RANGE_BOUNDS_CHANGED: { actions: ['applyColorRangeBounds'], }, IMAGE_COLOR_MAP_CHANGED: { actions: assignColorMap, }, IMAGE_COLOR_MAP_DEPENDENCIES_UPDATE: { actions: 'applyColorMap', }, TOGGLE_IMAGE_SHADOW: { actions: [assignShadowEnabled, 'toggleShadow'], }, IMAGE_GRADIENT_OPACITY_CHANGED: { actions: [assignGradientOpacity, 'applyGradientOpacity'], }, IMAGE_GRADIENT_OPACITY_SCALE_CHANGED: { actions: [ assignGradientOpacityScale, 'applyGradientOpacityScale', ], }, IMAGE_VOLUME_SAMPLE_DISTANCE_CHANGED: { actions: [ assignVolumeSampleDistance, 'applyVolumeSampleDistance', ], }, IMAGE_BLEND_MODE_CHANGED: { actions: [assignBlendMode, 'applyBlendMode'], }, IMAGE_HISTOGRAM_UPDATED: { actions: ['applyHistogram'], }, LABEL_IMAGE_ASSIGNED: { actions: ['updateLabelImageInterface'], }, LABEL_IMAGE_LOOKUP_TABLE_CHANGED: { actions: [assignLookupTable, 'applyLookupTable'], }, LABEL_IMAGE_BLEND_CHANGED: { actions: [assignLabelImageBlend, 'applyLabelImageBlend'], }, LABEL_IMAGE_WEIGHTS_CHANGED: { actions: [assignLabelImageWeights, 'applyLabelImageWeights'], }, LABEL_IMAGE_LABEL_NAMES_CHANGED: { actions: [assignLabelNames, 'applyLabelNames'], }, LABEL_IMAGE_SELECTED_LABEL_CHANGED: { actions: [assignSelectedLabel, 'applySelectedLabel'], }, CINEMATIC_CHANGED: { actions: 'applyCinematicChanged' }, COMPONENT_VISIBILITIES_UPDATED: { actions: 'updateImageInterface', }, WINDOW_LEVEL_TOGGLED: { actions: [ assignWindowLevelEnabled, 'toggleWindowLevel', forwardTo('transferFunctionManipulators'), ], }, IMAGE_COLOR_RANGE_RESET: { actions: ['applyWindowLevelReset'], }, }, }, }, }, // need scaleSelector service stub to avoid errors if overridden options does not define { services: { scaleSelector: () => () => undefined }, ...options } ) } export default createImagesUIMachine ================================================ FILE: src/UI/Images/transferFunctionManipulators.js ================================================ import vtkMouseRangeManipulator from 'vtk.js/Sources/Interaction/Manipulators/MouseRangeManipulator' const MIN_WINDOW = 1e-8 export const createTransferFunctionManipulators = context => ( callback, onReceive ) => { const getPoints = () => { const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const component = actorContext.selectedComponent return actorContext.piecewiseFunctionPoints.get(component) } const setPoints = points => { const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const component = actorContext.selectedComponent context.service.send({ type: 'IMAGE_PIECEWISE_FUNCTION_POINTS_CHANGED', data: { name, component, points, keepAutoAdjusting: false, }, }) } const newRange = (width, level) => { return [level - width / 2, level + width / 2] } const windowGet = () => { const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const component = actorContext.selectedComponent const [lower, upper] = actorContext.colorRanges.get(component) return upper - lower } const windowSet = newWidth => { const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const component = actorContext.selectedComponent context.service.send({ type: 'IMAGE_COLOR_RANGE_CHANGED', data: { name: context.images.selectedName, component, range: newRange(newWidth, levelGet()), }, }) } const levelGet = () => { const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const component = actorContext.selectedComponent const [lower, upper] = actorContext.colorRanges.get(component) return (upper + lower) / 2 } const levelSet = newLevel => { const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const component = actorContext.selectedComponent context.service.send({ type: 'IMAGE_COLOR_RANGE_CHANGED', data: { name: context.images.selectedName, component, range: newRange(windowGet(), newLevel), }, }) } // pan const manipulator2D = context.itkVtkView .getInteractorStyle2D() .findMouseManipulator(1, false, false, false) // rotate const manipulator3D = context.itkVtkView .getInteractorStyle3D() .findMouseManipulator(1, false, false, false) // window/level const rangeManipulator = vtkMouseRangeManipulator.newInstance({ button: 1, }) const pwfGet = () => { const opacities = getPoints().map(([, y]) => y) return Math.max(...opacities) } const pwfSet = newMaxOpacity => { const oldMax = pwfGet() const delta = newMaxOpacity - oldMax const newPoints = getPoints().map(([x, y]) => [x, Math.min(y + delta, 1)]) setPoints(newPoints) } // opacity up/down const pwfRangeManipulator = vtkMouseRangeManipulator.newInstance({ button: 3, // Right mouse alt: true, }) const pwfRangeManipulatorShift = vtkMouseRangeManipulator.newInstance({ button: 1, // Left mouse shift: true, // For the macOS folks alt: true, }) // max as 1.01 not 1.0 to allow for squishing of low function points if a point is already at 1 pwfRangeManipulator.setVerticalListener(0, 1.01, 0.01, pwfGet, pwfSet) pwfRangeManipulatorShift.setVerticalListener(0, 1.01, 0.01, pwfGet, pwfSet) ;[rangeManipulator, pwfRangeManipulator, pwfRangeManipulatorShift].forEach( m => { context.itkVtkView.getInteractorStyle2D().addMouseManipulator(m) context.itkVtkView.getInteractorStyle3D().addMouseManipulator(m) } ) const applyColorRange = (context, event) => { const name = event.data.name const component = event.data.component const actorContext = context.images.actorContext.get(name) if ( name !== context.images.selectedName || component !== actorContext.selectedComponent ) { return } const colorRange = event.data.range let fullRange = colorRange if (actorContext.colorRangeBounds.has(component)) { fullRange = actorContext.colorRangeBounds.get(component) } const diff = fullRange[1] - fullRange[0] // level rangeManipulator.setVerticalListener( fullRange[0] - diff, fullRange[1] + diff, Math.min(diff, 1) / 256, levelGet, levelSet ) // window rangeManipulator.setHorizontalListener( MIN_WINDOW, diff * 2, Math.min(diff, 1) / 256, windowGet, windowSet ) } onReceive(event => { const { type } = event const name = event.data.name const actorContext = context.images.actorContext.get(name) const component = event.data.component if (type === 'WINDOW_LEVEL_TOGGLED') { if (actorContext.windowLevelEnabled) { context.itkVtkView .getInteractorStyle2D() .removeMouseManipulator(manipulator2D) context.itkVtkView .getInteractorStyle3D() .removeMouseManipulator(manipulator3D) context.itkVtkView .getInteractorStyle2D() .addMouseManipulator(rangeManipulator) context.itkVtkView .getInteractorStyle3D() .addMouseManipulator(rangeManipulator) } else { context.itkVtkView .getInteractorStyle2D() .removeMouseManipulator(rangeManipulator) context.itkVtkView .getInteractorStyle3D() .removeMouseManipulator(rangeManipulator) context.itkVtkView .getInteractorStyle2D() .addMouseManipulator(manipulator2D) context.itkVtkView .getInteractorStyle3D() .addMouseManipulator(manipulator3D) } } if ( actorContext.windowLevelEnabled && (type === 'SELECT_IMAGE_COMPONENT' || type === 'IMAGE_COLOR_RANGE_CHANGED' || type === 'WINDOW_LEVEL_TOGGLED') ) { if ( actorContext.selectedComponent === component && actorContext.colorRanges.has(component) ) { const range = actorContext.colorRanges.get(component) applyColorRange(context, { data: { name, component, range, }, }) } } }) } ================================================ FILE: src/UI/Layers/createLayerUIActor.js ================================================ import { Machine, assign } from 'xstate' const assignLayerVisibility = assign({ layers: (context, event) => { const layers = context.layers const name = event.data const actorContext = layers.actorContext.get(name) actorContext.visible = !actorContext.visible layers.actorContext.set(name, actorContext) return layers }, }) const createLayerUIActor = (options, context, actorContext) => { return Machine( { id: 'layerUI', initial: 'active', context: { actorContext, ...context }, states: { active: { entry: 'createLayerInterface', on: { SELECT_LAYER: { actions: 'selectLayer', }, TOGGLE_LAYER_VISIBILITY: { actions: [assignLayerVisibility, 'toggleLayerVisibility'], }, }, initial: 'idle', states: { idle: { entry: 'finishDataUpdate', on: { START_DATA_UPDATE: 'dataUpdating' }, }, dataUpdating: { entry: 'startDataUpdate', on: { FINISH_DATA_UPDATE: 'dataLoading' }, }, dataLoading: { // wait until data is loaded on GPU on: { POST_RENDER: 'idle' }, }, }, }, }, }, options ) } export default createLayerUIActor ================================================ FILE: src/UI/Layers/createLayersUIMachine.js ================================================ import { Machine, assign, spawn, send, actions, forwardTo } from 'xstate' import { PixelTypes } from 'itk-wasm' import createLayerUIActor from './createLayerUIActor' import LayerActorContext from '../../Context/LayerActorContext' import ImageActorContext from '../../Context/ImageActorContext' import { getOutputIntensityComponentCount } from '../../Rendering/Images/createImageRenderingActor' function resize(arr, newSize, defaultValue) { return [ ...arr, ...Array(Math.max(newSize - arr.length, 0)).fill(defaultValue), ] } function spawnLayerRenderingActor(options) { return assign({ layers: (context, event) => { const layers = context.layers switch (event.type) { case 'ADD_IMAGE': { let name = event.data.name // Ensure unique name let nameNumber = 0 while (layers.layerUIActors.has(name)) { name = `${event.data.name}-${nameNumber + 1}` nameNumber++ } const actorContext = layers.actorContext.get(name) ?? new LayerActorContext() actorContext.type = 'image' actorContext.bbox = false layers.actorContext.set(name, actorContext) layers.lastAddedData = { name, data: event.data } layers.layerUIActors.set( name, spawn( createLayerUIActor(options, context, actorContext), `layerUIActor-${name}`, actorContext ) ) break } case 'ADD_LABEL_IMAGE': { let name = event.data.labelImage.name // Ensure unique name let nameNumber = 0 while (layers.layerUIActors.has(name)) { name = `${event.data.labelImage.name}-${nameNumber + 1}` nameNumber++ } const actorContext = layers.actorContext.get(name) ?? new LayerActorContext() actorContext.type = 'labelImage' actorContext.bbox = false layers.actorContext.set(name, actorContext) layers.lastAddedData = { name, data: event.data } layers.layerUIActors.set( name, spawn( createLayerUIActor(options, context, actorContext), `layerUIActor-${name}` ) ) break } default: throw new Error(`Unexpected event type: ${event.type}`) } return layers }, }) } const assignImageContext = assign({ images: context => { const images = context.images let actorName = null let image = null let labelImage = null let imageName = null let labelImageName = null if ('labelImage' in context.layers.lastAddedData.data) { labelImage = context.layers.lastAddedData.data.labelImage imageName = context.layers.lastAddedData.data.imageName ?? context.images.selectedName actorName = imageName ?? context.layers.lastAddedData.data.labelImage.name labelImageName = context.layers.lastAddedData.name } else { imageName = context.layers.lastAddedData.name // find Map key with ImageActorContext with matching image name. Needed if image loaded after labelImage const [keyName] = Array.from(images.actorContext.entries()).find( ([, { imageName: actorImageName }]) => actorImageName === imageName ) ?? [] actorName = keyName ?? imageName } images.selectedName = actorName const actorContext = images.actorContext.get(actorName) ?? new ImageActorContext() let layerContext if (labelImage) { if ( actorContext.image && labelImage.imageType.dimension !== actorContext.image.imageType.dimension ) throw new Error('Label image dimensions do not match Image dimensions') actorContext.labelImage = labelImage actorContext.labelImageName = labelImageName layerContext = context.layers.actorContext.get(labelImageName) actorContext.imageName = imageName ?? 'Image' } else { if ( actorContext.labelImage && image.imageType.dimensions !== actorContext.labelImage.imageType.dimensions ) throw new Error('Label image dimensions do not match Image dimensions') image = context.layers.lastAddedData.data actorContext.image = image layerContext = context.layers.actorContext.get(imageName) } layerContext.imageActorContext = actorContext images.actorContext.set(actorName, actorContext) if (image === null) { return images } const components = image.imageType.components // Assign default independentComponents if (actorContext.independentComponents === null) { // If a 2D RGB image if ( image.imageType.pixelType === PixelTypes.RGB || image.imageType.pixelType === PixelTypes.RGBA ) { actorContext.independentComponents = false } else { actorContext.independentComponents = true } } // Assign default colorMaps for (let component = 0; component < components; component++) { if (actorContext.colorMaps.has(component)) { continue } let colorMap = 'CT-Chest-Vessels' // If a 2D RGB or RGBA if (components === 2) { switch (component) { case 0: colorMap = 'BkMa' break case 1: colorMap = 'BkCy' break } } else if (actorContext.independentComponents) { if (components === 3) { switch (component) { case 0: colorMap = 'BkRd' break case 1: colorMap = 'BkGn' break case 2: colorMap = 'BkBu' break } } else if (components >= 4) { switch (component) { case 0: colorMap = 'BkRd' break case 1: colorMap = 'BkGn' break case 2: colorMap = 'BkBu' break case 3: colorMap = 'Grayscale' break } } } actorContext.colorMaps.set(component, colorMap) } for (let component = 0; component < components; component++) { if (!actorContext.piecewiseFunctionPoints.has(component)) { // Assign default piecewiseFunction const points = context.use2D ? [[0.5, 1]] : [ [0, 0], [0.9, 0.9], ] if (context.use2D && components === 1) { // For 2D ImageMapper, if multiple components, // opacity function sets component contribution factor. // If just 1 component, opacity function is irrelevant. points.length = 0 } actorContext.piecewiseFunctionPoints.set(component, points) } actorContext.colorRanges.set(component, [0.2, 0.8]) } // make Maps like this: { 0: true, 1: true, ... n: true} const trueForAll = [...Array(components).keys()].map(c => [c, true]) actorContext.colorRangeMinAutoAdjust = new Map([...trueForAll]) actorContext.colorRangeMaxAutoAdjust = new Map([...trueForAll]) actorContext.colorRangeBoundsAutoAdjust = new Map([...trueForAll]) actorContext.piecewiseFunctionPointsAutoAdjust = new Map([...trueForAll]) return images }, }) const assignComponentVisibilities = assign({ images: ({ images }, event) => { const actorContext = images.actorContext.get(event.data.name) const componentCount = getOutputIntensityComponentCount(actorContext) actorContext.componentVisibilities = resize( actorContext.componentVisibilities, componentCount, true ) return images }, }) const sendComponentVisibilitiesUpdated = (c, { data: { name } }) => { c.service.send({ type: 'COMPONENT_VISIBILITIES_UPDATED', data: { name }, }) } const assignSelectedName = assign({ images: (context, event) => { const images = context.images const name = event.data const type = context.layers.layersUIActors.get(name).type if (type === 'image' || type === 'labelImage') { images.selectedName = name } return images }, }) const forwardToNamedActor = send((_, e) => e, { to: (c, e) => `layerUIActor-${e.name}`, }) const sendEventToAllActors = actions.pure( ({ layers: { layerUIActors } }, event) => Array.from(layerUIActors?.values() ?? []).map(actor => send(event, { to: actor, }) ) ) function createLayersUIMachine(options, context) { const { layerUIActor } = options return Machine( { id: 'layers', initial: 'idle', context, states: { idle: { always: { target: 'active', actions: ['createLayersInterface'], }, }, active: { invoke: [ { id: 'compareUI', src: 'compareUI', }, ], on: { SELECT_LAYER: { assignSelectedName, actions: [ send((_, e) => e, { to: (c, e) => `layerUIActor-${e.data}`, }), ], }, TOGGLE_LAYER_VISIBILITY: { actions: send((_, e) => e, { to: (c, e) => `layerUIActor-${e.data}`, }), }, ADD_IMAGE: { actions: [ spawnLayerRenderingActor(layerUIActor), assignImageContext, assignComponentVisibilities, c => c.service.send({ type: 'IMAGE_ASSIGNED', data: c.images.selectedName, }), c => c.service.send({ type: 'SELECT_LAYER', data: c.images.selectedName, }), ], }, ADD_LABEL_IMAGE: { actions: [ spawnLayerRenderingActor(layerUIActor), assignImageContext, c => c.service.send({ type: 'LABEL_IMAGE_ASSIGNED', data: c.images.selectedName, }), c => c.service.send({ type: 'SELECT_LAYER', data: c.images.selectedName, }), ], }, START_DATA_UPDATE: { actions: forwardToNamedActor }, FINISH_DATA_UPDATE: { actions: forwardToNamedActor }, POST_RENDER: { actions: sendEventToAllActors }, COMPARE_UPDATED: { actions: [ assignComponentVisibilities, sendComponentVisibilitiesUpdated, send((_, e) => e, { to: (c, e) => `layerUIActor-${e.data.name}`, }), forwardTo('compareUI'), ], }, }, }, }, }, // need service stub to avoid errors if overridden options does not define { services: { compareUI: () => () => undefined }, ...options } ) } export default createLayersUIMachine ================================================ FILE: src/UI/Main/createMainUIMachine.js ================================================ import { Machine, assign } from 'xstate' const assignFullscreenEnabled = assign({ main: (context, event) => { const main = context.main main.fullscreenEnabled = !main.fullscreenEnabled return main }, }) const assignAnnotationsEnabled = assign({ main: (context, event) => { const main = context.main main.annotationsEnabled = !main.annotationsEnabled return main }, }) const assignRotateEnabled = assign({ main: (context, event) => { const main = context.main main.rotateEnabled = !main.rotateEnabled return main }, }) const assignAxesEnabled = assign({ main: (context, event) => { const main = context.main main.axesEnabled = !main.axesEnabled return main }, }) const assignCroppingPlanesEnabled = assign({ main: (context, event) => { const main = context.main main.croppingPlanesEnabled = !main.croppingPlanesEnabled return main }, }) const assignCroppingPlanes = assign({ main: (context, event) => { const main = context.main main.croppingPlanes = event.data return main }, }) const assignViewMode = assign({ main: (context, event) => { const main = context.main main.viewMode = event.data return main }, }) const assignSlicingPlanes = assign({ main: (context, event) => { const main = context.main main.slicingPlanes = event.data return main }, }) const assignXSlice = assign({ main: (context, event) => { const main = context.main main.xSlice = event.data return main }, }) const assignYSlice = assign({ main: (context, event) => { const main = context.main main.ySlice = event.data return main }, }) const assignZSlice = assign({ main: (context, event) => { const main = context.main main.zSlice = event.data return main }, }) function createMainUIMachine(options, context) { let initialViewMode = 'volume' switch (context.main.viewMode) { case 'XPlane': initialViewMode = 'xPlane' break case 'YPlane': initialViewMode = 'yPlane' break case 'ZPlane': initialViewMode = 'zPlane' break case 'Volume': initialViewMode = 'volume' break default: throw new Error(`Invalid initial view mode: ${context.main.viewMode}`) } return Machine( { id: 'main', initial: 'idle', context, states: { idle: { always: { target: 'active', actions: ['createMainInterface'], }, }, active: { type: 'parallel', on: { TOGGLE_BACKGROUND_COLOR: { actions: 'toggleBackgroundColor', }, SLICING_PLANES_CHANGED: { actions: [assignSlicingPlanes, 'applySlicingPlanes'], }, X_SLICE_CHANGED: { actions: [assignXSlice, 'applyXSlice'], }, Y_SLICE_CHANGED: { actions: [assignYSlice, 'applyYSlice'], }, Z_SLICE_CHANGED: { actions: [assignZSlice, 'applyZSlice'], }, CROPPING_PLANES_CHANGED: { actions: assignCroppingPlanes, }, }, states: { fullscreen: { initial: context.fullscreenEnabled ? 'enabled' : 'disabled', states: { enabled: { exit: 'toggleFullscreen', on: { TOGGLE_FULLSCREEN: { target: 'disabled', actions: assignFullscreenEnabled, }, DISABLE_FULLSCREEN: { target: 'disabled', actions: assignFullscreenEnabled, }, }, }, disabled: { exit: 'toggleFullscreen', on: { TOGGLE_FULLSCREEN: { target: 'enabled', actions: assignFullscreenEnabled, }, }, }, }, }, annotations: { initial: context.annotationsEnabled ? 'enabled' : 'disabled', states: { enabled: { entry: 'toggleAnnotations', on: { TOGGLE_ANNOTATIONS: { target: 'disabled', actions: assignAnnotationsEnabled, }, }, }, disabled: { entry: 'toggleAnnotations', on: { TOGGLE_ANNOTATIONS: { target: 'enabled', actions: assignAnnotationsEnabled, }, }, }, }, }, rotate: { initial: context.rotateEnabled ? 'enabled' : 'disabled', states: { enabled: { entry: 'toggleRotate', on: { TOGGLE_ROTATE: { target: 'disabled', actions: assignRotateEnabled, }, }, }, disabled: { entry: 'toggleRotate', on: { TOGGLE_ROTATE: { target: 'enabled', actions: assignRotateEnabled, }, }, }, }, }, axes: { initial: context.axesEnabled ? 'enabled' : 'disabled', states: { enabled: { entry: 'toggleAxes', on: { TOGGLE_AXES: { target: 'disabled', actions: assignAxesEnabled, }, }, }, disabled: { entry: 'toggleAxes', on: { TOGGLE_AXES: { target: 'enabled', actions: assignAxesEnabled, }, }, }, }, }, croppingPlanes: { initial: context.croppingPlanesEnabled ? 'enabled' : 'disabled', states: { enabled: { entry: 'toggleCroppingPlanes', on: { TOGGLE_CROPPING_PLANES: { target: 'disabled', actions: assignCroppingPlanesEnabled, }, }, }, disabled: { entry: 'toggleCroppingPlanes', on: { TOGGLE_CROPPING_PLANES: { target: 'enabled', actions: assignCroppingPlanesEnabled, }, }, }, }, }, viewMode: { initial: initialViewMode, states: { xPlane: { entry: 'viewModeXPlane', }, yPlane: { entry: 'viewModeYPlane', }, zPlane: { entry: 'viewModeZPlane', }, volume: { entry: 'viewModeVolume', }, }, on: { VIEW_MODE_CHANGED: [ { target: '.xPlane', cond: (c, e) => e.data === 'XPlane', actions: assignViewMode, }, { target: '.yPlane', cond: (c, e) => e.data === 'YPlane', actions: assignViewMode, }, { target: '.zPlane', cond: (c, e) => e.data === 'ZPlane', actions: assignViewMode, }, { target: '.volume', cond: (c, e) => e.data === 'Volume', actions: assignViewMode, }, ], }, }, }, }, }, }, options ) } export default createMainUIMachine ================================================ FILE: src/UI/Widgets/createWidgetsUIMachine.js ================================================ import { Machine, assign } from 'xstate' const assignDistanceEnabled = assign({ widgets: (context, event) => { const widgets = context.widgets widgets.distanceEnabled = !widgets.distanceEnabled return widgets }, }) const assignDistanceWidgetValue = assign({ widgets: (context, event) => { const widgets = context.widgets widgets.distanceValue = event.data return widgets }, }) function createWidgetsUIMachine(options, context) { let initialViewMode = 'volume' switch (context.main.viewMode) { case 'XPlane': initialViewMode = 'xPlane' break case 'YPlane': initialViewMode = 'yPlane' break case 'ZPlane': initialViewMode = 'zPlane' break case 'Volume': initialViewMode = 'volume' break default: throw new Error(`Invalid initial view mode: ${context.main.viewMode}`) } return Machine( { id: 'widgets', initial: 'idle', context, states: { idle: { always: { target: 'active', actions: ['createWidgetsInterface'], }, }, active: { type: 'parallel', on: { DISTANCE_WIDGET_VALUE_CHANGED: { actions: [assignDistanceWidgetValue, 'applyDistanceWidgetValue'], }, }, states: { distanceWidget: { initial: context.widgets.distanceEnabled ? 'enabled' : 'disabled', states: { enabled: { entry: 'toggleDistanceWidget', on: { TOGGLE_DISTANCE_WIDGET: { target: 'disabled', actions: [assignDistanceEnabled, 'toggleDistanceWidget'], }, }, }, disabled: { entry: 'toggleDistanceWidget', on: { TOGGLE_DISTANCE_WIDGET: { target: 'enabled', actions: [assignDistanceEnabled, 'toggleDistanceWidget'], }, }, }, }, }, viewMode: { initial: initialViewMode, states: { xPlane: { entry: 'viewModeXPlane', }, yPlane: { entry: 'viewModeYPlane', }, zPlane: { entry: 'viewModeZPlane', }, volume: { entry: 'viewModeVolume', }, }, on: { VIEW_MODE_CHANGED: [ { target: '.xPlane', cond: (c, e) => e.data === 'XPlane', }, { target: '.yPlane', cond: (c, e) => e.data === 'YPlane', }, { target: '.zPlane', cond: (c, e) => e.data === 'ZPlane', }, { target: '.volume', cond: (c, e) => e.data === 'Volume', }, ], }, }, }, }, }, }, options ) } export default createWidgetsUIMachine ================================================ FILE: src/UI/addKeyboardShortcuts.js ================================================ import Mousetrap from 'mousetrap' import preventDefaults from '../UserInterface/preventDefaults' const MOUSETRAP = new Mousetrap() const addKeyboardShortcuts = (container, service) => { container.addEventListener('mouseenter', () => { MOUSETRAP.bind('1', function(event /*, combo*/) { preventDefaults(event) service.send({ type: 'VIEW_MODE_CHANGED', data: 'XPlane' }) }) MOUSETRAP.bind('alt+1', function(event /*, combo*/) { preventDefaults(event) service.send({ type: 'VIEW_MODE_CHANGED', data: 'XPlane' }) }) MOUSETRAP.bind('2', function(event /*, combo*/) { preventDefaults(event) service.send({ type: 'VIEW_MODE_CHANGED', data: 'YPlane' }) }) MOUSETRAP.bind('alt+2', function(event /*, combo*/) { preventDefaults(event) service.send({ type: 'VIEW_MODE_CHANGED', data: 'YPlane' }) }) MOUSETRAP.bind('3', function(event /*, combo*/) { preventDefaults(event) service.send({ type: 'VIEW_MODE_CHANGED', data: 'ZPlane' }) }) MOUSETRAP.bind('alt+3', function(event /*, combo*/) { preventDefaults(event) service.send({ type: 'VIEW_MODE_CHANGED', data: 'ZPlane' }) }) MOUSETRAP.bind('4', function(event /*, combo*/) { preventDefaults(event) service.send({ type: 'VIEW_MODE_CHANGED', data: 'Volume' }) }) MOUSETRAP.bind('alt+4', function(event /*, combo*/) { preventDefaults(event) service.send({ type: 'VIEW_MODE_CHANGED', data: 'Volume' }) }) MOUSETRAP.bind('r', function(event /*, combo*/) { preventDefaults(event) service.send('RESET_CAMERA') }) MOUSETRAP.bind('alt+r', function(event /*, combo*/) { preventDefaults(event) service.send('RESET_CAMERA') }) MOUSETRAP.bind('p', function(event /*, combo*/) { preventDefaults(event) service.send('RESET_CAMERA') }) MOUSETRAP.bind('alt+p', function(event /*, combo*/) { preventDefaults(event) service.send('RESET_CAMERA') }) MOUSETRAP.bind('e', function(event) { preventDefaults(event) service.send('RESET_CROPPING_PLANES') }) MOUSETRAP.bind('alt+e', function(event) { preventDefaults(event) service.send('RESET_CROPPING_PLANES') }) MOUSETRAP.bind('w', function(event) { preventDefaults(event) service.send('TOGGLE_CROPPING_PLANES') }) MOUSETRAP.bind('alt+w', function(event) { preventDefaults(event) service.send('TOGGLE_CROPPING_PLANES') }) MOUSETRAP.bind('q', function(event /*, combo*/) { preventDefaults(event) service.send('TOGGLE_UI_COLLAPSED') }) MOUSETRAP.bind('alt+q', function(event /*, combo*/) { preventDefaults(event) service.send('TOGGLE_UI_COLLAPSED') }) MOUSETRAP.bind('f', function(event /*, combo*/) { preventDefaults(event) service.send('TOGGLE_FULLSCREEN') }) MOUSETRAP.bind('alt+f', function(event /*, combo*/) { preventDefaults(event) service.send('TOGGLE_FULLSCREEN') }) //MOUSETRAP.bind('u', function(event /*, combo*/) { //preventDefaults(event) //const toggleFullscreenButton = document.getElementById( //`${viewerDOMId}-toggleFullscreenButton` //) //toggleFullscreenButton.click() //}) //MOUSETRAP.bind('alt+u', function(event /*, combo*/) { //preventDefaults(event) //const toggleFullscreenButton = document.getElementById( //`${viewerDOMId}-toggleFullscreenButton` //) //toggleFullscreenButton.click() //}) //MOUSETRAP.bind('s', function(event /*, combo*/) { //preventDefaults(event) //const toggleSlicingPlanesButton = document.getElementById( //`${viewerDOMId}-toggleSlicingPlanesButton` //) //toggleSlicingPlanesButton.click() //}) //MOUSETRAP.bind('alt+s', function(event /*, combo*/) { //preventDefaults(event) //const toggleSlicingPlanesButton = document.getElementById( //`${viewerDOMId}-toggleSlicingPlanesButton` //) //toggleSlicingPlanesButton.click() //}) //MOUSETRAP.bind('o', function(event /*, combo*/) { //preventDefaults(event) //const toggleSlicingPlanesButton = document.getElementById( //`${viewerDOMId}-toggleSlicingPlanesButton` //) //toggleSlicingPlanesButton.click() //}) //MOUSETRAP.bind('alt+o', function(event /*, combo*/) { //preventDefaults(event) //const toggleSlicingPlanesButton = document.getElementById( //`${viewerDOMId}-toggleSlicingPlanesButton` //) //toggleSlicingPlanesButton.click() //}) MOUSETRAP.bind('p', function(event /*, combo*/) { preventDefaults(event) service.send('TOGGLE_ROTATE') }) MOUSETRAP.bind('alt+p', function(event /*, combo*/) { preventDefaults(event) service.send('TOGGLE_ROTATE') }) // MOUSETRAP.bind('c', function(event /*, combo*/) { // preventDefaults(event) // const config = context.getConfig() // const blob = new Blob([JSON.stringify(config)], { // type: 'text/plain;charset=utf-8', // }) // saveAs(blob, 'viewerConfig.json') // }) // MOUSETRAP.bind('alt+c', function(event /*, combo*/) { // preventDefaults(event) // const config = context.getConfig() // const blob = new Blob([JSON.stringify(config)], { // type: 'text/plain;charset=utf-8', // }) // saveAs(blob, 'viewerConfig.json') // }) }) container.addEventListener('mouseleave', () => { MOUSETRAP.unbind('1') MOUSETRAP.unbind('alt+1') MOUSETRAP.unbind('2') MOUSETRAP.unbind('alt+2') MOUSETRAP.unbind('3') MOUSETRAP.unbind('alt+3') MOUSETRAP.unbind('4') MOUSETRAP.unbind('alt+4') MOUSETRAP.unbind('r') MOUSETRAP.unbind('alt+r') MOUSETRAP.unbind('p') MOUSETRAP.unbind('alt+p') MOUSETRAP.unbind('e') MOUSETRAP.unbind('alt+e') MOUSETRAP.unbind('w') MOUSETRAP.unbind('alt+w') MOUSETRAP.unbind('.') MOUSETRAP.unbind('alt+.') MOUSETRAP.unbind('w') MOUSETRAP.unbind('alt+w') MOUSETRAP.unbind(',') MOUSETRAP.unbind('alt+,') MOUSETRAP.unbind("'") MOUSETRAP.unbind("alt+'") MOUSETRAP.unbind('q') MOUSETRAP.unbind('alt+q') MOUSETRAP.unbind('f') MOUSETRAP.unbind('alt+f') MOUSETRAP.unbind('u') MOUSETRAP.unbind('alt+u') MOUSETRAP.unbind('s') MOUSETRAP.unbind('alt+s') MOUSETRAP.unbind('o') MOUSETRAP.unbind('alt+o') MOUSETRAP.unbind('p') MOUSETRAP.unbind('alt+p') }) } export default addKeyboardShortcuts ================================================ FILE: src/UI/createRenderingViewContainers.js ================================================ /* Create the containers for the rendering views. */ function createRenderingViewContainers(context) { // Todo: migrate from ViewerStore // context.container = document.createElement('div'), if (!!!context.rootContainer) { throw new Error('rootContainer must be supplied in the context') } // Todo: migrate container creation from ViewerStore context.renderingViewContainers.set('volume', context.container) } export default createRenderingViewContainers ================================================ FILE: src/UI/createUIMachine.js ================================================ import { Machine, forwardTo } from 'xstate' import createMainUIMachine from './Main/createMainUIMachine' import createLayersUIMachine from './Layers/createLayersUIMachine' import createImagesUIMachine from './Images/createImagesUIMachine' import createWidgetsUIMachine from './Widgets/createWidgetsUIMachine' function createUIMachine(options, context) { const { main, layers, images, widgets } = options const mainMachine = createMainUIMachine(main, context) const layersMachine = createLayersUIMachine(layers, context) const imagesMachine = createImagesUIMachine(images, context) const widgetsMachine = createWidgetsUIMachine(widgets, context) return Machine( { id: 'ui', initial: 'idle', context, states: { idle: { always: { target: 'active', actions: ['createInterface'], }, }, active: { type: 'parallel', invoke: [ { id: 'main', src: mainMachine, }, { id: 'layers', src: layersMachine, }, { id: 'images', src: imagesMachine, }, { id: 'widgets', src: widgetsMachine, }, ], on: { TOGGLE_BACKGROUND_COLOR: { actions: forwardTo('main'), }, TOGGLE_DARK_MODE: { actions: 'toggleDarkMode', }, TOGGLE_FULLSCREEN: { actions: forwardTo('main'), }, DISABLE_FULLSCREEN: { actions: forwardTo('main'), }, TOGGLE_ROTATE: { actions: forwardTo('main'), }, TOGGLE_ANNOTATIONS: { actions: forwardTo('main'), }, TOGGLE_AXES: { actions: forwardTo('main'), }, TOGGLE_CROPPING_PLANES: { actions: forwardTo('main'), }, CROPPING_PLANES_CHANGED: { actions: forwardTo('main'), }, VIEW_MODE_CHANGED: { actions: [forwardTo('main'), forwardTo('widgets')], }, SLICING_PLANES_CHANGED: { actions: forwardTo('main'), }, X_SLICE_CHANGED: { actions: forwardTo('main'), }, Y_SLICE_CHANGED: { actions: forwardTo('main'), }, Z_SLICE_CHANGED: { actions: forwardTo('main'), }, SELECT_LAYER: { actions: forwardTo('layers'), }, TOGGLE_LAYER_VISIBILITY: { actions: forwardTo('layers'), }, ADD_IMAGE: { actions: forwardTo('layers'), }, IMAGE_ASSIGNED: { actions: [forwardTo('images'), forwardTo('widgets')], }, RENDERED_IMAGE_ASSIGNED: { actions: forwardTo('images'), }, IMAGE_RENDERING_ACTIVE: { actions: forwardTo('images'), }, ADD_LABEL_IMAGE: { actions: forwardTo('layers'), }, LABEL_IMAGE_ASSIGNED: { actions: [forwardTo('images')], }, TOGGLE_IMAGE_INTERPOLATION: { actions: forwardTo('images'), }, SELECT_IMAGE_COMPONENT: { actions: forwardTo('images'), }, IMAGE_COMPONENT_VISIBILITY_CHANGED: { actions: forwardTo('images'), }, IMAGE_PIECEWISE_FUNCTION_CHANGED: { actions: forwardTo('images'), }, IMAGE_PIECEWISE_FUNCTION_GAUSSIANS_CHANGED: { actions: forwardTo('images'), }, IMAGE_PIECEWISE_FUNCTION_POINTS_CHANGED: { actions: forwardTo('images'), }, IMAGE_COLOR_RANGE_CHANGED: { actions: forwardTo('images'), }, IMAGE_COLOR_RANGE_BOUNDS_CHANGED: { actions: forwardTo('images'), }, IMAGE_COLOR_MAP_CHANGED: { actions: forwardTo('images'), }, IMAGE_COLOR_MAP_DEPENDENCIES_UPDATE: { actions: forwardTo('images'), }, TOGGLE_IMAGE_SHADOW: { actions: forwardTo('images'), }, IMAGE_GRADIENT_OPACITY_CHANGED: { actions: forwardTo('images'), }, IMAGE_GRADIENT_OPACITY_SCALE_CHANGED: { actions: forwardTo('images'), }, IMAGE_VOLUME_SAMPLE_DISTANCE_CHANGED: { actions: forwardTo('images'), }, IMAGE_BLEND_MODE_CHANGED: { actions: forwardTo('images'), }, IMAGE_HISTOGRAM_UPDATED: { actions: forwardTo('images'), }, LABEL_IMAGE_LOOKUP_TABLE_CHANGED: { actions: forwardTo('images'), }, LABEL_IMAGE_BLEND_CHANGED: { actions: forwardTo('images'), }, LABEL_IMAGE_WEIGHTS_CHANGED: { actions: forwardTo('images'), }, LABEL_IMAGE_LABEL_NAMES_CHANGED: { actions: forwardTo('images'), }, LABEL_IMAGE_SELECTED_LABEL_CHANGED: { actions: forwardTo('images'), }, TOGGLE_DISTANCE_WIDGET: { actions: forwardTo('widgets'), }, DISTANCE_WIDGET_VALUE_CHANGED: { actions: forwardTo('widgets'), }, CINEMATIC_CHANGED: { actions: forwardTo('images'), }, WINDOW_LEVEL_TOGGLED: { actions: forwardTo('images'), }, IMAGE_COLOR_RANGE_RESET: { actions: forwardTo('images'), }, START_DATA_UPDATE: { actions: forwardTo('layers') }, FINISH_DATA_UPDATE: { actions: forwardTo('layers') }, POST_RENDER: { actions: forwardTo('layers') }, COMPARE_UPDATED: { actions: forwardTo('layers'), }, COMPONENT_VISIBILITIES_UPDATED: { actions: forwardTo('images'), }, }, states: { // Optional feature of the user interface uiCollapsed: { initial: context.uiCollapsed ? 'enabled' : 'disabled', states: { enabled: { entry: 'toggleUICollapsed', on: { TOGGLE_UI_COLLAPSED: { target: 'disabled', }, }, }, disabled: { entry: 'toggleUICollapsed', on: { TOGGLE_UI_COLLAPSED: { target: 'enabled', }, }, }, }, }, }, }, }, }, options ) } export default createUIMachine ================================================ FILE: src/UI/reference-ui/.babelrc ================================================ { "presets": [ [ "@babel/preset-env", { "targets": { "browsers": ["last 2 versions"] }, "useBuiltIns": false } ] ], "plugins": [ "@babel/plugin-transform-runtime", ["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/plugin-proposal-class-properties", { "loose": false }] ] } ================================================ FILE: src/UI/reference-ui/package.json ================================================ { "name": "itk-viewer-reference-ui", "version": "11.11.2", "description": "Reference UI for itk-viewer", "module": "dist/referenceUIMachineOptions.js", "scripts": { "build": "rollup -c rollup.config.js", "dev": "rollup -w -c rollup.config.js", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/Kitware/itk-vtk-viewer.git" }, "keywords": [ "vanilla", "ui", "itk-viewer" ], "author": "Matt McCormick", "license": "Apache-2.0", "bugs": { "url": "https://github.com/Kitware/itk-vtk-viewer/issues" }, "homepage": "https://github.com/Kitware/itk-vtk-viewer#readme", "devDependencies": { "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-decorators": "^7.21.0", "@babel/plugin-transform-runtime": "^7.17.0", "@rollup/plugin-commonjs": "^21.0.2", "@rollup/plugin-node-resolve": "^13.0.0", "@rollup/plugin-typescript": "^9.0.2", "autoprefixer": "^10.4.4", "postcss": "^8.4.12", "rollup": "^2.70.1", "rollup-plugin-ignore": "^1.0.10", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-svgo": "^2.0.0", "rollup-plugin-terser": "^7.0.2", "typescript": "^5.3.3" }, "dependencies": { "@babel/runtime": "^7.17.6", "@itk-viewer/icons": "^11.14.1", "@lit-labs/context": "^0.2.0", "@material/web": "1.0.1", "itk-viewer-color-maps": "^1.2.0", "itk-viewer-transfer-function-editor": "^1.6.0", "lit": "^2.4.0", "xstate-lit": "^1.3.1" } } ================================================ FILE: src/UI/reference-ui/rollup.config.js ================================================ // Configuration for bundling the ReferenceUI and derivatives import autoprefixer from 'autoprefixer' import postcss from 'rollup-plugin-postcss' import svgo from 'rollup-plugin-svgo' import { nodeResolve } from '@rollup/plugin-node-resolve' import commonjs from '@rollup/plugin-commonjs' import { terser } from 'rollup-plugin-terser' import { babel } from '@rollup/plugin-babel' import ignore from 'rollup-plugin-ignore' import typescript from '@rollup/plugin-typescript' export default { input: 'src/referenceUIMachineOptions.js', output: [ { file: 'dist/referenceUIMachineOptions.js', format: 'es' }, { file: 'dist/referenceUIMachineOptions.min.js', format: 'es', plugins: [terser()], }, ], plugins: [ // ignore crypto module ignore(['crypto']), commonjs({ transformMixedEsModules: true, }), typescript(), babel({ include: ['src/**'], extensions: ['.js'], exclude: 'node_modules/**', babelHelpers: 'runtime', }), svgo({ raw: true }), postcss({ modules: true, plugins: [autoprefixer], }), nodeResolve({ preferBuiltins: false, browser: true, }), ], } ================================================ FILE: src/UI/reference-ui/src/Images/applyBlendMode.js ================================================ function applyBlendMode(context, event) { const name = event.data.name const blendMode = event.data.blendMode const blendModeLower = blendMode.toLowerCase() switch (blendModeLower) { case 'composite': context.images.blendModeSelector.value = 0 if (!context.use2D) { context.images.volumeRow1.style.display = 'flex' } break case 'maximum': context.images.blendModeSelector.value = 1 context.images.volumeRow1.style.display = 'none' break case 'minimum': context.images.blendModeSelector.value = 2 context.images.volumeRow1.style.display = 'none' break case 'average': context.images.blendModeSelector.value = 3 context.images.volumeRow1.style.display = 'none' break default: throw new Error(`Invalid blend mode: ${blendMode}`) } } export default applyBlendMode ================================================ FILE: src/UI/reference-ui/src/Images/applyColorMap.js ================================================ function applyColorMap(context, { data: { component, name } }) { if (context.images.selectedName !== name) return const actorContext = context.images.actorContext.get(name) if (component === actorContext.selectedComponent) { const colorMap = actorContext.colorMaps.get(component) context.images.iconSelector.setSelectedValue(colorMap) const colorTransferFunction = context.images.colorTransferFunctions?.get( component ) if (colorTransferFunction) { context.images.transferFunctionWidget.setColorTransferFunction( colorTransferFunction ) } if ( actorContext.fusedImage && // When comparing images, getScalars is null first time actorContext.fusedImage.getPointData().getScalars() ) { const range = actorContext.fusedImage .getPointData() .getScalars() .getRange(component) context.images.transferFunctionWidget.setRange(range) } } } export default applyColorMap ================================================ FILE: src/UI/reference-ui/src/Images/applyColorRange.js ================================================ function applyColorRange(context, event) { const name = event.data.name const component = event.data.component const actorContext = context.images.actorContext.get(name) if ( name !== context.images.selectedName || component !== actorContext.selectedComponent ) { return } const colorRange = event.data.range const minimumInput = context.images.colorRangeInputRow.children[1].children[0] const maximumInput = context.images.colorRangeInputRow.children[3].children[0] if (actorContext.windowLevelEnabled) { minimumInput.value = colorRange[1] - colorRange[0] maximumInput.value = (colorRange[1] + colorRange[0]) / 2 } else { minimumInput.value = colorRange[0] maximumInput.value = colorRange[1] } let fullRange = colorRange if (actorContext.colorRangeBounds.has(component)) { fullRange = actorContext.colorRangeBounds.get(component) } if (event.data.fullRange) { // use more up to date colorRangeBounds fullRange = event.data.fullRange } const diff = fullRange[1] - fullRange[0] const colorRangeNormalized = [ (colorRange[0] - fullRange[0]) / diff, (colorRange[1] - fullRange[0]) / diff, ] context.images.transferFunctionWidget.setColorRange(colorRangeNormalized) } export default applyColorRange ================================================ FILE: src/UI/reference-ui/src/Images/applyColorRangeBounds.js ================================================ import applyColorRange from './applyColorRange' function applyColorRangeBounds(context, event) { const { name, component } = event.data const actorContext = context.images.actorContext.get(name) if ( name !== context.images.selectedName || component !== actorContext.selectedComponent ) { return } const range = event.data.range const minimumInput = context.images.colorRangeInputRow.children[1].children[0] const maximumInput = context.images.colorRangeInputRow.children[3].children[0] const image = actorContext.image if ( (image && image.imageType.componentType === 'float') || image.imageType.componentType === 'double' ) { const step = (range[1] - range[0]) / 1000.0 minimumInput.step = step maximumInput.step = step } if (actorContext.colorRanges.has(component)) { applyColorRange(context, { data: { name, component, range: actorContext.colorRanges.get(component), fullRange: range, }, }) } } export default applyColorRangeBounds ================================================ FILE: src/UI/reference-ui/src/Images/applyComponentVisibility.js ================================================ import style from '../ItkVtkViewer.module.css' function applyComponentVisibility(context, event) { const name = event.data.name if (name !== context.images.selectedName) { return } const actorContext = context.images.actorContext.get(name) const componentSelector = context.images.componentSelector actorContext.componentVisibilities.forEach((visibility, compIdx) => { const element = componentSelector.querySelector( `input[data-component-index="${compIdx}"][type="checkbox"]` ) element.checked = visibility }) } export default applyComponentVisibility ================================================ FILE: src/UI/reference-ui/src/Images/applyGradientOpacity.js ================================================ function applyGradientOpacity(context, event) { const name = event.data.name const gradientOpacity = event.data.gradientOpacity context.images.gradientOpacitySlider.value = gradientOpacity } export default applyGradientOpacity ================================================ FILE: src/UI/reference-ui/src/Images/applyGradientOpacityScale.js ================================================ function applyGradientOpacityScale(context, event) { const name = event.data.name const gradientOpacityScale = event.data.gradientOpacityScale context.images.gradientOpacityScaleSlider.value = gradientOpacityScale } export default applyGradientOpacityScale ================================================ FILE: src/UI/reference-ui/src/Images/applyHistogram.js ================================================ function applyHistogram(context, event) { const name = event.data.name const component = event.data.component const actorContext = context.images.actorContext.get(name) if ( name !== context.images.selectedName || component !== actorContext.selectedComponent ) { return } const histogram = event.data.histogram ?? [] context.images.transferFunctionWidget.setHistogram(histogram) } export default applyHistogram ================================================ FILE: src/UI/reference-ui/src/Images/applyImagesContrastSensitiveStyle.js ================================================ import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' function applyImagesContrastSensitiveStyle(context) { if (context.images.distanceButtonLabel) { applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.images.distanceButtonLabel ) applyContrastSensitiveStyleToElement( context, 'distanceLabel', context.images.distanceLabel ) } if (context.images.shadowButtonLabel) { applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.images.shadowButtonLabel ) applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.images.sliderEntryDiv ) applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.images.volumeSampleDistanceDiv ) applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.images.blendModeDiv ) applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.images.labelImageBlendDiv ) applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.images.scaleSelectorIconDiv ) } } export default applyImagesContrastSensitiveStyle ================================================ FILE: src/UI/reference-ui/src/Images/applyLabelImageBlend.js ================================================ function applyLabelImageBlend(context, event) { const name = event.data.name const actorContext = context.images.actorContext.get(name) const labelImageBlend = event.data.labelImageBlend const slider = context.images.labelImageBlendSlider slider.value = labelImageBlend const haveImage = !!actorContext.image if (haveImage) { slider.style.display = 'flex' } else { slider.style.display = 'none' } } export default applyLabelImageBlend ================================================ FILE: src/UI/reference-ui/src/Images/applyLabelImageWeights.js ================================================ function applyLabelImageWeights(context, event) {} export default applyLabelImageWeights ================================================ FILE: src/UI/reference-ui/src/Images/applyLabelNames.js ================================================ function applyLabelNames(context, event) { const name = event.data.name const actorContext = context.images.actorContext.get(name) const labelNames = event.data.labelNames const optionsList = [] labelNames.forEach((name, label) => optionsList.push( `` ) ) optionsList.unshift('') context.images.labelSelector.innerHTML = optionsList.join('') } export default applyLabelNames ================================================ FILE: src/UI/reference-ui/src/Images/applyLookupTable.js ================================================ function applyLookupTable(context, event) { const name = event.data.name const lut = event.data.lookupTable if (name !== context.images.selectedName) { return } if (lut !== context.images.labelImageIconSelector.getSelectedValue()) { context.images.labelImageIconSelector.setSelectedValue(lut) } } export default applyLookupTable ================================================ FILE: src/UI/reference-ui/src/Images/applyPiecewiseFunctionGaussians.js ================================================ function applyPiecewiseFunctionGaussians(context, event) { const images = context.images const name = event.data.name const actorContext = context.images.actorContext.get(name) const component = event.data.component const gaussians = event.data.gaussians const transferFunctionWidget = context.images.transferFunctionWidget transferFunctionWidget.setGaussians(gaussians) } export default applyPiecewiseFunctionGaussians ================================================ FILE: src/UI/reference-ui/src/Images/applySelectedLabel.js ================================================ function applySelectedLabel(context, event) { const name = event.data.name const actorContext = context.images.actorContext.get(name) const selectedLabel = event.data.selectedLabel if (selectedLabel === 'all') { context.images.labelSelector.selectedIndex = 0 // 'All' is first } else { context.images.labelImageWeightSlider.value = actorContext.labelImageWeights.get( selectedLabel ) context.images.labelSelector.selectedIndex = parseInt(selectedLabel) + 1 // 'All' is first } } export default applySelectedLabel ================================================ FILE: src/UI/reference-ui/src/Images/applyVolumeSampleDistance.js ================================================ function applyVolumeSampleDistance(context, event) { const name = event.data.name const volumeSampleDistance = event.data.volumeSampleDistance context.images.volumeSampleDistanceSlider.value = volumeSampleDistance } export default applyVolumeSampleDistance ================================================ FILE: src/UI/reference-ui/src/Images/applyWindowingReset.js ================================================ function applyWindowLevelReset(context, { data }) { const actorContext = context.images.actorContext.get(data.name) const component = actorContext.selectedComponent const bounds = actorContext.colorRangeBounds.get(component) const wMax = bounds[1] - bounds[0] const lMin = bounds[0] const level = wMax / 2 + lMin const width = wMax const newRange = () => { return [level - width / 2, level + width / 2] } context.service.send({ type: 'IMAGE_COLOR_RANGE_CHANGED', data: { name: data.name, component, range: newRange(), }, }) } export default applyWindowLevelReset ================================================ FILE: src/UI/reference-ui/src/Images/cinematic.js ================================================ import style from '../ItkVtkViewer.module.css' import { volumeScatteringIconDataUri } from '@itk-viewer/icons' const sliderMap = new Map() function makeSlider(context, label, parameterName, { min, max, step, start }) { const container = document.createElement('div') container.setAttribute('class', style.sliderEntry) container.innerHTML = `
${label}
` const slider = container.children[1] slider.addEventListener('input', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'SET_CINEMATIC_PARAMETERS', data: { name: context.images.selectedName, params: { [parameterName]: Number(slider.value) }, }, }) }) sliderMap.set(parameterName, slider) return container } export function createCinematicParameters(context, rowParent) { // hidable sliders const row = document.createElement('div') const rootContainer = document.createElement('div') rootContainer.setAttribute('class', style.sliderColumn) row.appendChild(rootContainer) rowParent.appendChild(row) context.images.volumeUiElements.push(row) rootContainer.style.flexDirection = 'column' rootContainer.appendChild( makeSlider(context, 'Volume Scattering', 'scatteringBlend', { min: 0, max: 1, step: 1 / 100, start: 0, }) ) } export function applyCinematicChanged(context, { actorContext }) { const { cinematicParameters } = actorContext sliderMap.get( 'scatteringBlend' ).disabled = !cinematicParameters.isCinematicPossible ;['scatteringBlend'].forEach(param => { sliderMap.get(param).value = cinematicParameters[param] }) } ================================================ FILE: src/UI/reference-ui/src/Images/createBlendModeSelector.js ================================================ import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import { blendModeIconDataUri } from '@itk-viewer/icons' function createBlendModeSelector(context, uiContainer) { const blendModeEntry = document.createElement('div') blendModeEntry.innerHTML = `
blend mode
` const blendModeDiv = blendModeEntry.children[0] context.images.blendModeDiv = blendModeDiv applyContrastSensitiveStyleToElement( context, 'invertibleButton', blendModeDiv ) uiContainer.appendChild(blendModeEntry) const blendModeSelector = document.createElement('select') blendModeSelector.setAttribute('class', style.selector) blendModeSelector.id = `${context.id}-colorMapSelector` blendModeSelector.innerHTML = ` ` blendModeEntry.appendChild(blendModeSelector) context.images.blendModeSelector = blendModeSelector blendModeSelector.addEventListener('change', event => { event.preventDefault() event.stopPropagation() let mode = 'blendmode' switch (parseInt(event.target.value)) { case 0: mode = 'Composite' break case 1: mode = 'Maximum' break case 2: mode = 'Minimum' break case 3: mode = 'Average' break } context.service.send({ type: 'IMAGE_BLEND_MODE_CHANGED', data: { name: context.images.selectedName, blendMode: mode, }, }) }) uiContainer.appendChild(blendModeSelector) } export default createBlendModeSelector ================================================ FILE: src/UI/reference-ui/src/Images/createColorRangeInput.js ================================================ import style from '../ItkVtkViewer.module.css' import createInterpolationButton from './createInterpolationButton' import createColorMapIconSelector from '../createColorMapIconSelector' import createWindowLevelReset from './createWindowLevelReset' function createColorRangeInput(context, imageUIGroup) { const viewerDOMId = context.id const colorRangeInputRow = document.createElement('div') colorRangeInputRow.setAttribute('class', style.uiRow) // This row needs background different from normal uiRows, to aid // in the illusion that it's the content portion of a tabbed pane colorRangeInputRow.setAttribute( 'style', 'background: rgba(127, 127, 127, 0.5);' ) context.images.colorRangeInputRow = colorRangeInputRow createInterpolationButton(context, colorRangeInputRow) const minimumInput = document.createElement('input') minimumInput.type = 'number' minimumInput.setAttribute('class', style.numberInput) const minimumDiv = document.createElement('div') minimumDiv.setAttribute('itk-vtk-tooltip', '') minimumDiv.setAttribute('itk-vtk-tooltip-top-input', '') minimumDiv.setAttribute('itk-vtk-tooltip-content', 'Color range min') minimumDiv.appendChild(minimumInput) const maximumInput = document.createElement('input') maximumInput.type = 'number' maximumInput.setAttribute('class', style.numberInput) const maximumDiv = document.createElement('div') maximumDiv.setAttribute('itk-vtk-tooltip', '') maximumDiv.setAttribute('itk-vtk-tooltip-top-input', '') maximumDiv.setAttribute('itk-vtk-tooltip-content', 'Color range max') maximumDiv.appendChild(maximumInput) minimumInput.addEventListener('change', event => { event.preventDefault() event.stopPropagation() const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const currentRange = actorContext.colorRanges.get( actorContext.selectedComponent ) let newRange = [] if (actorContext.windowLevelEnabled) { const level = (currentRange[1] + currentRange[0]) / 2 const width = Number(event.target.value) newRange = [level - width / 2, level + width / 2] } else { newRange = [Number(event.target.value), currentRange[1]] } context.service.send({ type: 'IMAGE_COLOR_RANGE_CHANGED', data: { name, component: actorContext.selectedComponent, range: newRange, }, }) }) maximumInput.addEventListener('change', event => { event.preventDefault() event.stopPropagation() const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const currentRange = actorContext.colorRanges.get( actorContext.selectedComponent ) let newRange = [] if (actorContext.windowLevelEnabled) { const width = currentRange[1] - currentRange[0] const level = Number(event.target.value) newRange = [level - width / 2, level + width / 2] } else { newRange = [currentRange[0], Number(event.target.value)] } context.service.send({ type: 'IMAGE_COLOR_RANGE_CHANGED', data: { name, component: actorContext.selectedComponent, range: newRange, }, }) }) const colorMapSelector = document.createElement('div') colorMapSelector.id = `${viewerDOMId}-imageColorMapSelector` colorRangeInputRow.appendChild(minimumDiv) colorRangeInputRow.appendChild(colorMapSelector) colorRangeInputRow.appendChild(maximumDiv) const iconSelector = createColorMapIconSelector(colorMapSelector) context.images.iconSelector = iconSelector colorMapSelector.addEventListener('changed', event => { event.preventDefault() event.stopPropagation() const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const componentIndex = actorContext.selectedComponent const colorMap = iconSelector.getSelectedValue() const currentColorMap = actorContext.colorMaps.get(componentIndex) if (currentColorMap !== colorMap) { context.service.send({ type: 'IMAGE_COLOR_MAP_CHANGED', data: { name, component: componentIndex, colorMap }, }) } }) context.images.colorMapSelector = colorMapSelector createWindowLevelReset(context, colorRangeInputRow) imageUIGroup.appendChild(colorRangeInputRow) } export default createColorRangeInput ================================================ FILE: src/UI/reference-ui/src/Images/createComponentSelector.js ================================================ import style from '../ItkVtkViewer.module.css' function createComponentSelector(context, imageUIGroup) { const viewerDOMId = context.id const componentSelector = document.createElement('div') componentSelector.setAttribute('class', style.selector) componentSelector.id = `${viewerDOMId}-componentSelector` context.images.componentSelector = componentSelector const componentRow = document.createElement('div') componentRow.setAttribute('class', style.uiRow) // This row needs custom bottom padding, to aid in the illusion // that it's the tabbed portion of a tabbed pane componentRow.setAttribute('style', 'padding-bottom: 0px;') componentRow.className += ` ${viewerDOMId}-volumeComponents` context.images.componentRow = componentRow componentSelector.addEventListener('change', event => { event.preventDefault() event.stopPropagation() const selectedIndex = Number(event.target.dataset.componentIndex) if (event.target.type === 'radio') { context.service.send({ type: 'SELECT_IMAGE_COMPONENT', data: { name: context.images.selectedName, component: selectedIndex }, }) } else if (event.target.type === 'checkbox') { const visibility = event.target.checked context.service.send({ type: 'IMAGE_COMPONENT_VISIBILITY_CHANGED', data: { name: context.images.selectedName, component: selectedIndex, visibility, }, }) } }) componentRow.appendChild(componentSelector) imageUIGroup.appendChild(componentRow) } export default createComponentSelector ================================================ FILE: src/UI/reference-ui/src/Images/createGradientOpacitySlider.js ================================================ import macro from '@kitware/vtk.js/macro' import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import { gradientIconDataUri } from '@itk-viewer/icons' function createGradientOpacitySlider(context, uiContainer) { const sliderEntry = document.createElement('div') sliderEntry.setAttribute('class', style.sliderEntry) sliderEntry.innerHTML = `
gradient opacity
` const sliderEntryDiv = sliderEntry.children[0] const gradientOpacityScaleDiv = sliderEntry.children[1] const gradientOpacityScaleSlider = gradientOpacityScaleDiv.children[0] const gradientOpacitySlider = sliderEntry.children[2] context.images.sliderEntryDiv = sliderEntryDiv applyContrastSensitiveStyleToElement( context, 'invertibleButton', sliderEntryDiv ) context.images.gradientOpacitySlider = gradientOpacitySlider context.images.gradientOpacityScaleSlider = gradientOpacityScaleSlider sliderEntryDiv.addEventListener('click', event => { if (gradientOpacityScaleDiv.style.display === 'none') { gradientOpacityScaleDiv.style.display = 'block' } else { gradientOpacityScaleDiv.style.display = 'none' } }) gradientOpacitySlider.addEventListener('input', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'IMAGE_GRADIENT_OPACITY_CHANGED', data: { name: context.images.selectedName, gradientOpacity: Number(gradientOpacitySlider.value), }, }) }) gradientOpacityScaleSlider.addEventListener('input', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'IMAGE_GRADIENT_OPACITY_SCALE_CHANGED', data: { name: context.images.selectedName, gradientOpacityScale: Number(gradientOpacityScaleSlider.value), }, }) }) uiContainer.appendChild(sliderEntry) } export default createGradientOpacitySlider ================================================ FILE: src/UI/reference-ui/src/Images/createImagesInterface.js ================================================ import style from '../ItkVtkViewer.module.css' import applyGroupVisibility from '../applyGroupVisibility' import createComponentSelector from './createComponentSelector' import createColorRangeInput from './createColorRangeInput' import createTransferFunctionWidget from './createTransferFunctionWidget' import createVolumeRenderingInputs from './createVolumeRenderingInputs' import createLabelImageColorWidget from './createLabelImageColorWidget' import createLabelImageWeightWidget from './createLabelImageWeightWidget' function createImagesInterface(context) { const imagesUIGroup = document.createElement('div') imagesUIGroup.setAttribute('class', style.uiGroup) context.images.imagesUIGroup = imagesUIGroup context.uiGroups.set('images', imagesUIGroup) const componentAndScale = document.createElement('div') imagesUIGroup.appendChild(componentAndScale) componentAndScale.setAttribute('style', 'display: flex;') context.images.componentAndScale = componentAndScale createComponentSelector(context, componentAndScale) createColorRangeInput(context, imagesUIGroup) createTransferFunctionWidget(context, imagesUIGroup) createVolumeRenderingInputs(context, imagesUIGroup) context.uiContainer.appendChild(imagesUIGroup) createLabelImageColorWidget(context) createLabelImageWeightWidget(context) applyGroupVisibility( context, ['images', 'labelImages', 'labelImageWeights'], false ) } export default createImagesInterface ================================================ FILE: src/UI/reference-ui/src/Images/createInterpolationButton.js ================================================ import style from '../ItkVtkViewer.module.css' import { interpolationIconDataUri } from '@itk-viewer/icons' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import toggleInterpolation from './toggleInterpolation' function createInterpolationButton(context, uiRow) { const interpolationButton = document.createElement('div') // Todo: send event to disable interpolation when label maps added //if (context.images.labelMaps.length) { //context.images.interpolationEnabled = false //} // and the "input" element needs to get the 'disabled' attribute added interpolationButton.innerHTML = `` const interpolationButtonInput = interpolationButton.children[0] const interpolationButtonLabel = interpolationButton.children[1] context.images.interpolationButtonLabel = interpolationButtonLabel context.images.interpolationButtonInput = interpolationButtonInput toggleInterpolation(context, { data: context.images.selectedName }) interpolationButton.addEventListener('change', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'TOGGLE_IMAGE_INTERPOLATION', data: context.images.selectedName, }) }) uiRow.appendChild(interpolationButton) } export default createInterpolationButton ================================================ FILE: src/UI/reference-ui/src/Images/createLabelImageColorWidget.js ================================================ import macro from '@kitware/vtk.js/macro' import createCategoricalColorIconSelector from '../createCategoricalColorIconSelector' import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import { opacityIconDataUri } from '@itk-viewer/icons' function createLabelImageColorWidget(context) { const viewerDOMId = context.id const labelImageColorUIGroup = document.createElement('div') context.images.labelImageColorUIGroup = labelImageColorUIGroup labelImageColorUIGroup.setAttribute('class', style.uiGroup) context.uiGroups.set('labelImages', labelImageColorUIGroup) const labelImageWidgetRow = document.createElement('div') labelImageWidgetRow.setAttribute('class', style.uiRow) const categoricalColorSelector = document.createElement('div') categoricalColorSelector.id = `${context.id}-lookupTableSelector` const iconSelector = createCategoricalColorIconSelector( categoricalColorSelector ) context.images.labelImageIconSelector = iconSelector categoricalColorSelector.addEventListener('changed', event => { event.preventDefault() event.stopPropagation() const name = context.images.selectedName const lut = iconSelector.getSelectedValue() context.service.send({ type: 'LABEL_IMAGE_LOOKUP_TABLE_CHANGED', data: { name, lookupTable: lut }, }) }) const sliderEntry = document.createElement('div') sliderEntry.setAttribute('class', style.sliderEntry) sliderEntry.innerHTML = `
opacity
` const labelImageBlendSlider = sliderEntry.querySelector( `#${context.id}-labelImageBlendSlider` ) context.images.labelImageBlendSlider = labelImageBlendSlider const sliderEntryDiv = sliderEntry.children[0] context.images.labelImageBlendDiv = sliderEntryDiv applyContrastSensitiveStyleToElement( context, 'invertibleButton', sliderEntryDiv ) labelImageBlendSlider.addEventListener('input', event => { event.preventDefault() event.stopPropagation() const name = context.images.selectedName context.service.send({ type: 'LABEL_IMAGE_BLEND_CHANGED', data: { name, labelImageBlend: Number(labelImageBlendSlider.value) }, }) }) labelImageWidgetRow.appendChild(categoricalColorSelector) labelImageWidgetRow.appendChild(sliderEntry) labelImageColorUIGroup.appendChild(labelImageWidgetRow) context.uiContainer.appendChild(labelImageColorUIGroup) } export default createLabelImageColorWidget ================================================ FILE: src/UI/reference-ui/src/Images/createLabelImageWeightWidget.js ================================================ import style from '../ItkVtkViewer.module.css' function createLabelMapWeightWidget(context) { const labelImageWeightUIGroup = document.createElement('div') context.images.labelImageWeightUIGroup = labelImageWeightUIGroup labelImageWeightUIGroup.setAttribute('class', style.uiGroup) context.uiGroups.set('labelImageWeights', labelImageWeightUIGroup) const labelImageWidgetRow = document.createElement('div') labelImageWidgetRow.setAttribute('class', style.uiRow) const uniqueLabelSelectorDiv = document.createElement('div') uniqueLabelSelectorDiv.id = `${context.id}-labelImageUniqueLabelSelector` const labelSelector = document.createElement('select') labelSelector.setAttribute('class', style.selector) labelSelector.id = `${context.id}-labelSelector` context.images.labelSelector = labelSelector context.images.labelSelector = labelSelector uniqueLabelSelectorDiv.appendChild(labelSelector) const sliderEntry = document.createElement('div') sliderEntry.setAttribute('class', style.sliderEntry) // ` sliderEntry.innerHTML = ` ` const weightElement = sliderEntry.querySelector( `#${context.id}-labelImageWeightSlider` ) context.images.labelImageWeightSlider = weightElement labelImageWidgetRow.appendChild(uniqueLabelSelectorDiv) labelImageWidgetRow.appendChild(sliderEntry) labelImageWeightUIGroup.appendChild(labelImageWidgetRow) context.uiContainer.appendChild(labelImageWeightUIGroup) labelSelector.addEventListener('change', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'LABEL_IMAGE_SELECTED_LABEL_CHANGED', data: { name: context.images.selectedName, selectedLabel: event.target.value, }, }) }) weightElement.addEventListener('input', event => { event.preventDefault() event.stopPropagation() const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const labelImageWeights = actorContext.labelImageWeights if (actorContext.selectedLabel === 'all') { const weight = Number(weightElement.value) for (const label of labelImageWeights.keys()) { labelImageWeights.set(label, weight) } actorContext.labelImageToggleWeight = weight } else { labelImageWeights.set( parseInt(actorContext.selectedLabel), Number(weightElement.value) ) } context.service.send({ type: 'LABEL_IMAGE_WEIGHTS_CHANGED', data: { name: context.images.selectedName, labelImageWeights }, }) }) } export default createLabelMapWeightWidget ================================================ FILE: src/UI/reference-ui/src/Images/createSampleDistanceSlider.js ================================================ import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import { sampleDistanceIconDataUri } from '@itk-viewer/icons' function createSampleDistanceSlider(context, uiContainer) { const sliderEntry = document.createElement('div') sliderEntry.setAttribute('class', style.sliderEntry) sliderEntry.innerHTML = `
sample distance
` const spacingElement = sliderEntry.querySelector(`.${context.id}-spacing`) const spacingDiv = sliderEntry.children[0] context.images.volumeSampleDistanceDiv = spacingDiv context.images.volumeSampleDistanceSlider = spacingElement applyContrastSensitiveStyleToElement(context, 'invertibleButton', spacingDiv) spacingElement.addEventListener('input', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'IMAGE_VOLUME_SAMPLE_DISTANCE_CHANGED', data: { name: context.images.selectedName, volumeSampleDistance: Number(spacingElement.value), }, }) }) uiContainer.appendChild(sliderEntry) } export default createSampleDistanceSlider ================================================ FILE: src/UI/reference-ui/src/Images/createShadowToggle.js ================================================ import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import { shadowIconDataUri } from '@itk-viewer/icons' function createShadowToggle(context, uiContainer) { const shadowButton = document.createElement('div') shadowButton.innerHTML = `` const shadowButtonInput = shadowButton.children[0] const shadowButtonLabel = shadowButton.children[1] applyContrastSensitiveStyleToElement( context, 'invertibleButton', shadowButtonLabel ) context.images.shadowButtonLabel = shadowButtonLabel context.images.shadowButtonInput = shadowButtonInput shadowButton.addEventListener('change', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'TOGGLE_IMAGE_SHADOW', data: context.images.selectedName, }) }) uiContainer.appendChild(shadowButton) } export default createShadowToggle ================================================ FILE: src/UI/reference-ui/src/Images/createTransferFunctionEditor.js ================================================ import { throttle } from './throttle' import { TransferFunctionEditor } from 'itk-viewer-transfer-function-editor' const PIECEWISE_UPDATE_DELAY = 200 const updateContextPiecewiseFunction = (context, points) => { if (!context.images.piecewiseFunctions) return // not ready yet const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const component = actorContext.selectedComponent context.service.send({ type: 'IMAGE_PIECEWISE_FUNCTION_POINTS_CHANGED', data: { name, component, points, keepAutoAdjusting: false, }, }) } const updateContextColorRange = (context, points) => { if (!context.images.piecewiseFunctions) return // not ready yet const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const component = actorContext.selectedComponent context.service.send({ type: 'IMAGE_COLOR_RANGE_POINTS_CHANGED', data: { name, component, points, }, }) } const vtkPiecewiseGaussianWidgetFacade = (tfEditor, context) => { const throttledUpdate = throttle( () => updateContextPiecewiseFunction(context, tfEditor.getPoints()), PIECEWISE_UPDATE_DELAY ) tfEditor.eventTarget.addEventListener('updated', throttledUpdate) const throttledColorRangeUpdate = throttle( () => updateContextColorRange(context, tfEditor.getColorRange()), PIECEWISE_UPDATE_DELAY ) tfEditor.eventTarget.addEventListener('colorRange', throttledColorRangeUpdate) return { setColorTransferFunction: tf => { tfEditor.setColorTransferFunction(tf) }, setPoints(points) { // tfEditor.setPoints recreates them and they loose their "grabbed" state // so ignore events coming down triggered by user dragging points const currentPoints = tfEditor.getPoints() const arePointsModified = points.length !== currentPoints.length || points.some(([newX, newY], idx) => { const [oldX, oldY] = currentPoints[idx] return newX !== oldX || newY !== oldY }) if (arePointsModified) tfEditor.setPoints(points) }, getPoints() { return tfEditor.getPoints() }, setColorRange: newRange => { const displayedRange = tfEditor.getColorRange() // if same, avoid infinite event loop if (displayedRange.some((v, i) => v !== newRange[i])) { tfEditor.setColorRange(newRange) } }, setRange: range => { tfEditor.setRange(range) }, setRangeZoom: newRange => { tfEditor.setViewBox(...newRange) }, setHistogram: h => tfEditor.setHistogram(h), render: () => undefined, getGaussians() { console.warn('getGaussians not implemented, use getPoints') return [] }, setGaussians() { console.warn('setGaussians not implemented, use setPoints') }, } } export const createTransferFunctionEditor = (context, mount) => { const editor = new TransferFunctionEditor(mount) return vtkPiecewiseGaussianWidgetFacade(editor, context) } ================================================ FILE: src/UI/reference-ui/src/Images/createTransferFunctionWidget.js ================================================ import { createTransferFunctionEditor } from './createTransferFunctionEditor' import style from '../ItkVtkViewer.module.css' const createTransferFunctionWidget = (context, imagesUIGroup) => { const piecewiseWidgetContainer = document.createElement('div') piecewiseWidgetContainer.setAttribute('style', 'height: 150px; width: 400px') piecewiseWidgetContainer.setAttribute('class', style.piecewiseWidget) const transferFunctionWidgetRow = document.createElement('div') transferFunctionWidgetRow.setAttribute('class', style.uiRow) // This row needs background different from normal uiRows, to aid // in the illusion that it's the content portion of a tabbed pane transferFunctionWidgetRow.setAttribute( 'style', 'background: rgba(127, 127, 127, 0.5);' ) imagesUIGroup.appendChild(transferFunctionWidgetRow) transferFunctionWidgetRow.appendChild(piecewiseWidgetContainer) const transferFunctionWidget = createTransferFunctionEditor( context, piecewiseWidgetContainer ) context.images.transferFunctionWidget = transferFunctionWidget } export default createTransferFunctionWidget export const applyPiecewiseFunctionPointsToEditor = (context, event) => { const { transferFunctionWidget, actorContext } = context.images const { points, component, name } = event.data const imageActorContext = actorContext.get(name) if (component === imageActorContext.selectedComponent) { transferFunctionWidget.setPoints(points) } } ================================================ FILE: src/UI/reference-ui/src/Images/createVolumeRenderingInputs.js ================================================ import style from '../ItkVtkViewer.module.css' import createShadowToggle from './createShadowToggle' import createGradientOpacitySlider from './createGradientOpacitySlider' import createSampleDistanceSlider from './createSampleDistanceSlider' import createBlendModeSelector from './createBlendModeSelector' import { createCinematicParameters } from './cinematic' import createWindowLevelToggle from './createWindowLevelToggle' function createVolumeRenderingInputs(context, imagesUIGroup) { const volumeRow1 = document.createElement('div') volumeRow1.setAttribute('class', style.uiRow) createShadowToggle(context, volumeRow1) createGradientOpacitySlider(context, volumeRow1) createWindowLevelToggle(context, volumeRow1) imagesUIGroup.appendChild(volumeRow1) context.images.volumeRow1 = volumeRow1 const volumeRow2 = document.createElement('div') volumeRow2.setAttribute('class', style.uiRow) createSampleDistanceSlider(context, volumeRow2) createBlendModeSelector(context, volumeRow2) imagesUIGroup.appendChild(volumeRow2) context.images.volumeUiElements = [volumeRow1, volumeRow2] createCinematicParameters(context, imagesUIGroup) } export default createVolumeRenderingInputs ================================================ FILE: src/UI/reference-ui/src/Images/createWindowLevelReset.js ================================================ import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import { resetImageIconDataUri } from '@itk-viewer/icons' function createWindowLevelReset(context, uiContainer) { const windowLevelResetButton = document.createElement('div') windowLevelResetButton.innerHTML = `
gradient opacity
` const windowLevelResetButtonInput = windowLevelResetButton.children[0] const windowLevelResetButtonLabel = windowLevelResetButton.children[1] applyContrastSensitiveStyleToElement( context, 'invertibleButton', windowLevelResetButtonLabel ) context.images.windowLevelResetButtonLabel = windowLevelResetButtonLabel context.images.windowLevelResetButtonInput = windowLevelResetButtonInput windowLevelResetButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'IMAGE_COLOR_RANGE_RESET', data: { name: context.images.selectedName, }, }) }) uiContainer.appendChild(windowLevelResetButton) } export default createWindowLevelReset ================================================ FILE: src/UI/reference-ui/src/Images/createWindowLevelToggle.js ================================================ import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import { windowingIconDataUri } from '@itk-viewer/icons' function createWindowLevelToggle(context, uiContainer) { const windowLevelToggle = document.createElement('div') windowLevelToggle.innerHTML = `` const windowLevelToggleInput = windowLevelToggle.children[0] const windowLevelToggleLabel = windowLevelToggle.children[1] applyContrastSensitiveStyleToElement( context, 'invertibleButton', windowLevelToggleLabel ) context.images.windowLevelToggleLabel = windowLevelToggleLabel context.images.windowLevelToggleInput = windowLevelToggleInput windowLevelToggle.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) context.service.send({ type: 'WINDOW_LEVEL_TOGGLED', data: { name, component: actorContext.selectedComponent, }, }) }) uiContainer.appendChild(windowLevelToggle) } export default createWindowLevelToggle ================================================ FILE: src/UI/reference-ui/src/Images/imagesUIMachineOptions.js ================================================ import createImagesInterface from './createImagesInterface' import updateImageInterface from './updateImageInterface' import updateLabelImageInterface from './updateLabelImageInterface' import updateRenderedImageInterface from './updateRenderedImageInterface' import selectImageComponent from './selectImageComponent' import toggleInterpolation from './toggleInterpolation' import applyComponentVisibility from './applyComponentVisibility' import applyColorRange from './applyColorRange' import applyColorRangeBounds from './applyColorRangeBounds' import applyColorMap from './applyColorMap' import applyPiecewiseFunctionGaussians from './applyPiecewiseFunctionGaussians' import toggleShadow from './toggleShadow' import applyGradientOpacity from './applyGradientOpacity' import applyGradientOpacityScale from './applyGradientOpacityScale' import applyVolumeSampleDistance from './applyVolumeSampleDistance' import applyBlendMode from './applyBlendMode' import applyHistogram from './applyHistogram' import applyLookupTable from './applyLookupTable' import applyLabelImageBlend from './applyLabelImageBlend' import applyLabelImageWeights from './applyLabelImageWeights' import applyLabelNames from './applyLabelNames' import applySelectedLabel from './applySelectedLabel' import scaleSelector from './scaleSelector' import { applyPiecewiseFunctionPointsToEditor } from './createTransferFunctionWidget' import { applyCinematicChanged } from './cinematic' import toggleWindowLevel from './toggleWindowLevel' import applyWindowLevelReset from './applyWindowingReset' const imagesUIMachineOptions = { actions: { createImagesInterface, updateImageInterface, updateLabelImageInterface, updateRenderedImageInterface, selectImageComponent, toggleInterpolation, applyComponentVisibility, applyColorRange, applyColorRangeBounds, applyColorMap, applyPiecewiseFunctionGaussians, applyPiecewiseFunctionPointsToEditor, toggleWindowLevel, applyWindowLevelReset, toggleShadow, applyGradientOpacity, applyGradientOpacityScale, applyVolumeSampleDistance, applyBlendMode, applyCinematicChanged, applyHistogram, applyLookupTable, applyLabelImageBlend, applyLabelImageWeights, applyLabelNames, applySelectedLabel, }, services: { scaleSelector, }, } export default imagesUIMachineOptions ================================================ FILE: src/UI/reference-ui/src/Images/scaleSelector.js ================================================ import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import style from '../ItkVtkViewer.module.css' import { scaleSelectIconDataUri } from '@itk-viewer/icons' function applyScaleCount(input, scaleCount) { input.innerHTML = '' // clear old options const autoPickOption = document.createElement('option') autoPickOption.value = 'Framerate-pick' autoPickOption.innerHTML = 'Framerate-pick' input.appendChild(autoPickOption) ;[...Array(scaleCount).keys()].reverse().forEach(i => { const option = document.createElement('option') option.value = i option.innerHTML = i input.appendChild(option) }) } const scaleSelector = (context, event) => (send, onReceive) => { const scaleSelectorDiv = document.createElement('div') scaleSelectorDiv.setAttribute( 'style', 'display: flex; align-self: center; height: 25px; margin-right: 5px' ) context.images.componentAndScale.appendChild(scaleSelectorDiv) scaleSelectorDiv.innerHTML = `
Resolution Scale
` const scaleSelectorIcon = scaleSelectorDiv.children[0] context.images.scaleSelectorIconDiv = scaleSelectorIcon // stash for applyImagesContrastSensitiveStyle applyContrastSensitiveStyleToElement( context, 'invertibleButton', scaleSelectorIcon ) const scaleSelector = document.createElement('select') scaleSelectorDiv.appendChild(scaleSelector) scaleSelector.setAttribute('style', 'max-width: 3.2ch') scaleSelector.setAttribute('class', style.selector) scaleSelector.addEventListener('change', event => { event.preventDefault() event.stopPropagation() const imageActor = context.images.imageRenderingActors.get( context.images.selectedName ) if (event.target.value === 'Framerate-pick') { imageActor.send('ADJUST_SCALE_FOR_FRAMERATE') } else { imageActor.send('SET_IMAGE_SCALE', { targetScale: parseInt(event.target.value), }) } }) function onImageAssigned(name) { const imageActorContext = context.images.actorContext.get(name) const image = imageActorContext.image ?? imageActorContext.labelImage const scaleCount = image.scaleInfo.length if (scaleCount > 1) { scaleSelectorDiv.style.display = 'flex' applyScaleCount(scaleSelector, scaleCount) } else { scaleSelectorDiv.style.display = 'none' } } onImageAssigned(event.data) onReceive(event => { const { type } = event if (type === 'IMAGE_ASSIGNED') { onImageAssigned(event.data) } else if (type === 'RENDERED_IMAGE_ASSIGNED') { scaleSelector.value = event.loadedScale } else if (type === 'IMAGE_RENDERING_ACTIVE') { // set scale number after ADJUST_SCALE_FOR_FRAMERATE even if no scale change scaleSelector.value = context.images.actorContext.get( event.data.name ).loadedScale } }) } export default scaleSelector ================================================ FILE: src/UI/reference-ui/src/Images/selectImageComponent.js ================================================ import applyColorRangeBounds from './applyColorRangeBounds' import applyColorRange from './applyColorRange' import applyColorMap from './applyColorMap' import applyHistogram from './applyHistogram' function selectImageComponent(context, event) { context.images.componentSelector.value = event.data const name = event.data.name const actorContext = context.images.actorContext.get(name) const component = event.data.component const transferFunctionWidget = context.images.transferFunctionWidget if (actorContext.colorRanges.has(component)) { const range = actorContext.colorRanges.get(component) applyColorRange(context, { data: { name, component, range, }, }) } const piecewiseFunctionPoints = actorContext.piecewiseFunctionPoints.get( component ) if (transferFunctionWidget && piecewiseFunctionPoints) { transferFunctionWidget.setPoints(piecewiseFunctionPoints) } if (actorContext.colorRangeBounds.has(component)) { applyColorRangeBounds(context, { data: { name, component, range: actorContext.colorRangeBounds.get(component), }, }) } if (actorContext.colorMaps.has(component)) { applyColorMap(context, { data: { name, component, colorMap: actorContext.colorMaps.get(component), }, }) context.images.iconSelector.setSelectedValue( actorContext.colorMaps.get(component) ) } const histogram = actorContext.histograms.get(component) if (histogram) { applyHistogram(context, { data: { name, component, histogram, }, }) } else { context.service.send({ type: 'UPDATE_IMAGE_HISTOGRAM', data: { name, component }, }) } } export default selectImageComponent ================================================ FILE: src/UI/reference-ui/src/Images/throttle.js ================================================ // from https://stackoverflow.com/a/27078401 // Trailing call functionality is desired. // Returns a function, that, when invoked, will only be triggered at most once // during a given window of time. Normally, the throttled function will run // as much as it can, without ever going more than once per `wait` duration; // but if you'd like to disable the execution on the leading edge, pass // `{leading: false}`. To disable execution on the trailing edge, ditto. export function throttle(func, wait, options) { var context, args, result var timeout = null var previous = 0 if (!options) options = {} var later = function() { previous = options.leading === false ? 0 : Date.now() timeout = null result = func.apply(context, args) if (!timeout) context = args = null } return function() { var now = Date.now() if (!previous && options.leading === false) previous = now var remaining = wait - (now - previous) context = this args = arguments if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout) timeout = null } previous = now result = func.apply(context, args) if (!timeout) context = args = null } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining) } return result } } ================================================ FILE: src/UI/reference-ui/src/Images/toggleInterpolation.js ================================================ function toggleInterpolation(context, event) { const name = event.data const actorContext = context.images.actorContext.get(name) const interpolation = actorContext.interpolationEnabled context.images.interpolationButtonInput.checked = interpolation } export default toggleInterpolation ================================================ FILE: src/UI/reference-ui/src/Images/toggleShadow.js ================================================ function toggleShadow(context, event) { const name = event.data const actorContext = context.images.actorContext.get(name) const shadow = actorContext.shadowEnabled context.images.shadowButtonInput.checked = shadow } export default toggleShadow ================================================ FILE: src/UI/reference-ui/src/Images/toggleUseShadow.js ================================================ function toggleUseShadow(context, event) { const name = event.data const actorContext = context.images.actorContext.get(name) const useShadow = actorContext.useShadow context.images.useShadowButtonInput.checked = useShadow context.imageUI.representationProxy.setUseShadow(useShadow) context.service.send('RENDER') } export default toggleUseShadow ================================================ FILE: src/UI/reference-ui/src/Images/toggleWindowLevel.js ================================================ const MIN_WINDOW = 1e-8 function toggleWindowLevel(context, event) { const name = event.data.name const actorContext = context.images.actorContext.get(name) const wl = actorContext.windowLevelEnabled const colorRange = actorContext.colorRanges.get( actorContext.selectedComponent ) const fullRange = actorContext.colorRangeBounds.get( actorContext.selectedComponent ) context.images.windowLevelToggleInput.checked = wl const minimumTooltip = context.images.colorRangeInputRow.children[1] const maximumTooltip = context.images.colorRangeInputRow.children[3] const minimumInput = minimumTooltip.children[0] const maximumInput = maximumTooltip.children[0] if (wl) { minimumTooltip.setAttribute('itk-vtk-tooltip-content', 'Window width') maximumTooltip.setAttribute('itk-vtk-tooltip-content', 'Window level') minimumInput.value = colorRange[1] - colorRange[0] maximumInput.value = (colorRange[1] + colorRange[0]) / 2 minimumInput.min = MIN_WINDOW minimumInput.max = (fullRange[1] - fullRange[0]) * 2 maximumInput.min = fullRange[0] - fullRange[0] maximumInput.max = fullRange[1] + fullRange[1] const step = 10 ** Math.ceil(Math.log((fullRange[1] - fullRange[0]) / 1000)) minimumInput.step = step maximumInput.step = step } else { minimumTooltip.setAttribute('itk-vtk-tooltip-content', 'Color range min') maximumTooltip.setAttribute('itk-vtk-tooltip-content', 'Color range max') minimumInput.value = colorRange[0] maximumInput.value = colorRange[1] minimumInput.min = fullRange[0] minimumInput.max = fullRange[1] maximumInput.min = fullRange[0] maximumInput.max = fullRange[1] const step = (fullRange[1] - fullRange[0]) / 1000.0 minimumInput.step = step maximumInput.step = step } } export default toggleWindowLevel ================================================ FILE: src/UI/reference-ui/src/Images/updateAvailableComponents.js ================================================ import style from '../ItkVtkViewer.module.css' function updateAvailableComponents(context) { const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const image = actorContext.image if (image) { const components = actorContext.componentVisibilities.length if (components > 1 && actorContext.independentComponents) { context.images.componentRow.style.display = 'flex' } else { context.images.componentRow.style.display = 'none' } context.images.componentSelector.innerHTML = new Array(components) .fill(undefined) .map((_, ii) => ii) .map( (idx, component) => `` ) .join('') context.images.componentSelector.value = actorContext.selectedComponent } } export default updateAvailableComponents ================================================ FILE: src/UI/reference-ui/src/Images/updateImageInterface.js ================================================ import updateAvailableComponents from './updateAvailableComponents' import toggleInterpolation from './toggleInterpolation' import applyColorRangeBounds from './applyColorRangeBounds' import applyColorRange from './applyColorRange' import applyColorMap from './applyColorMap' import toggleShadow from './toggleShadow' import applyGradientOpacity from './applyGradientOpacity' import applyGradientOpacityScale from './applyGradientOpacityScale' import applyVolumeSampleDistance from './applyVolumeSampleDistance' import applyBlendMode from './applyBlendMode' function updateImageInterface(context) { updateAvailableComponents(context) const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const image = actorContext.image const component = actorContext.selectedComponent // If not a 2D RGB image if (actorContext.independentComponents) { context.images.colorRangeInputRow.style.display = 'flex' context.images.colorMapSelector.style.display = 'block' } else { context.images.colorRangeInputRow.style.display = 'none' context.images.colorMapSelector.style.display = 'none' } if (image) { if (image.imageType.dimension === 3) { context.images.volumeUiElements.forEach(e => (e.style.display = 'flex')) if (context.main.xPlaneRow) { context.main.xPlaneRow.style.display = 'flex' context.main.yPlaneRow.style.display = 'flex' context.main.zPlaneRow.style.display = 'flex' } } else { context.images.volumeUiElements.forEach(e => (e.style.display = 'none')) if (context.main.xPlaneRow) { context.main.xPlaneRow.style.display = 'none' context.main.yPlaneRow.style.display = 'none' context.main.zPlaneRow.style.display = 'none' } } toggleInterpolation(context, { data: name }) if (actorContext.colorRanges.has(component)) { applyColorRange(context, { data: { name, component, range: actorContext.colorRanges.get(component), }, }) } if (actorContext.colorRangeBounds.has(component)) { applyColorRangeBounds(context, { data: { name, component, range: actorContext.colorRangeBounds.get(component), }, }) } if (actorContext.colorMaps.has(component)) { const colorMap = actorContext.colorMaps.get(component) applyColorMap(context, { data: { name, component, colorMap, }, }) context.images.iconSelector.setSelectedValue(colorMap) } toggleShadow(context, { data: name }) applyGradientOpacity(context, { data: { name, gradientOpacity: actorContext.gradientOpacity }, }) applyGradientOpacityScale(context, { data: { name, gradientOpacityScale: actorContext.gradientOpacityScale }, }) applyVolumeSampleDistance(context, { data: { name, volumeSampleDistance: actorContext.volumeSampleDistance }, }) applyBlendMode(context, { data: { name, blendMode: actorContext.blendMode }, }) } } export default updateImageInterface ================================================ FILE: src/UI/reference-ui/src/Images/updateLabelImageInterface.js ================================================ import applyLookupTable from './applyLookupTable' import applyLabelImageBlend from './applyLabelImageBlend' function updateLabelImageInterface(context) { const name = context.images.selectedName const actorContext = context.images.actorContext.get(name) const labelImage = actorContext.labelImage if (labelImage) { applyLookupTable(context, { data: { name, lookupTable: actorContext.lookupTable }, }) applyLabelImageBlend(context, { data: { name, labelImageBlend: actorContext.labelImageBlend }, }) } } export default updateLabelImageInterface ================================================ FILE: src/UI/reference-ui/src/Images/updateRenderedImageInterface.js ================================================ function updateRenderedImageInterface(context, event) { const name = event.data const actorContext = context.images.actorContext.get(name) const { transferFunctionWidget } = context.images if (!transferFunctionWidget) { console.warn('No transfer function widget') return } const points = actorContext.piecewiseFunctionPoints.get( actorContext.selectedComponent ) // no points if just label image if (points) { transferFunctionWidget.setPoints(points) } } export default updateRenderedImageInterface ================================================ FILE: src/UI/reference-ui/src/ItkVtkViewer.module.css ================================================ .loading { border: 16px solid #f3f3f3; /* Light grey */ border-top: 16px solid #3498db; /* Blue */ border-radius: 50%; width: 120px; height: 120px; position: absolute; left: calc(50% - 60px); top: calc(50% - 60px); animation: spin 2s linear infinite; box-sizing: border-box; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .viewContainer { position: absolute; top: 0; left: 0; bottom: 0; right: 0; display: flex; flex-direction: column; background: rgba(128, 128, 128, 0.8); } .viewport { position: relative; flex: 1; min-height: 0; } .uiContainer { position: absolute; top: 0; left: 0; z-index: 1000; height: fit-content; max-height: 100%; display: flex; padding: 6px 0 0 6px; border: 0px; box-sizing: border-box; --md-navigation-drawer-container-shape-start-end: 0; --md-navigation-drawer-container-shape-end-end: 0; --md-navigation-drawer-container-color: rgba(128, 128, 128, 0.5); } .drawer { overflow: hidden auto; } .floater { position: absolute; z-index: 3000; } .uiGroup { background: rgba(128, 128, 128, 0.5); border-radius: 4px; margin: 2px; } .uiRow { display: flex; flex-direction: row; flex: 1; align-items: center; justify-content: space-between; padding: 5px; } .mainUIRow { composes: uiRow; justify-content: space-around; max-width: 420px; } .planeUIRow { composes: uiRow; background: rgba(128, 128, 128, 0.5); } .layers { flex-wrap: wrap; } .progress { color: white; font-size: 200%; height: 100vh; width: 100vw; text-align: center; vertical-align: middle; line-height: 100vh; } .piecewiseWidget { flex: 1; background: rgba(255, 255, 255, 0.2); border-radius: 3px; z-index: 1000; } .logo { position: absolute; top: 5px; right: 5px; height: 2em; width: 2em; cursor: pointer; z-index: 100; } .fpsMonitor { position: absolute; top: 5px; right: 5px; border-radius: 5px; background: rgba(255, 255, 255, 0.6); cursor: pointer; z-index: 101; } [itk-vtk-tooltip] { position: relative; } [itk-vtk-tooltip]::before { content: attr(itk-vtk-tooltip-content); visibility: hidden; position: absolute; top: 50%; right: calc(100% + 16px); width: 400%; padding: 4px 6px; text-align: center; text-transform: none; font-size: 0.9em; font-family: monospace; border-radius: 3px; background: rgba(0.9, 0.9, 0.9, 0.95); color: white; opacity: 0; transform: translate(15px, -50%); transition-property: all; transition-duration: 0.3s; transition-timing-function: ease-in-out; transition-delay: 0.8s; z-index: 3000; } [itk-vtk-tooltip]:hover::before { opacity: 1; visibility: visible; transform: translate(0, -50%); } [itk-vtk-tooltip-bottom]::before { top: calc(100% + 16px); left: 50%; right: initial; transform: translate(-50%, -15px); } [itk-vtk-tooltip-bottom]:hover::before { transform: translate(-50%, 0); } [itk-vtk-tooltip-right]::before { top: 50%; left: calc(100% + 16px); right: initial; transform: translate(-15px, -50%); } [itk-vtk-tooltip-right]:hover::before { transform: translate(0, -50%); } [itk-vtk-tooltip-left]::before { top: -50%; right: 50%; left: initial; } [itk-vtk-tooltip-top-screenshot]::before { top: initial; left: 260%; right: initial; bottom: calc(100% + 8px); transform: translate(-50%, 15px); } [itk-vtk-tooltip-top-screenshot]:hover::before { transform: translate(-50%, 0); } [itk-vtk-tooltip-top-annotations]::before { top: initial; left: 160%; right: initial; bottom: calc(100% + 10px); transform: translate(-50%, 15px); } [itk-vtk-tooltip-top-annotations]:hover::before { transform: translate(-50%, 0); } [itk-vtk-tooltip-top-axes]::before { top: initial; left: 160%; right: initial; bottom: calc(100% + 10px); transform: translate(-50%, 15px); } [itk-vtk-tooltip-top-axes]:hover::before { transform: translate(-50%, 0); } [itk-vtk-tooltip-top-fullscreen]::before { top: initial; left: 120%; right: initial; bottom: calc(100% + 10px); transform: translate(-50%, 15px); width: 400%; } [itk-vtk-tooltip-top-fullscreen]:hover::before { transform: translate(-50%, 0); } [itk-vtk-tooltip-top]::before { top: initial; left: 60%; right: initial; bottom: calc(100% + 10px); transform: translate(-50%, 15px); } [itk-vtk-tooltip-top]:hover::before { transform: translate(-50%, 0); } [itk-vtk-tooltip-top-fullscreen]::before { top: initial; left: 120%; right: initial; bottom: calc(100% + 10px); transform: translate(-50%, 15px); width: 400%; } [itk-vtk-tooltip-top-input]::before { top: initial; right: initial; bottom: 25%; transform: translate(-50%, 15px); width: fit-content; } .layerEntryCommon { flex: 1; display: flex; flex-direction: row; align-items: stretch; justify-content: space-between; border-style: solid; border-width: 2px; } .layerEntryBrightBG { border-color: #666; } .layerEntryDarkBG { border-color: #aaa; } .layerLabelCommon { border: none; background: transparent; font-size: 1.2em; z-index: 1000; flex: 1; overflow: hidden; text-overflow: ellipsis; text-align: center; } .layerLabelBrightBG { color: black; } .layerLabelDarkBG { color: white; } .visibleButton { flex-basis: 2.5em; cursor: pointer; z-index: 1000; } .visibleButton img { height: 1.2em; width: 1.2em; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; } .noFlexBasis { flex-basis: auto; } .layerIcon { display: inline-block; } .layerIcon img { height: 1.2em; width: 1.2em; padding-top: 2px; padding-bottom: 2px; padding-left: 8px; padding-right: 6px; } .iconGroup { display: inline-block; } .ldsRing { display: inline-block; position: relative; width: 20px; height: 20px; margin-top: 4px; } .ldsRing div { box-sizing: border-box; display: block; position: absolute; width: 1em; height: 1em; margin: 0; border: 0.15em solid #000; border-radius: 50%; animation: ldsRing 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; border-color: #000 transparent transparent transparent; } .ldsRing div:nth-child(1) { animation-delay: -0.45s; } .ldsRing div:nth-child(2) { animation-delay: -0.3s; } .ldsRing div:nth-child(3) { animation-delay: -0.15s; } @keyframes ldsRing { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .tooltipButtonBrightBG::before { } .tooltipButtonDarkBG::before { filter: invert(100%); -webkit-filter: invert(100%); } .invertibleButtonBrightBG { } .invertibleButtonDarkBG { filter: invert(100%); -webkit-filter: invert(100%); } .screenshotButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .screenshotButton img { height: 1.2em; width: 1.2em; } .annotationsButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .annotationsButton img { height: 1.2em; width: 1.2em; } .axesButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .axesButton img { height: 1.2em; width: 1.2em; } .fullscreenButton { flex: 1; width: 8m; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .fullscreenButton img { height: 1.2em; width: 1.2em; } .interpolationButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-right: 4px; cursor: pointer; z-index: 1000; } .interpolationButton img { width: 1.2em; margin-top: 4px; } .cropButton { flex: 1; height: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .cropButton img { height: 1.2em; width: 1.2em; } .resetCropButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .resetCropButton img { height: 1.2em; width: 1.2em; } .distanceEntry { flex: 1; display: flex; flex-direction: row; align-items: self-start; } .distanceButton { flex: 1; height: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .distanceButton img { height: 1.2em; width: 1.2em; } .distanceLabelCommon { border: none; background: transparent; font-size: 1.2em; margin-right: 10px; z-index: 1000; } .distanceLabelBrightBG { color: black; } .distanceLabelDarkBG { color: white; } .distanceInput { background: transparent; color: white; font-size: 1em; width: 80px; } .resetCameraButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .resetCameraButton img { height: 1.2em; width: 1.2em; } .bgColorButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .bgColorButton img { height: 1.2em; width: 1.2em; } .viewModeButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .viewModeButton img { width: 1.3em; height: 1.3em; } .shadowButton { width: 8mm; padding: 4px; padding-left: 0px; cursor: pointer; z-index: 1000; } .shadowButton img { width: 1.3em; height: 1.3em; } .viewPlanesButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 0px; padding-right: 6px; cursor: pointer; z-index: 1000; } .viewPlanesButton img { width: 1.3em; height: 1.3em; } .toggleInput { margin: 0px; width: 0; opacity: 0; box-sizing: content-box; } .toggleButton { cursor: pointer; border-radius: 0.2em; opacity: 0.45; } input:checked.toggleInput + label { opacity: 1; } .numberInput { color: white; background: transparent; font-size: 1em; padding-left: 2px; width: 70px; } .selector { display: flex; direction: row; font-size: 1.2em; z-index: 1000; } .componentTab { position: absolute; opacity: 0; pointer-events: none; } .disableInterface { pointer-events: none; opacity: 0.5; } .componentTab + .compTabLabel { background: rgba(40, 40, 40, 0.5); padding: 5px; margin-right: 2px; border-radius: 5px 5px 0px 0px; color: #777; } .componentTab:hover + .compTabLabel { background: rgba(90, 90, 90, 0.5); } .componentTab:checked + .compTabLabel { background: rgba(127, 127, 127, 0.5); color: #fff; } .componentVisibility { position: relative; top: -2px; margin-left: 10px; } select { -moz-appearance: none; } select option { color: black; } select:focus { outline: none; border: none; } .sampleDistanceButton { width: 8mm; padding: 4px; padding-left: 6px; z-index: 1000; } .sampleDistanceButton img { width: 1.2em; height: 1.2em; } .sliderColumn { display: flex; flex-direction: column; flex: 1; padding: 0 5px; } .sliderIcon { width: 1.8em; margin-right: 10px; z-index: 1000; } .blendModeButton { width: 8mm; padding: 4px; padding-left: 8px; padding-right: 0px; z-index: 1000; } .blendModeButton img { width: 1.2em; height: 1.2em; } .gradientOpacitySlider { width: 8mm; padding: 4px; padding-left: 6px; padding-right: 0px; z-index: 1000; } .gradientOpacitySlider img { width: 1.2em; height: 1.2em; } .sliderEntry { flex: 1; display: flex; flex-direction: row; align-items: center; } .slider { flex: 1; min-height: 1rem; } .planeLabel { padding-left: 6px; padding: 2px; display: block; font-size: 1.1em; font-family: monospace; color: black; border-width: 2px; border-radius: 10%; } .xPlaneLabel { composes: planeLabel; background-color: #ef5350; } .yPlaneLabel { composes: planeLabel; background-color: #fdd835; } .zPlaneLabel { composes: planeLabel; background-color: #4caf50; } .gradientOpacityScale { z-index: 1100; position: relative; } .gradientOpacityScale input { position: absolute; bottom: 20px; left: -24px; width: 12px; writing-mode: bt-lr; -webkit-appearance: slider-vertical; } .bigFileDrop { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); background-color: white; background-image: url('./dropBG.jpg'); background-repeat: no-repeat; background-position: center; background-size: contain; border-radius: 10px; width: 50px; padding: calc(50vh - 2em) calc(50vw - 25px - 2em); } .fullscreenContainer { position: absolute; width: 100vw; height: 100vh; top: 0; left: 0; overflow: hidden; background: black; margin: 0; padding: 0; } .collapseButton { position: absolute; top: 0; right: -48px; } .windowLevelButton { width: 8mm; padding: 4px; padding-left: 0px; cursor: pointer; z-index: 1000; } .windowLevelButton img { width: 1.3em; height: 1.3em; } .inputLabel { line-height: 1.7; } .saveDialog label { display: flex; align-items: center; } ================================================ FILE: src/UI/reference-ui/src/Layers/addLayerUIRow.js ================================================ import style from '../ItkVtkViewer.module.css' function addLayerUIRow(context) { const layersUIRow = document.createElement('div') layersUIRow.setAttribute('class', style.layersUIRow) context.layers.layersUIGroup.appendChild(layersUIRow) } export default addLayerUIRow ================================================ FILE: src/UI/reference-ui/src/Layers/applyLayersContrastSensitiveStyle.js ================================================ import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' function applyLayersContrastSensitiveStyle(context) { context.layers.uiLayers.forEach(layerEntry => { applyContrastSensitiveStyleToElement(context, 'layerEntry', layerEntry) const visibleButton = layerEntry.children[0] const visibleLabel = visibleButton.children[1] applyContrastSensitiveStyleToElement( context, 'invertibleButton', visibleLabel ) const invisibleButton = layerEntry.children[1] const invisibleLabel = invisibleButton.children[1] applyContrastSensitiveStyleToElement( context, 'invertibleButton', invisibleLabel ) const layerLabel = layerEntry.children[2] applyContrastSensitiveStyleToElement(context, 'layerLabel', layerLabel) const iconElement = layerEntry.children[3] applyContrastSensitiveStyleToElement( context, 'invertibleButton', iconElement ) }) } export default applyLayersContrastSensitiveStyle ================================================ FILE: src/UI/reference-ui/src/Layers/compareUI.js ================================================ import { playIconDataUri, pauseIconDataUri, rotateIconDataUri, } from '@itk-viewer/icons' import style from '../ItkVtkViewer.module.css' import { makeHtml } from '../utils' export const compareUI = context => (send, onReceive) => { const root = document.createElement('div') root.setAttribute( 'style', 'align-self: center; align-content: center; margin-left: 4px; margin-right: 4px' ) const parent = context.layers.compareContainer parent.appendChild(root) const checkerboardUi = makeHtml(`
`) root.appendChild(checkerboardUi) const swapButtonId = `${context.id}-swapImageOrder` const playImageMixButtonId = `${context.id}-image-mix-play` const playImageMixImgId = `${context.id}-image-mix-play-img` const imageMixRoot = makeHtml(`
`) root.appendChild(imageMixRoot) const [xPattern, yPattern, zPattern] = checkerboardUi.querySelectorAll( 'input' ) const [ animateImageMix, imageMixSlider, swapOrder, ] = imageMixRoot.querySelectorAll('input') const animateImageImg = imageMixRoot.querySelector(`#${playImageMixImgId}`) const update = () => { const name = context.images.selectedName const imageContext = context.images.actorContext.get(name) const { compare = undefined } = imageContext ?? {} const { method = undefined, checkerboard } = compare ?? {} if (method && method !== 'disabled') root.style.display = 'block' else root.style.display = 'none' if (checkerboard) { checkerboardUi.style.display = 'flex' } else { checkerboardUi.style.display = 'none' } const [x, y, z] = compare?.pattern ?? [] xPattern.value = x yPattern.value = y zPattern.value = z swapOrder.checked = !!compare?.swapImageOrder ?? false imageMixSlider.value = compare?.imageMix ?? 0.5 animateImageImg.src = imageContext?.imageMixAnimation ? pauseIconDataUri : playIconDataUri } update() const updateCompare = options => { const name = context.images.selectedName const imageContext = context.images.actorContext.get(name) const { compare } = imageContext context.service.send({ type: 'COMPARE_IMAGES', data: { name, fixedImageName: compare.fixedImageName, options: { ...options }, }, }) } const parsePattern = value => Math.max(1, parseInt(value)) xPattern.addEventListener('change', event => { event.preventDefault() event.stopPropagation() const [, ...yz] = context.images.actorContext.get(context.images.selectedName).compare .pattern ?? [] const x = parsePattern(event.target.value) updateCompare({ pattern: [x, ...yz] }) }) yPattern.addEventListener('change', event => { event.preventDefault() event.stopPropagation() const [x, , z] = context.images.actorContext.get(context.images.selectedName).compare .pattern ?? [] const y = parsePattern(event.target.value) updateCompare({ pattern: [x, y, z] }) }) zPattern.addEventListener('change', event => { event.preventDefault() event.stopPropagation() const [x, y] = context.images.actorContext.get(context.images.selectedName).compare .pattern ?? [] const z = parsePattern(event.target.value) updateCompare({ pattern: [x, y, z] }) }) animateImageMix.addEventListener('input', event => { event.preventDefault() event.stopPropagation() const name = context.images.selectedName context.service.send({ type: 'ANIMATE_IMAGE_MIX', data: { name, play: event.target.checked, }, }) }) imageMixSlider.addEventListener('input', event => { event.preventDefault() event.stopPropagation() updateCompare({ imageMix: event.target.value }) }) swapOrder.addEventListener('change', event => { event.preventDefault() event.stopPropagation() updateCompare({ swapImageOrder: event.target.checked }) }) onReceive(event => { const { type } = event if (type === 'COMPARE_UPDATED') { update() } }) } ================================================ FILE: src/UI/reference-ui/src/Layers/createLayerInterface.js ================================================ import '@material/web/dialog/dialog.js' import '@material/web/button/text-button.js' import '@material/web/radio/radio.js' import { invisibleIconDataUri, boundingBoxIconDataUri, downloadIconDataUri, visibleIconDataUri, } from '@itk-viewer/icons' import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import { makeHtml } from '../utils' import './layerIcon.js' import { extensions } from './extensionToImageIo.js' let dialog function createLayerEntry(context, name, layer) { const layerEntry = document.createElement('div') layerEntry.setAttribute('class', style.layerEntryCommon) layerEntry.style.borderWidth = '3px' applyContrastSensitiveStyleToElement(context, 'layerEntry', layerEntry) const visibleButton = document.createElement('div') visibleButton.innerHTML = `` const visibleLabel = visibleButton.children[1] applyContrastSensitiveStyleToElement( context, 'invertibleButton', visibleLabel ) layerEntry.appendChild(visibleButton) const invisibleButton = document.createElement('div') invisibleButton.innerHTML = `` const invisibleLabel = invisibleButton.children[1] applyContrastSensitiveStyleToElement( context, 'invertibleButton', invisibleLabel ) layerEntry.appendChild(invisibleButton) if (layer.visible) { visibleButton.style.display = 'flex' invisibleButton.style.display = 'none' } else { visibleButton.style.display = 'none' invisibleButton.style.display = 'flex' } visibleButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'TOGGLE_LAYER_VISIBILITY', data: name }) visibleButton.checked = true }) invisibleButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'TOGGLE_LAYER_VISIBILITY', data: name }) invisibleButton.checked = false }) const layerLabel = document.createElement('label') layerLabel.setAttribute('class', `${style.layerLabelCommon}`) applyContrastSensitiveStyleToElement(context, 'layerLabel', layerLabel) layerLabel.innerText = name layerEntry.appendChild(layerLabel) const imageIcons = document.createElement('div') imageIcons.style.display = 'flex' imageIcons.setAttribute('class', `${style.iconGroup}`) layerEntry.appendChild(imageIcons) const spinner = document.createElement('div') spinner.setAttribute('class', `${style.ldsRing}`) spinner.innerHTML = '
' imageIcons.appendChild(spinner) layer.spinner = spinner const layerBBoxButton = document.createElement('div') layerBBoxButton.innerHTML = `` const layerBBoxButtonInput = layerBBoxButton.children[0] const layerBBoxLabel = layerBBoxButton.children[1] layerBBoxButton.style.height = '23px' applyContrastSensitiveStyleToElement( context, 'invertibleButton', layerBBoxLabel ) imageIcons.appendChild(layerBBoxButton) layerBBoxButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'TOGGLE_LAYER_BBOX', data: { name: context.images.selectedName, layerName: name, }, }) const actorContext = context.layers.actorContext.get(name) layerBBoxButtonInput.checked = actorContext.bbox }) // There can only be one dialog in app? OK/Cancel buttons don't work if multiple layers... if (!dialog) { dialog = makeHtml(`
Save file format
${extensions .map( (extension, i) => `` ) .join('')}
Cancel OK
`) dialog.addEventListener('close', () => { const okClicked = dialog.returnValue === 'ok' if (okClicked) { const radios = document.querySelectorAll('md-radio[name=format]') const format = Array.from(radios).find(radio => radio.checked).value context.service.send({ type: 'DOWNLOAD_IMAGE', data: { name: context.images.selectedName, format, }, }) } }) imageIcons.appendChild(dialog) } const downloadImage = document.createElement('div') downloadImage.innerHTML = ` ` const downloadImageLabel = downloadImage.children[1] downloadImage.style.height = '23px' applyContrastSensitiveStyleToElement( context, 'invertibleButton', downloadImageLabel ) imageIcons.appendChild(downloadImage) downloadImage.addEventListener('click', event => { event.stopPropagation() dialog.show() }) const icon = makeHtml(``) icon.layer = layer icon.name = name imageIcons.appendChild(icon) layerEntry.addEventListener('click', event => { event.preventDefault() context.service.send({ type: 'SELECT_LAYER', data: name }) }) return layerEntry } function createLayerInterface(context) { const name = context.layers.lastAddedData.name const layer = context.layers.actorContext.get(name) const layerEntry = createLayerEntry(context, name, layer) context.layers.layersUIGroup.appendChild(layerEntry) context.layers.uiLayers.set(name, layerEntry) } export default createLayerInterface ================================================ FILE: src/UI/reference-ui/src/Layers/createLayersInterface.js ================================================ import style from '../ItkVtkViewer.module.css' import { makeHtml } from '../utils' function createLayersInterface(context) { const layersUIGroup = makeHtml(`
`) context.layers.layersUIGroup = layersUIGroup context.uiGroups.set('layers', layersUIGroup) context.uiContainer.appendChild(layersUIGroup) // layer name -> layerEntry map context.layers.uiLayers = new Map() const compareContainer = document.createElement('div') compareContainer.setAttribute('class', style.uiGroup) context.uiContainer.appendChild(compareContainer) context.layers.compareContainer = compareContainer } export default createLayersInterface ================================================ FILE: src/UI/reference-ui/src/Layers/dataUpdateIndicator.js ================================================ export function startDataUpdate({ actorContext: { spinner } }) { spinner.style.visibility = 'visible' } export function finishDataUpdate({ actorContext: { spinner } }) { spinner.style.visibility = 'hidden' } ================================================ FILE: src/UI/reference-ui/src/Layers/extensionToImageIo.js ================================================ // from itk-wasm/packages/image-io/typescript/src/extension-to-image-io.ts // Commented out formats failed roundtrip test const extensionToImageIo = new Map([ // ['bmp', 'bmp'], // ['dcm', 'gdcm'], // ['gipl', 'gipl'], // ['gipl.gz', 'gipl'], ['hdf5', 'hdf5'], // ['jpg', 'jpeg'], // ['jpeg', 'jpeg'], // ['iwi', 'wasm'], ['iwi.cbor', 'wasm'], // ['iwi.cbor.zst', 'wasmZstd'], // ['lsm', 'lsm'], // ['mnc', 'mnc'], // ['mnc.gz', 'mnc'], // ['mnc2', 'mnc'], // ['mgh', 'mgh'], // ['mgz', 'mgh'], // ['mgh.gz', 'mgh'], ['mha', 'meta'], // ['mhd', 'meta'], // ['mrc', 'mrc'], // ['nia', 'nifti'], ['nii', 'nifti'], ['nii.gz', 'nifti'], // ['hdr', 'nifti'], ['nrrd', 'nrrd'], // ['nhdr', 'nrrd'], // ['png', 'png'], // ['pic', 'bioRad'], ['tif', 'tiff'], // ['tiff', 'tiff'], ['vtk', 'vtk'], // ['isq', 'scanco'], // ['aim', 'scanco'], // ['fdf', 'fdf'], ]) export const extensions = Array.from(extensionToImageIo.keys()) ================================================ FILE: src/UI/reference-ui/src/Layers/layerIcon.ts ================================================ import { LitElement, html, css } from 'lit' import { customElement, property, state } from 'lit/decorators.js' import { compareArrays, connectState } from 'xstate-lit/dist/select-state.js' import { imageIconDataUri, labelsIconDataUri, toggleIconDataUri, } from '@itk-viewer/icons' import { viewerContext } from '../context' import './layerSettings.js' @customElement('layer-icon') class LayerIcon extends LitElement { @property() layer: { type: string } = { type: 'image' } @property() name: string = '' @state() private settingsOpen = true otherImages = connectState( viewerContext, this, (state: any) => [...state.context.layers.actorContext.keys()].filter( key => key !== this.name && state.context.images.actorContext.get(this.name)?.labelImage?.name !== key ), compareArrays ) selectedName = connectState( viewerContext, this, (state: any) => state.context.images.selectedName ) getIcon() { if (this.layer.type === 'image') { if ( this.name === this.selectedName.value && this.otherImages.value && this.otherImages.value.length > 0 ) return { icon: toggleIconDataUri, alt: 'settings' } return { icon: imageIconDataUri, alt: 'image' } } if (this.layer.type === 'labelImage') return { icon: labelsIconDataUri, alt: 'labels' } throw new Error(`Unsupported layer type: ${this.layer.type}`) } render() { const { icon, alt } = this.getIcon() const settingsPossible = alt === 'settings' return html`
${alt}
` } static styles = css` .icon { height: 1.2em; width: 1.2em; padding-top: 2px; padding-bottom: 2px; padding-left: 8px; padding-right: 6px; } ` } ================================================ FILE: src/UI/reference-ui/src/Layers/layerSettings.ts ================================================ import { LitElement, html, css } from 'lit' import { customElement, property } from 'lit/decorators.js' import { ref, createRef, Ref } from 'lit/directives/ref.js' import { map } from 'lit/directives/map.js' import { ContextConsumer } from '@lit-labs/context' import '@material/web/menu/menu.js' import { MdMenu } from '@material/web/menu/menu.js' import '@material/web/menu/menu-item.js' import { makeHtml } from '../utils' import style from '../ItkVtkViewer.module.css' import { viewerContext } from '../context' @customElement('layer-settings') class LayerSettings extends LitElement { @property() name: string = '' @property() otherImages: Array = [] @property() enable: boolean = true menuRef: Ref = createRef() anchorRef: Ref = createRef() // avoid overflow: hidden on parents clipping menu floatingAnchor = makeHtml(`
`) stateService = new ContextConsumer(this, viewerContext, undefined, true) connectedCallback() { super.connectedCallback() document.body.appendChild(this.floatingAnchor) } disconnectedCallback() { super.disconnectedCallback() document.body.removeChild(this.floatingAnchor) } showMenu() { if (!this.enable) return const { top = 0, left = 0 } = this.anchorRef.value?.getBoundingClientRect() ?? {} this.floatingAnchor.style.top = `${top}px` this.floatingAnchor.style.left = `${left}px` if (this.menuRef.value) { this.floatingAnchor.appendChild(this.menuRef.value) this.menuRef.value.anchorElement = this.floatingAnchor this.menuRef.value.show() } } compareWith(name: string, method: string) { this.stateService.value?.service.send({ type: 'COMPARE_IMAGES', data: { name: this.name, fixedImageName: name, options: { method }, }, }) } stopComparing() { this.stateService.value?.service.send({ type: 'COMPARE_IMAGES', data: { name: this.name, options: { method: 'disabled' }, }, }) } render() { return html`
{ this.showMenu() this.render() }} class=${this.enable ? 'clickable' : ''} >
${map( this.otherImages, name => html` this.compareWith(name, 'checkerboard')} >
"Checkerboard compare with ${name}"
this.compareWith(name, 'green-magenta')} >
"Green-Magenta compare with ${name}"
this.compareWith(name, 'cyan-red')} >
"Cyan-Red compare with ${name}"
this.compareWith(name, 'cyan-magenta')} >
"Cyan-Magenta compare with ${name}"
this.compareWith(name, 'blend')}>
"Blend compare with ${name}"
` )}
"Stop comparing"
` } static styles = css` .clickable { cursor: pointer; } ` } ================================================ FILE: src/UI/reference-ui/src/Layers/layersUIMachineOptions.js ================================================ import createLayersInterface from './createLayersInterface' import createLayerInterface from './createLayerInterface' import toggleLayerVisibility from './toggleLayerVisibility' import selectLayer from './selectLayer' import { startDataUpdate, finishDataUpdate } from './dataUpdateIndicator' import { compareUI } from './compareUI' const layersUIMachineOptions = { layerUIActor: { actions: { createLayerInterface, selectLayer, toggleLayerVisibility, startDataUpdate, finishDataUpdate, }, }, actions: { createLayersInterface, }, services: { compareUI, }, } export default layersUIMachineOptions ================================================ FILE: src/UI/reference-ui/src/Layers/selectLayer.js ================================================ import applyGroupVisibility from '../applyGroupVisibility' const selectedBorderWidth = '3px' const unselectedBorderWidth = '2px' function selectLayer(context, event) { const name = event.data const actorContext = context.layers.actorContext.get(name) const layerEntry = context.layers.uiLayers.get(name) layerEntry.style.borderWidth = selectedBorderWidth const type = actorContext.type context.layers.actorContext.forEach((ac, layerName) => { if (layerName !== name && ac.type === type) { const entry = context.layers.uiLayers.get(layerName) entry.style.borderWidth = unselectedBorderWidth } }) if (!actorContext.visible) { context.service.send({ type: 'TOGGLE_LAYER_VISIBILITY', data: name, }) } if (!context.uiCollapsed) { const { imageActorContext } = actorContext switch (type) { case 'image': applyGroupVisibility(context, ['images'], true) if (imageActorContext.labelImageName) { applyGroupVisibility( context, ['labelImages', 'labelImageWeights'], true ) } break case 'labelImage': if (actorContext.imageName) { applyGroupVisibility(context, ['images'], true) } applyGroupVisibility( context, ['labelImages', 'labelImageWeights'], true ) break default: console.error(`Unsupported layer type: ${type}`) } } } export default selectLayer ================================================ FILE: src/UI/reference-ui/src/Layers/toggleLayerVisibility.js ================================================ import applyGroupVisibility from '../applyGroupVisibility' function toggleLayerVisibility(context, event) { const layers = context.layers const name = event.data const actorContext = layers.actorContext.get(name) const visible = actorContext.visible const layerEntry = context.layers.uiLayers.get(name) const visibleButton = layerEntry.children[0] const invisibleButton = layerEntry.children[1] if (visible) { visibleButton.style.display = 'flex' invisibleButton.style.display = 'none' } else { visibleButton.style.display = 'none' invisibleButton.style.display = 'flex' switch (actorContext.type) { case 'image': applyGroupVisibility(context, ['images'], false) break case 'labelImage': applyGroupVisibility( context, ['labelImages', 'labelImageWeights'], false ) break default: console.error(`Unsupported layer type: ${type}`) } } } export default toggleLayerVisibility ================================================ FILE: src/UI/reference-ui/src/Main/applyMainContrastSensitiveStyle.js ================================================ import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' function applyMainContrastSensitiveStyle(context) { applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.main.collapseUIButton ) applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.main.screenshotButton ) if (context.main.fullscreenButton) { applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.main.fullscreenButton ) } if (context.main.rotateButtonLabel) { applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.main.rotateButtonLabel ) } applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.main.annotationsButtonLabel ) applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.main.axesButtonLabel ) applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.main.cropButtonLabel ) applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.main.resetCropButtonLabel ) applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.main.bgColorButtonLabel ) if (!context.use2D) { applyContrastSensitiveStyleToElement( context, 'tooltipButton', context.main.xPlaneButtonLabel ) applyContrastSensitiveStyleToElement( context, 'tooltipButton', context.main.yPlaneButtonLabel ) applyContrastSensitiveStyleToElement( context, 'tooltipButton', context.main.zPlaneButtonLabel ) applyContrastSensitiveStyleToElement( context, 'tooltipButton', context.main.volumeButtonLabel ) applyContrastSensitiveStyleToElement( context, 'tooltipButton', context.main.viewPlanesButtonLabel ) } applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.main.resetCameraButtonLabel ) } export default applyMainContrastSensitiveStyle ================================================ FILE: src/UI/reference-ui/src/Main/applySlicingPlanes.js ================================================ function applySlicingPlanes(context, event) { const slicingPlanes = event.data const main = context.main if (context.use2D) { if (main.viewPlanesButton) { main.viewPlanesButton.style.display = 'none' } return } if ( !slicingPlanes.x.visibile && !slicingPlanes.y.visible && !slicingPlanes.z.visible ) { if (main.viewPlanesButtonInput) { main.viewPlanesButtonInput.checked = false } } else { if (main.viewPlanesButtonInput) { main.viewPlanesButtonInput.checked = true } } if (!main.planeUIGroup) { return } main.xSliceElement.min = slicingPlanes.x.min main.xSliceElement.max = slicingPlanes.x.max main.xSliceElement.step = slicingPlanes.x.step main.ySliceElement.min = slicingPlanes.y.min main.ySliceElement.max = slicingPlanes.y.max main.ySliceElement.step = slicingPlanes.y.step main.zSliceElement.min = slicingPlanes.z.min main.zSliceElement.max = slicingPlanes.z.max main.zSliceElement.step = slicingPlanes.z.step if (main.viewMode === 'Volume') { main.xPlaneVisibleButton.style.display = slicingPlanes.x.visible ? 'flex' : 'none' main.xPlaneInvisibleButton.style.display = slicingPlanes.x.visible ? 'none' : 'flex' main.yPlaneVisibleButton.style.display = slicingPlanes.y.visible ? 'flex' : 'none' main.yPlaneInvisibleButton.style.display = slicingPlanes.y.visible ? 'none' : 'flex' main.zPlaneVisibleButton.style.display = slicingPlanes.z.visible ? 'flex' : 'none' main.zPlaneInvisibleButton.style.display = slicingPlanes.z.visible ? 'none' : 'flex' } main.xPlanePauseButton.style.display = slicingPlanes.x.scroll ? 'flex' : 'none' main.xPlanePlayButton.style.display = slicingPlanes.x.scroll ? 'none' : 'flex' main.yPlanePauseButton.style.display = slicingPlanes.y.scroll ? 'flex' : 'none' main.yPlanePlayButton.style.display = slicingPlanes.y.scroll ? 'none' : 'flex' main.zPlanePauseButton.style.display = slicingPlanes.z.scroll ? 'flex' : 'none' main.zPlanePlayButton.style.display = slicingPlanes.z.scroll ? 'none' : 'flex' } export default applySlicingPlanes ================================================ FILE: src/UI/reference-ui/src/Main/applyXSlice.js ================================================ function applyXSlice(context, event) { const position = event.data const xPlaneLabel = context.main.xPlaneLabel if (!xPlaneLabel) { return } const numberOfValueChars = 6 const valueString = String(position).substring(0, numberOfValueChars) const padLength = valueString.length < numberOfValueChars ? numberOfValueChars - valueString.length : 0 const pad = ' '.repeat(padLength) xPlaneLabel.innerHTML = `X: ${pad}${valueString}` context.main.xSliceElement.value = position } export default applyXSlice ================================================ FILE: src/UI/reference-ui/src/Main/applyYSlice.js ================================================ function applyYSlice(context, event) { const position = event.data const yPlaneLabel = context.main.yPlaneLabel if (!yPlaneLabel) { return } const numberOfValueChars = 6 const valueString = String(position).substring(0, numberOfValueChars) const padLength = valueString.length < numberOfValueChars ? numberOfValueChars - valueString.length : 0 const pad = ' '.repeat(padLength) yPlaneLabel.innerHTML = `Y: ${pad}${valueString}` context.main.ySliceElement.value = position } export default applyYSlice ================================================ FILE: src/UI/reference-ui/src/Main/applyZSlice.js ================================================ function applyZSlice(context, event) { const position = event.data const zPlaneLabel = context.main.zPlaneLabel if (!zPlaneLabel) { return } const numberOfValueChars = 6 const valueString = String(position).substring(0, numberOfValueChars) const padLength = valueString.length < numberOfValueChars ? numberOfValueChars - valueString.length : 0 const pad = ' '.repeat(padLength) zPlaneLabel.innerHTML = `Z: ${pad}${valueString}` context.main.zSliceElement.value = position } export default applyZSlice ================================================ FILE: src/UI/reference-ui/src/Main/createAnnotationsButton.js ================================================ import style from '../ItkVtkViewer.module.css' import { annotationsIconDataUri } from '@itk-viewer/icons' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import toggleAnnotations from './toggleAnnotations' function createAnnotationsButton(context, mainUIRow) { const annotationsButton = document.createElement('div') annotationsButton.innerHTML = `` const annotationsButtonInput = annotationsButton.children[0] const annotationsButtonLabel = annotationsButton.children[1] context.main.annotationsButtonLabel = annotationsButtonLabel context.main.annotationsButtonInput = annotationsButtonInput applyContrastSensitiveStyleToElement( context, 'invertibleButton', annotationsButtonLabel ) toggleAnnotations(context) annotationsButton.addEventListener('change', event => { event.preventDefault() event.stopPropagation() context.service.send('TOGGLE_ANNOTATIONS') }) mainUIRow.appendChild(annotationsButton) } export default createAnnotationsButton ================================================ FILE: src/UI/reference-ui/src/Main/createAxesButton.js ================================================ import style from '../ItkVtkViewer.module.css' import { axesIconDataUri } from '@itk-viewer/icons' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import toggleAxes from './toggleAxes' function createAxesButton(context, mainUIRow) { const axesButton = document.createElement('div') axesButton.innerHTML = `` const axesButtonInput = axesButton.children[0] const axesButtonLabel = axesButton.children[1] context.main.axesButtonLabel = axesButtonLabel context.main.axesButtonInput = axesButtonInput applyContrastSensitiveStyleToElement( context, 'invertibleButton', axesButtonLabel ) toggleAxes(context) axesButton.addEventListener('change', event => { event.preventDefault() event.stopPropagation() context.service.send('TOGGLE_AXES') }) mainUIRow.appendChild(axesButton) } export default createAxesButton ================================================ FILE: src/UI/reference-ui/src/Main/createBackgroundColorButton.js ================================================ import style from '../ItkVtkViewer.module.css' import { selectColorIconDataUri } from '@itk-viewer/icons' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' function createBackgroundColorButton(context, mainUIRow) { const viewerDOMId = context.id const bgColorButton = document.createElement('div') bgColorButton.innerHTML = `` const bgColorButtonInput = bgColorButton.children[0] const bgColorButtonLabel = bgColorButton.children[1] context.main.bgColorButtonLabel = bgColorButtonLabel context.main.bgColorButtonInput = bgColorButtonInput applyContrastSensitiveStyleToElement( context, 'invertibleButton', bgColorButtonLabel ) bgColorButton.addEventListener('change', event => { event.preventDefault() event.stopPropagation() context.service.send('TOGGLE_BACKGROUND_COLOR') }) bgColorButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() context.service.send('TOGGLE_BACKGROUND_COLOR') }) mainUIRow.appendChild(bgColorButton) } export default createBackgroundColorButton ================================================ FILE: src/UI/reference-ui/src/Main/createCroppingButtons.js ================================================ import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import { cropIconDataUri, resetCropIconDataUri } from '@itk-viewer/icons' import toggleCroppingPlanes from './toggleCroppingPlanes' function createCroppingButtons(context, mainUIRow) { const viewerDOMId = context.id const cropButton = document.createElement('div') cropButton.innerHTML = `` const cropButtonInput = cropButton.children[0] const cropButtonLabel = cropButton.children[1] context.main.cropButtonLabel = cropButtonLabel applyContrastSensitiveStyleToElement( context, 'invertibleButton', cropButtonLabel ) context.main.cropButtonInput = cropButtonInput toggleCroppingPlanes(context) cropButton.addEventListener('change', event => { event.preventDefault() event.stopPropagation() context.service.send('TOGGLE_CROPPING_PLANES') }) mainUIRow.appendChild(cropButton) const resetCropButton = document.createElement('div') resetCropButton.innerHTML = `` const resetCropButtonLabel = resetCropButton.children[1] context.main.resetCropButtonLabel = resetCropButtonLabel applyContrastSensitiveStyleToElement( context, 'invertibleButton', resetCropButtonLabel ) resetCropButton.addEventListener('change', event => { event.preventDefault() event.stopPropagation() context.service.send('RESET_CROPPING_PLANES') context.service.send('CROPPING_PLANES_CHANGED_BY_USER') }) resetCropButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() context.service.send('RESET_CROPPING_PLANES') context.service.send('CROPPING_PLANES_CHANGED_BY_USER') }) mainUIRow.appendChild(resetCropButton) } export default createCroppingButtons ================================================ FILE: src/UI/reference-ui/src/Main/createFullscreenButton.js ================================================ import style from '../ItkVtkViewer.module.css' import { fullscreenIconDataUri } from '@itk-viewer/icons' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import fullscreenMethods from './fullscreenMethods' function createFullscreenButton(context, mainUIRow) { if (fullscreenMethods) { const fullscreenButton = document.createElement('div') fullscreenButton.innerHTML = `` const fullscreenButtonInput = fullscreenButton.children[0] const fullscreenButtonLabel = fullscreenButton.children[1] applyContrastSensitiveStyleToElement( context, 'invertibleButton', fullscreenButton ) const container = context.rootContainer const oldWidth = container.style.width const oldHeight = container.style.height context.main.rootContainerOldWidth = container.style.width context.main.rootContainerOldHeight = container.style.height fullscreenButton.addEventListener('change', event => { event.preventDefault() event.stopPropagation() context.service.send('TOGGLE_FULLSCREEN') }) document.addEventListener(fullscreenMethods[2], event => { if (!document[fullscreenMethods[3]]) { context.service.send('DISABLE_FULLSCREEN') } }) context.main.fullscreenButton = fullscreenButton mainUIRow.appendChild(fullscreenButton) } } export default createFullscreenButton ================================================ FILE: src/UI/reference-ui/src/Main/createMainInterface.js ================================================ import style from '../ItkVtkViewer.module.css' import createScreenshotButton from './createScreenshotButton' import createFullscreenButton from './createFullscreenButton' import createRotateButton from './createRotateButton' import createAnnotationsButton from './createAnnotationsButton' import createAxesButton from './createAxesButton' import createViewPlanesToggle from './createViewPlanesToggle' import createPlaneSliders from './createPlaneSliders' import createBackgroundColorButton from './createBackgroundColorButton' import createCroppingButtons from './createCroppingButtons' import createViewModeButtons from './createViewModeButtons' import createResetCameraButton from './createResetCameraButton' function createMainInterface(context) { const mainUIGroup = document.createElement('div') mainUIGroup.setAttribute('class', style.uiGroup) context.uiGroups.set('main', mainUIGroup) const mainUIRow1 = document.createElement('div') mainUIRow1.setAttribute('class', style.mainUIRow) mainUIGroup.appendChild(mainUIRow1) createScreenshotButton(context, mainUIRow1) createFullscreenButton(context, mainUIRow1) if (!context.use2D) { createRotateButton(context, mainUIRow1) } createAnnotationsButton(context, mainUIRow1) createAxesButton(context, mainUIRow1) createViewPlanesToggle(context, mainUIRow1) createPlaneSliders(context) createBackgroundColorButton(context, mainUIRow1) const mainUIRow2 = document.createElement('div') mainUIRow2.setAttribute('class', style.mainUIRow) if (context.use2D) { createViewModeButtons(context, mainUIRow2) createCroppingButtons(context, mainUIRow1) createResetCameraButton(context, mainUIRow1) } else { createViewModeButtons(context, mainUIRow2) createCroppingButtons(context, mainUIRow2) createResetCameraButton(context, mainUIRow2) mainUIGroup.appendChild(mainUIRow2) } context.uiContainer.appendChild(mainUIGroup) } export default createMainInterface ================================================ FILE: src/UI/reference-ui/src/Main/createPlaneSliders.js ================================================ import macro from '@kitware/vtk.js/macro' import style from '../ItkVtkViewer.module.css' import { visibleIconDataUri, invisibleIconDataUri, pauseIconDataUri, playIconDataUri, } from '@itk-viewer/icons' function createPlaneSliders(context) { const planeUIGroup = document.createElement('div') planeUIGroup.setAttribute('class', style.uiGroup) const numberOfValueChars = 6 const viewerDOMId = context.id const xPlaneRow = document.createElement('div') xPlaneRow.setAttribute('class', style.planeUIRow) xPlaneRow.className += ` ${viewerDOMId}-x-plane-row` context.main.xPlaneRow = xPlaneRow const xPlaneVisibleButton = document.createElement('div') xPlaneVisibleButton.innerHTML = `` const xPlaneVisibleButtonInput = xPlaneVisibleButton.children[0] const xPlaneVisibleLabel = xPlaneVisibleButton.children[1] xPlaneRow.appendChild(xPlaneVisibleButton) context.main.xPlaneVisibleButton = xPlaneVisibleButton const xPlaneInvisibleButton = document.createElement('div') xPlaneVisibleButton.setAttribute('class', style.visibleButton) xPlaneInvisibleButton.setAttribute('class', style.visibleButton) xPlaneInvisibleButton.innerHTML = `` const xPlaneInvisibleButtonInput = xPlaneInvisibleButton.children[0] const xPlaneInvisibleLabel = xPlaneInvisibleButton.children[1] xPlaneRow.appendChild(xPlaneInvisibleButton) context.main.xPlaneInvisibleButton = xPlaneInvisibleButton if (context.main.viewMode === 'Volume') { if (context.main.slicingPlanes.x.visible) { xPlaneVisibleButton.style.display = 'flex' xPlaneInvisibleButton.style.display = 'none' } else { xPlaneVisibleButton.style.display = 'none' xPlaneInvisibleButton.style.display = 'flex' } } else { xPlaneVisibleButton.style.display = 'none' xPlaneInvisibleButton.style.display = 'none' } xPlaneVisibleButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const slicingPlanes = context.main.slicingPlanes slicingPlanes.x.visible = false context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) xPlaneVisibleButton.checked = true }) xPlaneInvisibleButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const slicingPlanes = context.main.slicingPlanes slicingPlanes.x.visible = true context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) xPlaneInvisibleButton.checked = false }) const xPlanePauseButton = document.createElement('div') xPlanePauseButton.innerHTML = `` const xPlanePauseButtonInput = xPlanePauseButton.children[0] const xPlanePauseLabel = xPlanePauseButton.children[1] xPlaneRow.appendChild(xPlanePauseButton) context.main.xPlanePauseButton = xPlanePauseButton const xPlanePlayButton = document.createElement('div') xPlanePauseButton.setAttribute('class', style.visibleButton) xPlanePlayButton.setAttribute('class', style.visibleButton) xPlanePlayButton.innerHTML = `` const xPlanePlayButtonInput = xPlanePlayButton.children[0] const xPlanePlayLabel = xPlanePlayButton.children[1] xPlaneRow.appendChild(xPlanePlayButton) context.main.xPlanePlayButton = xPlanePlayButton if (context.main.slicingPlanes.x.scroll) { xPlanePauseButton.style.display = 'flex' xPlanePlayButton.style.display = 'none' } else { xPlanePauseButton.style.display = 'none' xPlanePlayButton.style.display = 'flex' } xPlanePauseButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const slicingPlanes = context.main.slicingPlanes slicingPlanes.x.scroll = false context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) xPlanePauseButton.checked = true }) xPlanePlayButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const slicingPlanes = context.main.slicingPlanes slicingPlanes.x.scroll = true slicingPlanes.x.visible = true context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) xPlanePlayButton.checked = false }) const xSliderEntry = document.createElement('div') xSliderEntry.setAttribute('class', style.sliderEntry) xSliderEntry.innerHTML = ` ` const xPlaneLabel = xSliderEntry.querySelector(`#${viewerDOMId}-xSliceLabel`) context.main.xPlaneLabel = xPlaneLabel const xSliceElement = xSliderEntry.querySelector(`#${viewerDOMId}-xSlice`) xSliceElement.addEventListener('input', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'X_SLICE_CHANGED', data: Number(xSliceElement.value), }) }) context.main.xSliceElement = xSliceElement xPlaneRow.appendChild(xSliderEntry) planeUIGroup.appendChild(xPlaneRow) const yPlaneRow = document.createElement('div') yPlaneRow.setAttribute('class', style.planeUIRow) yPlaneRow.className += ` ${viewerDOMId}-y-plane-row` context.main.yPlaneRow = yPlaneRow const yPlaneVisibleButton = document.createElement('div') yPlaneVisibleButton.setAttribute('class', style.visibleButton) yPlaneVisibleButton.innerHTML = `` const yPlaneVisibleButtonInput = yPlaneVisibleButton.children[0] const yPlaneVisibleLabel = yPlaneVisibleButton.children[1] yPlaneRow.appendChild(yPlaneVisibleButton) context.main.yPlaneVisibleButton = yPlaneVisibleButton const yPlaneInvisibleButton = document.createElement('div') yPlaneInvisibleButton.setAttribute('class', style.visibleButton) yPlaneInvisibleButton.innerHTML = `` const yPlaneInvisibleButtonInput = yPlaneInvisibleButton.children[0] const yPlaneInvisibleLabel = yPlaneInvisibleButton.children[1] yPlaneRow.appendChild(yPlaneInvisibleButton) context.main.yPlaneInvisibleButton = yPlaneInvisibleButton if (context.main.viewMode === 'Volume') { if (context.main.slicingPlanes.y.visible) { yPlaneVisibleButton.style.display = 'flex' yPlaneInvisibleButton.style.display = 'none' } else { yPlaneVisibleButton.style.display = 'none' yPlaneInvisibleButton.style.display = 'flex' } } else { yPlaneVisibleButton.style.display = 'none' yPlaneInvisibleButton.style.display = 'none' } yPlaneVisibleButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const slicingPlanes = context.main.slicingPlanes slicingPlanes.y.visible = false context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) yPlaneVisibleButton.checked = true }) yPlaneInvisibleButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const slicingPlanes = context.main.slicingPlanes slicingPlanes.y.visible = true context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) yPlaneInvisibleButton.checked = false }) const yPlanePauseButton = document.createElement('div') yPlanePauseButton.innerHTML = `` const yPlanePauseButtonInput = yPlanePauseButton.children[0] const yPlanePauseLabel = yPlanePauseButton.children[1] yPlaneRow.appendChild(yPlanePauseButton) context.main.yPlanePauseButton = yPlanePauseButton const yPlanePlayButton = document.createElement('div') yPlanePauseButton.setAttribute('class', style.visibleButton) yPlanePlayButton.setAttribute('class', style.visibleButton) yPlanePlayButton.innerHTML = `` const yPlanePlayButtonInput = yPlanePlayButton.children[0] const yPlanePlayLabel = yPlanePlayButton.children[1] yPlaneRow.appendChild(yPlanePlayButton) context.main.yPlanePlayButton = yPlanePlayButton if (context.main.slicingPlanes.y.scroll) { yPlanePauseButton.style.display = 'flex' yPlanePlayButton.style.display = 'none' } else { yPlanePauseButton.style.display = 'none' yPlanePlayButton.style.display = 'flex' } yPlanePauseButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const slicingPlanes = context.main.slicingPlanes slicingPlanes.y.scroll = false context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) yPlanePauseButton.checked = true }) yPlanePlayButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const slicingPlanes = context.main.slicingPlanes slicingPlanes.y.scroll = true slicingPlanes.y.visible = true context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) yPlanePlayButton.checked = false }) const ySliderEntry = document.createElement('div') ySliderEntry.setAttribute('class', style.sliderEntry) ySliderEntry.innerHTML = ` ` const yPlaneLabel = ySliderEntry.querySelector(`#${viewerDOMId}-ySliceLabel`) context.main.yPlaneLabel = yPlaneLabel const ySliceElement = ySliderEntry.querySelector(`#${viewerDOMId}-ySlice`) ySliceElement.addEventListener('input', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'Y_SLICE_CHANGED', data: Number(ySliceElement.value), }) }) context.main.ySliceElement = ySliceElement yPlaneRow.appendChild(ySliderEntry) planeUIGroup.appendChild(yPlaneRow) const zPlaneRow = document.createElement('div') zPlaneRow.setAttribute('class', style.planeUIRow) zPlaneRow.className += ` ${viewerDOMId}-z-plane-row` context.main.zPlaneRow = zPlaneRow const zPlaneVisibleButton = document.createElement('div') zPlaneVisibleButton.setAttribute('class', style.visibleButton) zPlaneVisibleButton.innerHTML = `` const zPlaneVisibleButtonInput = zPlaneVisibleButton.children[0] const zPlaneVisibleLabel = zPlaneVisibleButton.children[1] zPlaneRow.appendChild(zPlaneVisibleButton) context.main.zPlaneVisibleButton = zPlaneVisibleButton const zPlaneInvisibleButton = document.createElement('div') zPlaneInvisibleButton.setAttribute('class', style.visibleButton) zPlaneInvisibleButton.innerHTML = `` const zPlaneInvisibleButtonInput = zPlaneInvisibleButton.children[0] const zPlaneInvisibleLabel = zPlaneInvisibleButton.children[1] zPlaneRow.appendChild(zPlaneInvisibleButton) context.main.zPlaneInvisibleButton = zPlaneInvisibleButton if (context.main.viewMode === 'Volume') { if (context.main.slicingPlanes.z.visible) { zPlaneVisibleButton.style.display = 'flex' zPlaneInvisibleButton.style.display = 'none' } else { zPlaneVisibleButton.style.display = 'none' zPlaneInvisibleButton.style.display = 'flex' } } else { zPlaneVisibleButton.style.display = 'none' zPlaneInvisibleButton.style.display = 'none' } zPlaneVisibleButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const slicingPlanes = context.main.slicingPlanes slicingPlanes.z.visible = false context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) zPlaneVisibleButton.checked = true }) zPlaneInvisibleButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const slicingPlanes = context.main.slicingPlanes slicingPlanes.z.visible = true context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) zPlaneInvisibleButton.checked = false }) const zPlanePauseButton = document.createElement('div') zPlanePauseButton.innerHTML = `` const zPlanePauseButtonInput = zPlanePauseButton.children[0] const zPlanePauseLabel = zPlanePauseButton.children[1] zPlaneRow.appendChild(zPlanePauseButton) context.main.zPlanePauseButton = zPlanePauseButton const zPlanePlayButton = document.createElement('div') zPlanePauseButton.setAttribute('class', style.visibleButton) zPlanePlayButton.setAttribute('class', style.visibleButton) zPlanePlayButton.innerHTML = `` const zPlanePlayButtonInput = zPlanePlayButton.children[0] const zPlanePlayLabel = zPlanePlayButton.children[1] zPlaneRow.appendChild(zPlanePlayButton) context.main.zPlanePlayButton = zPlanePlayButton if (context.main.slicingPlanes.z.scroll) { zPlanePauseButton.style.display = 'flex' zPlanePlayButton.style.display = 'none' } else { zPlanePauseButton.style.display = 'none' zPlanePlayButton.style.display = 'flex' } zPlanePauseButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const slicingPlanes = context.main.slicingPlanes slicingPlanes.z.scroll = false context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) zPlanePauseButton.checked = true }) zPlanePlayButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const slicingPlanes = context.main.slicingPlanes slicingPlanes.z.scroll = true slicingPlanes.z.visible = true context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) zPlanePlayButton.checked = false }) const zSliderEntry = document.createElement('div') zSliderEntry.setAttribute('class', style.sliderEntry) zSliderEntry.innerHTML = ` ` const zPlaneLabel = zSliderEntry.querySelector(`#${viewerDOMId}-zSliceLabel`) context.main.zPlaneLabel = zPlaneLabel const zSliceElement = zSliderEntry.querySelector(`#${viewerDOMId}-zSlice`) zSliceElement.addEventListener('input', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'Z_SLICE_CHANGED', data: Number(zSliceElement.value), }) }) context.main.zSliceElement = zSliceElement zPlaneRow.appendChild(zSliderEntry) planeUIGroup.appendChild(zPlaneRow) const viewContainer = context.viewContainers.get('volume') viewContainer.appendChild(planeUIGroup) if (context.use2D || context.uiCollapsed) { planeUIGroup.style.display = 'none' } context.main.planeUIGroup = planeUIGroup } export default createPlaneSliders ================================================ FILE: src/UI/reference-ui/src/Main/createResetCameraButton.js ================================================ import style from '../ItkVtkViewer.module.css' import { resetCameraIconDataUri } from '@itk-viewer/icons' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' function createResetCameraButton(context, mainUIRow) { const viewerDOMId = context.id const resetCameraButton = document.createElement('div') resetCameraButton.innerHTML = `` const resetCameraButtonLabel = resetCameraButton.children[1] context.main.resetCameraButtonLabel = resetCameraButtonLabel applyContrastSensitiveStyleToElement( context, 'invertibleButton', resetCameraButtonLabel ) resetCameraButton.addEventListener('change', event => { event.preventDefault() event.stopPropagation() context.service.send('RESET_CAMERA') }) resetCameraButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() context.service.send('RESET_CAMERA') }) mainUIRow.appendChild(resetCameraButton) } export default createResetCameraButton ================================================ FILE: src/UI/reference-ui/src/Main/createRotateButton.js ================================================ import style from '../ItkVtkViewer.module.css' import { rotateIconDataUri } from '@itk-viewer/icons' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import toggleRotate from './toggleRotate' function createRotateButton(context, mainUIRow) { const rotateButton = document.createElement('div') rotateButton.innerHTML = `` const rotateButtonInput = rotateButton.children[0] const rotateButtonLabel = rotateButton.children[1] context.main.rotateButtonLabel = rotateButtonLabel context.main.rotateButtonInput = rotateButtonInput applyContrastSensitiveStyleToElement( context, 'invertibleButton', rotateButtonLabel ) toggleRotate(context) rotateButton.addEventListener('change', event => { event.preventDefault() event.stopPropagation() context.service.send('TOGGLE_ROTATE') }) mainUIRow.appendChild(rotateButton) } export default createRotateButton ================================================ FILE: src/UI/reference-ui/src/Main/createScreenshotButton.js ================================================ import style from '../ItkVtkViewer.module.css' import { screenshotIconDataUri } from '@itk-viewer/icons' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' function createScreenshotButton(context, mainUIRow) { const screenshotButton = document.createElement('div') screenshotButton.innerHTML = `` const screenshotButtonInput = screenshotButton.children[0] const screenshotLabel = screenshotButton.children[1] applyContrastSensitiveStyleToElement( context, 'invertibleButton', screenshotLabel ) screenshotButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() context.service.send('TAKE_SCREENSHOT') screenshotButton.checked = true }) context.main.screenshotButton = screenshotButton mainUIRow.appendChild(screenshotButton) } export default createScreenshotButton ================================================ FILE: src/UI/reference-ui/src/Main/createViewModeButtons.js ================================================ import style from '../ItkVtkViewer.module.css' import { volumeIconDataUri, redPlaneIconDataUri, yellowPlaneIconDataUri, greenPlaneIconDataUri, } from '@itk-viewer/icons' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' function createViewModeButtons(context, mainRow) { const viewerDOMId = context.id const xPlaneButton = document.createElement('div') context.main.xPlaneButton = xPlaneButton xPlaneButton.innerHTML = `` const xPlaneButtonLabel = xPlaneButton.children[1] context.main.xPlaneButtonLabel = xPlaneButtonLabel applyContrastSensitiveStyleToElement( context, 'tooltipButton', xPlaneButtonLabel ) xPlaneButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'VIEW_MODE_CHANGED', data: 'XPlane' }) }) mainRow.appendChild(xPlaneButton) const yPlaneButton = document.createElement('div') context.main.yPlaneButton = yPlaneButton yPlaneButton.innerHTML = `` const yPlaneButtonLabel = yPlaneButton.children[1] context.main.yPlaneButtonLabel = yPlaneButtonLabel applyContrastSensitiveStyleToElement( context, 'tooltipButton', yPlaneButtonLabel ) yPlaneButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'VIEW_MODE_CHANGED', data: 'YPlane' }) }) mainRow.appendChild(yPlaneButton) const zPlaneButton = document.createElement('div') context.main.zPlaneButton = zPlaneButton zPlaneButton.innerHTML = `` const zPlaneButtonLabel = zPlaneButton.children[1] context.main.zPlaneButtonLabel = zPlaneButtonLabel applyContrastSensitiveStyleToElement( context, 'tooltipButton', zPlaneButtonLabel ) zPlaneButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'VIEW_MODE_CHANGED', data: 'ZPlane' }) }) mainRow.appendChild(zPlaneButton) const volumeButton = document.createElement('div') context.main.volumeButton = volumeButton volumeButton.innerHTML = `` const volumeButtonLabel = volumeButton.children[1] context.main.volumeButtonLabel = volumeButtonLabel applyContrastSensitiveStyleToElement( context, 'tooltipButton', volumeButtonLabel ) volumeButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() context.service.send({ type: 'VIEW_MODE_CHANGED', data: 'Volume' }) }) mainRow.appendChild(volumeButton) } export default createViewModeButtons ================================================ FILE: src/UI/reference-ui/src/Main/createViewPlanesToggle.js ================================================ import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import { viewPlanesIconDataUri } from '@itk-viewer/icons' function createViewPlanesToggle(context, volumeRow) { const viewerDOMId = context.id const viewPlanesButton = document.createElement('div') viewPlanesButton.innerHTML = `` const viewPlanesButtonInput = viewPlanesButton.children[0] const viewPlanesButtonLabel = viewPlanesButton.children[1] context.main.viewPlanesButton = viewPlanesButton context.main.viewPlanesButtonLabel = viewPlanesButtonLabel context.main.viewPlanesButtonInput = viewPlanesButtonInput applyContrastSensitiveStyleToElement( context, 'tooltipButton', viewPlanesButtonLabel ) viewPlanesButton.addEventListener('change', event => { event.preventDefault() event.stopPropagation() const slicingPlanes = context.main.slicingPlanes if ( !slicingPlanes.x.visibile && !slicingPlanes.y.visible && !slicingPlanes.z.visible ) { slicingPlanes.x.visible = true slicingPlanes.y.visible = true slicingPlanes.z.visible = true context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) } else { slicingPlanes.x.visible = false slicingPlanes.y.visible = false slicingPlanes.z.visible = false context.service.send({ type: 'SLICING_PLANES_CHANGED', data: slicingPlanes, }) } }) volumeRow.appendChild(viewPlanesButton) } export default createViewPlanesToggle ================================================ FILE: src/UI/reference-ui/src/Main/fullscreenMethods.js ================================================ const fullscreenMethods = [] window.addEventListener('load', () => { const body = document.querySelector('body') // https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API ;[ ['requestFullscreen', 'exitFullscreen', 'fullscreenchange', 'fullscreen'], [ 'mozRequestFullScreen', 'mozCancelFullScreen', 'mozfullscreenchange', 'mozFullScreen', ], [ 'msRequestFullscreen', 'msExitFullscreen', 'MSFullscreenChange', 'msFullscreenEnabled', ], [ 'webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitfullscreenchange', 'webkitIsFullScreen', ], ].forEach(methods => { if (body[methods[0]] && fullscreenMethods.length === 0) { fullscreenMethods.splice(methods, methods.length, ...methods) } }) }) export default fullscreenMethods ================================================ FILE: src/UI/reference-ui/src/Main/mainUIMachineOptions.js ================================================ import createMainInterface from './createMainInterface' import toggleFullscreen from './toggleFullscreen' import toggleRotate from './toggleRotate' import toggleAnnotations from './toggleAnnotations' import toggleAxes from './toggleAxes' import toggleBackgroundColor from './toggleBackgroundColor' import toggleCroppingPlanes from './toggleCroppingPlanes' import viewModeXPlane from './viewModeXPlane' import viewModeYPlane from './viewModeYPlane' import viewModeZPlane from './viewModeZPlane' import viewModeVolume from './viewModeVolume' import applySlicingPlanes from './applySlicingPlanes' import applyXSlice from './applyXSlice' import applyYSlice from './applyYSlice' import applyZSlice from './applyZSlice' const mainUIMachineOptions = { actions: { createMainInterface, toggleAnnotations, toggleFullscreen, toggleRotate, toggleAxes, toggleBackgroundColor, toggleCroppingPlanes, viewModeXPlane, viewModeYPlane, viewModeZPlane, viewModeVolume, applySlicingPlanes, applyXSlice, applyYSlice, applyZSlice, }, } export default mainUIMachineOptions ================================================ FILE: src/UI/reference-ui/src/Main/resetCrop.js ================================================ ================================================ FILE: src/UI/reference-ui/src/Main/toggleAnnotations.js ================================================ function toggleAnnotations(context) { if (context.main.annotationsButtonInput) { context.main.annotationsButtonInput.checked = context.main.annotationsEnabled } } export default toggleAnnotations ================================================ FILE: src/UI/reference-ui/src/Main/toggleAxes.js ================================================ function toggleAxes(context) { if (context.main.axesButtonInput) { context.main.axesButtonInput.checked = context.main.axesEnabled } } export default toggleAxes ================================================ FILE: src/UI/reference-ui/src/Main/toggleBackgroundColor.js ================================================ function toggleBackgroundColor(context) { context.main.selectedBackgroundColor = (context.main.selectedBackgroundColor + 1) % context.main.backgroundColors.length context.main.backgroundColor = context.main.backgroundColors[context.main.selectedBackgroundColor] } export default toggleBackgroundColor ================================================ FILE: src/UI/reference-ui/src/Main/toggleCrop.js ================================================ ================================================ FILE: src/UI/reference-ui/src/Main/toggleCroppingPlanes.js ================================================ function toggleCroppingPlanes(context) { if (context.main.cropButtonInput) { context.main.cropButtonInput.checked = context.main.croppingPlanesEnabled } } export default toggleCroppingPlanes ================================================ FILE: src/UI/reference-ui/src/Main/toggleFullscreen.js ================================================ import fullscreenMethods from './fullscreenMethods' function toggleFullscreen(context, event, actionMeta) { const fullscreenEnabled = context.main.fullscreenEnabled const fullscreenButtonInput = context.main.fullscreenButton.children[0] fullscreenButtonInput.checked = fullscreenEnabled // Triggered by operating system events, e.g. pressing Esc while in // Fullscreen or F11 when not in fullscreen if (fullscreenEnabled === document[fullscreenMethods[3]]) { return } const container = context.rootContainer const oldWidth = context.main.rootContainerOldWidth const oldHeight = context.main.rootContainerOldHeight if (fullscreenEnabled) { context.main.rootContainerOldWidth = container.style.width context.main.rootContainerOldHeight = container.style.height container.style.width = '100vw' container.style.height = '100vh' context.rootContainer[fullscreenMethods[0]]() } else { container.style.width = oldWidth container.style.height = oldHeight document[fullscreenMethods[1]]() } } export default toggleFullscreen ================================================ FILE: src/UI/reference-ui/src/Main/toggleRotate.js ================================================ function toggleRotate(context) { if (context.main.rotateButtonInput) { context.main.rotateButtonInput.checked = context.main.rotateEnabled } } export default toggleRotate ================================================ FILE: src/UI/reference-ui/src/Main/viewModeVolume.js ================================================ function viewModeVolume(context) { const main = context.main if (main.xPlaneButton) { main.xPlaneButton.checked = false } if (main.yPlaneButton) { main.yPlaneButton.checked = false } if (main.zPlaneButton) { main.zPlaneButton.checked = false } if (main.volumeButton) { main.volumeButton.checked = true } if (!main.planeUIGroup) { return } const slicingPlanes = main.slicingPlanes main.xPlaneVisibleButton.style.display = slicingPlanes.x.visible ? 'flex' : 'none' main.xPlaneInvisibleButton.style.display = slicingPlanes.x.visible ? 'none' : 'flex' main.yPlaneVisibleButton.style.display = slicingPlanes.y.visible ? 'flex' : 'none' main.yPlaneInvisibleButton.style.display = slicingPlanes.y.visible ? 'none' : 'flex' main.zPlaneVisibleButton.style.display = slicingPlanes.z.visible ? 'flex' : 'none' main.zPlaneInvisibleButton.style.display = slicingPlanes.z.visible ? 'none' : 'flex' if (context.uiCollapsed) { main.planeUIGroup.style.display = 'none' } else { main.planeUIGroup.style.display = 'block' main.xPlaneRow.style.display = 'flex' main.yPlaneRow.style.display = 'flex' main.zPlaneRow.style.display = 'flex' } } export default viewModeVolume ================================================ FILE: src/UI/reference-ui/src/Main/viewModeXPlane.js ================================================ function viewModeXPlane(context) { const main = context.main if (main.xPlaneButton) { main.xPlaneButton.checked = true } if (main.yPlaneButton) { main.yPlaneButton.checked = false } if (main.zPlaneButton) { main.zPlaneButton.checked = false } if (main.volumeButton) { main.volumeButton.checked = false } if (!main.planeUIGroup) { return } if (!context.use2D) { main.xPlaneVisibleButton.style.display = 'none' main.xPlaneInvisibleButton.style.display = 'none' main.yPlaneVisibleButton.style.display = 'none' main.yPlaneInvisibleButton.style.display = 'none' main.zPlaneVisibleButton.style.display = 'none' main.zPlaneInvisibleButton.style.display = 'none' } main.planeUIGroup.style.display = 'block' main.xPlaneRow.style.display = 'flex' main.yPlaneRow.style.display = 'none' main.zPlaneRow.style.display = 'none' } export default viewModeXPlane ================================================ FILE: src/UI/reference-ui/src/Main/viewModeYPlane.js ================================================ function viewModeYPlane(context) { const main = context.main if (main.xPlaneButton) { main.xPlaneButton.checked = false } if (main.yPlaneButton) { main.yPlaneButton.checked = true } if (main.zPlaneButton) { main.zPlaneButton.checked = false } if (main.volumeButton) { main.volumeButton.checked = false } if (!main.planeUIGroup) { return } if (!context.use2D) { main.xPlaneVisibleButton.style.display = 'none' main.xPlaneInvisibleButton.style.display = 'none' main.yPlaneVisibleButton.style.display = 'none' main.yPlaneInvisibleButton.style.display = 'none' main.zPlaneVisibleButton.style.display = 'none' main.zPlaneInvisibleButton.style.display = 'none' } main.planeUIGroup.style.display = 'block' main.xPlaneRow.style.display = 'none' main.yPlaneRow.style.display = 'flex' main.zPlaneRow.style.display = 'none' } export default viewModeYPlane ================================================ FILE: src/UI/reference-ui/src/Main/viewModeZPlane.js ================================================ function viewModeZPlane(context) { const main = context.main if (main.xPlaneButton) { main.xPlaneButton.checked = false } if (main.yPlaneButton) { main.yPlaneButton.checked = false } if (main.zPlaneButton) { main.zPlaneButton.checked = true } if (main.volumeButton) { main.volumeButton.checked = false } if (!main.planeUIGroup) { return } if (!context.use2D) { main.xPlaneVisibleButton.style.display = 'none' main.xPlaneInvisibleButton.style.display = 'none' main.yPlaneVisibleButton.style.display = 'none' main.yPlaneInvisibleButton.style.display = 'none' main.zPlaneVisibleButton.style.display = 'none' main.zPlaneInvisibleButton.style.display = 'none' } main.planeUIGroup.style.display = 'block' main.xPlaneRow.style.display = 'none' main.yPlaneRow.style.display = 'none' main.zPlaneRow.style.display = 'flex' } export default viewModeZPlane ================================================ FILE: src/UI/reference-ui/src/Widgets/applyDistanceWidgetValue.js ================================================ function applyDistanceWidgetValue(context, event) { context.widgets.distanceValueElement.setAttribute('value', `${event.data}`) } export default applyDistanceWidgetValue ================================================ FILE: src/UI/reference-ui/src/Widgets/applyWidgetsContrastSensitiveStyle.js ================================================ import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' function applyMainContrastSensitiveStyle(context) { applyContrastSensitiveStyleToElement( context, 'invertibleButton', context.widgets.distanceButtonLabel ) applyContrastSensitiveStyleToElement( context, 'distanceLabel', context.widgets.distanceLabel ) } export default applyMainContrastSensitiveStyle ================================================ FILE: src/UI/reference-ui/src/Widgets/createDistanceWidget.js ================================================ import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyleToElement from '../applyContrastSensitiveStyleToElement' import { lengthToolIconDataUri } from '@itk-viewer/icons' function createDistanceWidget(context, widgetsUIGroup) { const viewerDOMId = context.id // Put distance tools in their own row const distanceRulerRow = document.createElement('div') distanceRulerRow.setAttribute('class', style.uiRow) distanceRulerRow.style.display = context.use2D ? 'flex' : 'none' if (context.main.viewMode === 'Volume' && !context.use2D) { distanceRulerRow.style.display = 'none' } else { distanceRulerRow.style.display = 'flex' } const distanceEntry = document.createElement('div') distanceEntry.setAttribute('class', style.distanceEntry) const distanceButton = document.createElement('span') distanceButton.innerHTML = `` context.widgets.distanceButtonInput = distanceButton.children[0] context.widgets.distanceButtonInput.checked = context.widgets.distanceEnabled const distanceButtonLabel = distanceButton.children[1] context.widgets.distanceButtonLabel = distanceButtonLabel applyContrastSensitiveStyleToElement( context, 'invertibleButton', distanceButtonLabel ) distanceButton.addEventListener('change', event => { event.preventDefault() event.stopPropagation() context.service.send('TOGGLE_DISTANCE_WIDGET') }) distanceEntry.appendChild(distanceButton) const distanceLabel = document.createElement('label') distanceLabel.setAttribute('class', `${style.distanceLabelCommon}`) context.widgets.distanceLabel = distanceLabel applyContrastSensitiveStyleToElement(context, 'distanceLabel', distanceLabel) distanceLabel.setAttribute('for', `${viewerDOMId}-distanceValue`) distanceLabel.id = `${viewerDOMId}-distanceLabel` distanceLabel.innerText = 'Length:' distanceEntry.appendChild(distanceLabel) const distanceValue = document.createElement('input') distanceValue.type = 'text' distanceValue.setAttribute('class', style.distanceInput) distanceValue.id = `${viewerDOMId}-distanceValue` distanceValue.setAttribute('name', 'length') distanceValue.setAttribute('value', context.widgets.distanceValue.toString()) distanceValue.setAttribute('disabled', true) context.widgets.distanceValueElement = distanceValue distanceEntry.appendChild(distanceValue) context.widgets.distanceRulerRow = distanceRulerRow distanceRulerRow.appendChild(distanceEntry) widgetsUIGroup.appendChild(distanceRulerRow) } export default createDistanceWidget ================================================ FILE: src/UI/reference-ui/src/Widgets/createWidgetsInterface.js ================================================ import style from '../ItkVtkViewer.module.css' import createDistanceWidget from './createDistanceWidget' function createWidgetsInterface(context) { const widgetsUIGroup = document.createElement('div') widgetsUIGroup.setAttribute('class', style.uiGroup) context.widgets.widgetsUIGroup = widgetsUIGroup context.uiGroups.set('widgets', widgetsUIGroup) createDistanceWidget(context, widgetsUIGroup) context.uiContainer.appendChild(widgetsUIGroup) } export default createWidgetsInterface ================================================ FILE: src/UI/reference-ui/src/Widgets/toggleDistanceWidget.js ================================================ function toggleDistanceWidget(context) { context.widgets.distanceButtonInput.checked = context.widgets.distanceEnabled } export default toggleDistanceWidget ================================================ FILE: src/UI/reference-ui/src/Widgets/viewModeVolume.js ================================================ function viewModeVolume(context) { // Disable if in volume mode if (context.widgets.distanceEnabled) { context.service.send('TOGGLE_DISTANCE_WIDGET') } context.widgets.distanceRulerRow.style.display = 'none' } export default viewModeVolume ================================================ FILE: src/UI/reference-ui/src/Widgets/viewModeXPlane.js ================================================ function viewModeXPlane(context) { context.widgets.distanceRulerRow.style.display = 'flex' } export default viewModeXPlane ================================================ FILE: src/UI/reference-ui/src/Widgets/viewModeYPlane.js ================================================ function viewModeYPlane(context) { context.widgets.distanceRulerRow.style.display = 'flex' } export default viewModeYPlane ================================================ FILE: src/UI/reference-ui/src/Widgets/viewModeZPlane.js ================================================ function viewModeZPlane(context) { context.widgets.distanceRulerRow.style.display = 'flex' } export default viewModeZPlane ================================================ FILE: src/UI/reference-ui/src/Widgets/widgetsUIMachineOptions.js ================================================ import createWidgetsInterface from './createWidgetsInterface' import viewModeXPlane from './viewModeXPlane' import viewModeYPlane from './viewModeYPlane' import viewModeZPlane from './viewModeZPlane' import viewModeVolume from './viewModeVolume' import toggleDistanceWidget from './toggleDistanceWidget' import applyDistanceWidgetValue from './applyDistanceWidgetValue' const widgetsUIMachineOptions = { actions: { createWidgetsInterface, viewModeXPlane, viewModeYPlane, viewModeZPlane, viewModeVolume, toggleDistanceWidget, applyDistanceWidgetValue, }, } export default widgetsUIMachineOptions ================================================ FILE: src/UI/reference-ui/src/applyCategoricalColorToColorTransferFunction.js ================================================ import { CategoricalColors } from 'itk-viewer-color-maps' function applyCategoricalColorToColorTransferFunction( colorTransferFunction, labels, presetName ) { const numberOfLabels = labels.length if (!CategoricalColors.has(presetName)) { console.error( `Categorical color ${presetName} requested but it is not available` ) } const colors = CategoricalColors.get(presetName).IndexedColors const rgbPoints = new Array(numberOfLabels) // Assume background const haveBackground = labels[0] === 0 ? true : false let startIndex = 0 if (haveBackground) { startIndex = 1 rgbPoints[0] = [labels[0], 0.0, 0.0, 0.0, 0.5, 1.0] } for (let labelIndex = startIndex; labelIndex < numberOfLabels; labelIndex++) { const index = (labelIndex - startIndex) * 3 const color = colors.slice(index, index + 3) rgbPoints[labelIndex] = [ labels[labelIndex], color[0], color[1], color[2], 0.5, 1.0, ] } colorTransferFunction.removeAllPoints() rgbPoints.forEach(point => { colorTransferFunction.addRGBPointLong(...point) }) colorTransferFunction.setMappingRange(labels[0], labels[numberOfLabels - 1]) } export default applyCategoricalColorToColorTransferFunction ================================================ FILE: src/UI/reference-ui/src/applyContrastSensitiveStyleToElement.js ================================================ import style from './ItkVtkViewer.module.css' function applyContrastSensitiveStyleToElement(context, cssClass, element) { if (element) { const uiDarkMode = context.uiDarkMode const addPostFix = uiDarkMode ? 'DarkBG' : 'BrightBG' const removePostFix = !uiDarkMode ? 'DarkBG' : 'BrightBG' const removeClass = style[`${cssClass}${removePostFix}`] if (element.classList.contains(removeClass)) { element.classList.remove(removeClass) } element.classList.add(style[`${cssClass}${addPostFix}`]) } } export default applyContrastSensitiveStyleToElement ================================================ FILE: src/UI/reference-ui/src/applyGroupVisibility.js ================================================ function applyGroupVisibility(context, groupNames, visible) { for (let idx = 0; idx < groupNames.length; idx++) { if (!context.uiGroups.has(groupNames[idx])) { continue } const uiGroup = context.uiGroups.get(groupNames[idx]) if (visible) { uiGroup.style.display = 'block' } else { uiGroup.style.display = 'none' } } } export default applyGroupVisibility ================================================ FILE: src/UI/reference-ui/src/collapse-ui.ts ================================================ import { LitElement, html, css } from 'lit' import { customElement } from 'lit/decorators.js' import '@material/web/iconbutton/icon-button.js' import '@material/web/icon/icon.js' import { toggleIconDataUri } from '@itk-viewer/icons' import { connectState } from 'xstate-lit/dist/select-state' import { viewerContext } from './context' @customElement('collapse-ui') class CollapseUi extends LitElement { service = connectState( viewerContext, this, (state: any) => state.context.service ) static styles = css` .icon { width: 100%; } ` render() { return html` toggle ` } toggleUi() { this.service.value.send('TOGGLE_UI_COLLAPSED') } } ================================================ FILE: src/UI/reference-ui/src/context.ts ================================================ // "Inject" XState context into components with DOM events import { createContext } from '@lit-labs/context' export type ViewerContext = { service: any } export const viewerContext = createContext('viewer-context') type AppContext = any export let appContext: AppContext export const setContext = (context: AppContext) => { appContext = context } ================================================ FILE: src/UI/reference-ui/src/createCategoricalColorIconSelector.js ================================================ import { IconSelect } from '@thewtex/iconselect.js/lib/control/iconselect' import { CategoricalColorIcons } from 'itk-viewer-color-maps' function createCategoricalColorIconSelector(categoricalColorSelectorDiv) { const rows = 4 const cols = 2 const iconSelectParameters = { selectedIconWidth: 140, selectedIconHeight: 22, selectedBoxPadding: 1, iconsWidth: 60, iconsHeight: 22, boxIconSpace: 1, vectoralIconNumber: cols, horizontalIconNumber: rows, } const iconSelect = new IconSelect( `${categoricalColorSelectorDiv.id}`, categoricalColorSelectorDiv, iconSelectParameters ) categoricalColorSelectorDiv.style.width = '154px' const icons = new Array(rows * cols) let count = 0 for (let [key, value] of CategoricalColorIcons.entries()) { const index = Math.floor(count % rows) * cols + Math.floor(count / rows) icons[index] = { iconFilePath: value, iconValue: key } count++ } iconSelect.refresh(icons) return iconSelect } export default createCategoricalColorIconSelector ================================================ FILE: src/UI/reference-ui/src/createColorMapIconSelector.js ================================================ import { IconSelect } from '@thewtex/iconselect.js/lib/control/iconselect' import { ColorMapIcons, CategoricalColorIcons } from 'itk-viewer-color-maps' function createColorMapIconSelector(colorMapSelectorDiv) { const rows = 20 const cols = 4 const iconSelectParameters = { selectedIconWidth: 140, selectedIconHeight: 22, selectedBoxPadding: 1, iconsWidth: 80, iconsHeight: 22, boxIconSpace: 1, vectoralIconNumber: cols, horizontalIconNumber: rows, } const iconSelect = new IconSelect( `${colorMapSelectorDiv.id}`, colorMapSelectorDiv, iconSelectParameters ) colorMapSelectorDiv.style.width = '154px' // put above lower down label map color selector colorMapSelectorDiv.style.zIndex = '2001' const filteredIcons = new Map( Array.from(ColorMapIcons.entries()).concat( Array.from(CategoricalColorIcons.entries()).filter( ([name]) => !name.startsWith('modulate') ) ) ) const icons = new Array(Math.min(rows * cols, filteredIcons.size)) let count = 0 for (let [key, value] of filteredIcons.entries()) { const index = Math.floor(count % rows) * cols + Math.floor(count / rows) icons[index] = { iconFilePath: value, iconValue: key } count++ } iconSelect.refresh(icons) // keeps popout from getting clipped outside of sidebar width const box = colorMapSelectorDiv.querySelector('.icon-select .box') box.style.left = '-87px' box.style.top = '100%' box.style.width = '333px' // avoids asymmetric whitespace on right side of popout return iconSelect } export default createColorMapIconSelector ================================================ FILE: src/UI/reference-ui/src/createInterface.js ================================================ import style from './ItkVtkViewer.module.css' import '@material/web/labs/navigationdrawer/navigation-drawer.js' import './serviceContext' import './collapse-ui' import { setContext } from './context' import { makeHtml } from './utils' import { updateDrawer } from './toggleUICollapsed' function createInterface(context) { setContext(context) context.viewContainers = new Map() const viewContainer = document.createElement('div') viewContainer.className = `${style.viewContainer}` context.viewContainers.set('volume', viewContainer) const serviceContextProvider = document.createElement('service-context') serviceContextProvider.appendChild(viewContainer) context.rootContainer.appendChild(serviceContextProvider) const viewport = document.createElement('div') viewContainer.appendChild(viewport) viewport.setAttribute('class', style.viewport) const container3d = context.renderingViewContainers.get('volume') viewport.appendChild(container3d) container3d.style.height = '100%' // if somehow already set (by non reference-ui from config obj?) if (!context.uiContainer) { context.uiContainer = document.createElement('div') } const sidebar = makeHtml(`
`) viewport.appendChild(sidebar) const drawer = sidebar.querySelector('#drawer') context.drawer = drawer drawer.appendChild(context.uiContainer) context.uiContainer.style.width = 'var(--_container-width)' setTimeout(() => { // hack to keep scroll bar from squishing uiContainer, because uiContainer width does not get reduces with scroll bar. drawer.shadowRoot.children[0].style.overflow = 'visible' // sets hacked width of drawer based on context.uiCollapsed updateDrawer(context) }, 0) if (!context.uiGroups) { // String to UI group element context.uiGroups = new Map() } } export default createInterface ================================================ FILE: src/UI/reference-ui/src/referenceUIMachineOptions.js ================================================ import mainUIMachineOptions from './Main/mainUIMachineOptions' import layersUIMachineOptions from './Layers/layersUIMachineOptions' import imagesUIMachineOptions from './Images/imagesUIMachineOptions' import widgetsUIMachineOptions from './Widgets/widgetsUIMachineOptions' import toggleDarkMode from './toggleDarkMode' import createInterface from './createInterface' import toggleUICollapsed from './toggleUICollapsed' const referenceUIMachineOptions = { main: mainUIMachineOptions, layers: layersUIMachineOptions, images: imagesUIMachineOptions, widgets: widgetsUIMachineOptions, actions: { toggleDarkMode, createInterface, toggleUICollapsed, }, } export default referenceUIMachineOptions ================================================ FILE: src/UI/reference-ui/src/serviceContext.ts ================================================ import { LitElement, html, css } from 'lit' import { customElement } from 'lit/decorators.js' import { ContextProvider } from '@lit-labs/context' import { appContext, viewerContext } from './context' @customElement('service-context') export class ServiceContext extends LitElement { // @ts-ignore private provider = new ContextProvider(this, viewerContext, { service: appContext.service, }) render() { return html` ` } } declare global { interface HTMLElementTagNameMap { 'service-context': ServiceContext } } ================================================ FILE: src/UI/reference-ui/src/shims.d.ts ================================================ declare module '*' ================================================ FILE: src/UI/reference-ui/src/toggleDarkMode.js ================================================ import applyMainContrastSensitiveStyle from './Main/applyMainContrastSensitiveStyle' import applyLayersContrastSensitiveStyle from './Layers/applyLayersContrastSensitiveStyle' import applyImagesContrastSensitiveStyle from './Images/applyImagesContrastSensitiveStyle' import applyWidgetsContrastSensitiveStyle from './Widgets/applyWidgetsContrastSensitiveStyle' function toggleDarkMode(context) { applyMainContrastSensitiveStyle(context) applyLayersContrastSensitiveStyle(context) applyImagesContrastSensitiveStyle(context) applyWidgetsContrastSensitiveStyle(context) } export default toggleDarkMode ================================================ FILE: src/UI/reference-ui/src/toggleUICollapsed.js ================================================ export function updateDrawer(context) { context.drawer.opened = !context.uiCollapsed const drawerChild = context.drawer.shadowRoot.children[0] if (drawerChild) drawerChild.style.width = context.drawer.opened ? 'var(--_container-width)' : '' } function toggleUICollapsed(context, event, actionMeta) { if (!context.uiContainer) { return } if (actionMeta) { context.uiCollapsed = actionMeta.state.value.active.uiCollapsed === 'enabled' } updateDrawer(context) if (!context.uiCollapsed && context.images.selectedName) { context.service.send({ type: 'SELECT_LAYER', data: context.images.selectedName, }) } if (!context.use2D && !!context.main.planeUIGroup) { if (context.uiCollapsed && context.main.viewMode === 'Volume') { context.main.planeUIGroup.style.display = 'none' } else { context.main.planeUIGroup.style.display = 'block' } } } export default toggleUICollapsed ================================================ FILE: src/UI/reference-ui/src/utils.js ================================================ export const makeHtml = htmlString => { const template = document.createElement('template') template.innerHTML = htmlString return template.content.firstElementChild } ================================================ FILE: src/UI/reference-ui/tsconfig.json ================================================ { "compilerOptions": { "outDir": "dist", "target": "es2019", "module": "es2020", "moduleResolution": "node", "lib": ["es2019", "dom"], "declaration": true, "declarationMap": true, "experimentalDecorators": true, "useDefineForClassFields": false, "strict": true }, "include": ["src/**/*"], "exclude": ["node_modules", "**/*.spec.ts"] } ================================================ FILE: src/UI/styleRenderingViewContainers.js ================================================ function applyStyle(el, style) { Object.keys(style).forEach(key => { el.style[key] = style[key] }) } function styleContainer(context, event) { if (event.data) { context.renderingViewContainerStyle = event.data } for (let container of context.renderingViewContainers.values()) { applyStyle(container, context.renderingViewContainerStyle) } } export default styleContainer ================================================ FILE: src/UserInterface/CategoricalPresetNames.js ================================================ const CategoricalPresetNames = [ 'glasbey', 'glasbey_light', 'glasbey_warm', 'modulate', 'glasbey_bw', 'glasbey_dark', 'glasbey_cool', 'modulate_dark', ] export default CategoricalPresetNames ================================================ FILE: src/UserInterface/Geometries/createGeometryColorBySelector.js ================================================ import { reaction } from 'mobx' import style from '../ItkVtkViewer.module.css' import { ColorMode, ScalarMode, } from 'vtk.js/Sources/Rendering/Core/Mapper/Constants' function createGeometryColorBySelector(store, colorByRow) { const colorBySelector = document.createElement('select') colorBySelector.setAttribute('class', style.selector) colorBySelector.id = `${store.id}-colorBySelector` reaction( () => { return store.geometriesUI.geometries.slice() }, geometries => { if (!!!geometries || geometries.length === 0) { return } const hasScalars = store.geometriesUI.hasScalars const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex const colorByOptions = store.geometriesUI.colorByOptions if ( store.geometriesUI.hasScalars[selectedGeometryIndex] && colorByOptions[selectedGeometryIndex].length > 1 ) { colorByRow.style.display = 'flex' } else { colorByRow.style.display = 'none' } const colorByDefault = store.geometriesUI.colorByDefault geometries.forEach((geometry, index) => { if (store.geometriesUI.colorBy.length <= index) { store.geometriesUI.colorBy.push(colorByDefault[index]) } else { const current = store.geometriesUI.colorBy[index] if ( !!store.geometriesUI.colorByOptions[index] && !!!store.geometriesUI.colorByOptions[index].filter(option => { return ( option.label === current.label && option.value === current.value ) }).length ) { store.geometriesUI.colorBy[index] = colorByDefault[index] } } }) if (hasScalars[selectedGeometryIndex]) { colorBySelector.value = store.geometriesUI.colorBy[selectedGeometryIndex].value } } ) reaction( () => { return store.geometriesUI.selectedGeometryIndex }, selectedGeometryIndex => { const colorByOptions = store.geometriesUI.colorByOptions if ( !!colorByOptions[selectedGeometryIndex] && !!colorByOptions[selectedGeometryIndex].length ) { colorBySelector.innerHTML = colorByOptions[selectedGeometryIndex] .map( ({ label, value }) => `` ) .join('') colorBySelector.value = store.geometriesUI.colorBy[selectedGeometryIndex].value } const hasScalars = store.geometriesUI.hasScalars if ( hasScalars[selectedGeometryIndex] && colorByOptions[selectedGeometryIndex].length > 1 ) { colorByRow.style.display = 'flex' } else { colorByRow.style.display = 'none' } } ) reaction( () => { return store.geometriesUI.colorBy.slice() }, colorBy => { const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex if (!!!colorBy[selectedGeometryIndex]) { return } const [location, colorByArrayName] = colorBy[ selectedGeometryIndex ].value.split(':') const proxy = store.geometriesUI.representationProxies[selectedGeometryIndex] const interpolateScalarsBeforeMapping = location === 'pointData' proxy.setInterpolateScalarsBeforeMapping(interpolateScalarsBeforeMapping) proxy.setColorBy(colorByArrayName, location) proxy.getMapper().setColorModeToDefault() store.renderWindow.render() const hasScalars = store.geometriesUI.hasScalars if (hasScalars[selectedGeometryIndex]) { colorBySelector.value = store.geometriesUI.colorBy[selectedGeometryIndex].value } } ) colorBySelector.addEventListener('change', event => { event.preventDefault() event.stopPropagation() const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex const colorByOptions = store.geometriesUI.colorByOptions const selectedOption = store.geometriesUI.colorByOptions[ selectedGeometryIndex ].filter(option => { return option.value === event.target.value })[0] store.geometriesUI.colorBy[selectedGeometryIndex] = selectedOption }) // Initialize coloring const colorByDefault = store.geometriesUI.colorByDefault colorByDefault.forEach((colorBy, index) => { if (colorBy) { const [location, colorByArrayName] = colorBy.value.split(':') const proxy = store.geometriesUI.representationProxies[index] const interpolateScalarsBeforeMapping = location === 'pointData' proxy.setInterpolateScalarsBeforeMapping(interpolateScalarsBeforeMapping) proxy.setColorBy(colorByArrayName, location) proxy.getMapper().setColorModeToDefault() } }) const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex const colorByOptions = store.geometriesUI.colorByOptions if (colorByDefault[selectedGeometryIndex]) { colorBySelector.innerHTML = colorByOptions[selectedGeometryIndex] .map(({ label, value }) => ``) .join('') colorBySelector.value = colorByDefault[selectedGeometryIndex].value } if ( store.geometriesUI.hasScalars[selectedGeometryIndex] && colorByOptions[selectedGeometryIndex].length > 1 ) { colorByRow.style.display = 'flex' } else { colorByRow.style.display = 'none' } store.geometriesUI.colorBy = colorByDefault colorByRow.appendChild(colorBySelector) } export default createGeometryColorBySelector ================================================ FILE: src/UserInterface/Geometries/createGeometryColorChooser.js ================================================ import { reaction } from 'mobx' import style from '../ItkVtkViewer.module.css' import hex2rgb from '../hex2rgb' function createGeometryColorChooser(store, geometryColorRow) { const geometryColorInput = document.createElement('input') geometryColorInput.setAttribute('type', 'color') geometryColorInput.id = `${store.id}-geometryColorInput` const defaultGeometryColor = '#ff0000' reaction( () => { return store.geometriesUI.geometries.slice() }, geometries => { if (!!!geometries || geometries.length === 0) { return } const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex geometries.forEach((geometry, index) => { if (store.geometriesUI.colors.length <= index) { store.geometriesUI.colors.push(defaultGeometryColor) } }) geometryColorInput.value = store.geometriesUI.colors[selectedGeometryIndex] } ) reaction( () => { return store.geometriesUI.selectedGeometryIndex }, selectedGeometryIndex => { geometryColorInput.value = store.geometriesUI.colors[selectedGeometryIndex] if (store.geometriesUI.hasScalars[selectedGeometryIndex]) { geometryColorInput.style.display = 'none' } else { geometryColorInput.style.display = 'inline-block' } } ) function applyColors(colors) { colors.forEach((value, index) => { const rgb = hex2rgb(value) store.geometriesUI.representationProxies[index].setColor(rgb) }) store.renderWindow.render() const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex geometryColorInput.value = colors[selectedGeometryIndex] } reaction(() => { return store.geometriesUI.colors.slice() }, applyColors) geometryColorInput.addEventListener('input', event => { event.preventDefault() event.stopPropagation() const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex store.geometriesUI.colors[selectedGeometryIndex] = event.target.value }) const defaultGeometryColors = Array(store.geometriesUI.geometries.length) defaultGeometryColors.fill(defaultGeometryColor) geometryColorInput.value = defaultGeometryColor store.geometriesUI.colors = defaultGeometryColors applyColors(store.geometriesUI.colors) const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex if (store.geometriesUI.hasScalars[selectedGeometryIndex]) { geometryColorInput.style.display = 'none' } else { geometryColorInput.style.display = 'inline-block' } geometryColorRow.appendChild(geometryColorInput) } export default createGeometryColorChooser ================================================ FILE: src/UserInterface/Geometries/createGeometryColorRangeInput.js ================================================ import { reaction, observable, action, toJS } from 'mobx' import vtkLookupTableProxy from 'vtk.js/Sources/Proxy/Core/LookupTableProxy' import style from '../ItkVtkViewer.module.css' import createColorMapIconSelector from '../createColorMapIconSelector' import customColorMapIcon from '../customColorMapIcon' function createColorRangeInput(store, uiContainer) { const minimumInput = document.createElement('input') minimumInput.type = 'number' minimumInput.setAttribute('class', style.numberInput) const maximumInput = document.createElement('input') maximumInput.type = 'number' maximumInput.setAttribute('class', style.numberInput) function updateColorRangeInput() { const selectedIndex = store.geometriesUI.selectedGeometryIndex if ( !store.geometriesUI.hasScalars[selectedIndex] || store.geometriesUI.hasOnlyDirectColors[selectedIndex] ) { return } const colorByKey = store.geometriesUI.colorBy[selectedIndex].value const [location, colorByArrayName] = colorByKey.split(':') const geometry = store.geometriesUI.geometries[selectedIndex] const dataArray = location === 'pointData' ? geometry.getPointData().getArrayByName(colorByArrayName) : geometry.getCellData().getArrayByName(colorByArrayName) const range = dataArray.getRange() minimumInput.min = range[0] minimumInput.max = range[1] maximumInput.min = range[0] maximumInput.max = range[1] const data = dataArray.getData() if (data instanceof Float32Array || data instanceof Float64Array) { const step = (range[1] - range[0]) / 100.0 minimumInput.step = step maximumInput.step = step } const colorRange = store.geometriesUI.selectedColorRange minimumInput.value = colorRange[0] maximumInput.value = colorRange[1] } function addColorRangesReactions(index, colorRanges) { if (store.geometriesUI.colorRangesReactions.has(index)) { const disposer = store.geometriesUI.colorRangesReactions.get(index) disposer() } const disposer = reaction( () => { return toJS(store.geometriesUI.colorRanges) }, colorRanges => { if (index !== store.geometriesUI.selectedGeometryIndex) { return } const colorRange = store.geometriesUI.selectedColorRange if (!!colorRange) { minimumInput.value = colorRange[0] maximumInput.value = colorRange[1] const lutProxy = store.geometriesUI.selectedLookupTableProxy const colorTransferFunction = lutProxy.getLookupTable() colorTransferFunction.setMappingRange(...colorRange) colorTransferFunction.updateRange() if (!store.renderWindow.getInteractor().isAnimating()) { store.renderWindow.render() } } } ) store.geometriesUI.colorRangesReactions.set(index, disposer) } function setDefaultColorRangesColorMaps() { const colorByOptions = store.geometriesUI.colorByOptions if (!!!colorByOptions || colorByOptions.length === 0) { return } const geometries = store.geometriesUI.geometries colorByOptions.forEach((options, index) => { const geometry = geometries[index] if (!store.geometriesUI.colorRanges.has(index)) { const colorRanges = observable(new Map()) if (options) { options.forEach(option => { const [location, colorByArrayName] = option.value.split(':') const dataArray = location === 'pointData' ? geometry.getPointData().getArrayByName(colorByArrayName) : geometry.getCellData().getArrayByName(colorByArrayName) const range = dataArray.getRange() colorRanges.set(option.value, range) }) } store.geometriesUI.colorRanges.set(index, colorRanges) addColorRangesReactions(index, colorRanges) } else { // Constrain by min / max of possibly new inputs const colorRanges = store.geometriesUI.colorRanges.get(index) !!options && options.forEach(option => { const [location, colorByArrayName] = option.value.split(':') const dataArray = location === 'pointData' ? geometry.getPointData().getArrayByName(colorByArrayName) : geometry.getCellData().getArrayByName(colorByArrayName) const range = dataArray.getRange() if (colorRanges.has(option.value)) { const current = colorRanges.get(option.value) if (current[0] < range[0] || current[1] > range[1]) { const newRange = current.slice() if (current[0] < range[0]) { newRange[0] = range[0] } if (current[1] > range[1]) { newRange[1] = range[1] } colorRanges.set(option.value, newRange) } } else { colorRanges.set(option.value, range) } }) addColorRangesReactions(index, colorRanges) } if (store.geometriesUI.colorMaps.length <= index) { const defaultColorMap = 'Viridis (matplotlib)' store.geometriesUI.colorMaps.push(defaultColorMap) const lutProxy = store.geometriesUI.selectedLookupTableProxy if (!!lutProxy) { lutProxy.setPresetName(defaultColorMap) } } if (store.geometriesUI.colorMaps.length <= index) { const defaultColorMap = 'Viridis (matplotlib)' store.geometriesUI.colorMaps.push(defaultColorMap) const lutProxy = store.geometriesUI.selecetdLookupTableProxy if (!!lutProxy) { lutProxy.setPresetName(defaultColorMap) } } }) updateColorRangeInput() } setDefaultColorRangesColorMaps() reaction( () => { return store.geometriesUI.selectedColorRange }, colorRange => { if (!!colorRange) { minimumInput.value = colorRange[0] maximumInput.value = colorRange[1] const lutProxy = store.geometriesUI.selectedLookupTableProxy const colorTransferFunction = lutProxy.getLookupTable() colorTransferFunction.setMappingRange(...colorRange) colorTransferFunction.updateRange() } } ) minimumInput.addEventListener( 'change', action(event => { event.preventDefault() event.stopPropagation() const selectedIndex = store.geometriesUI.selectedGeometryIndex const colorByKey = store.geometriesUI.colorBy[selectedIndex].value const range = store.geometriesUI.colorRanges .get(selectedIndex) .get(colorByKey) range[0] = Number(event.target.value) store.geometriesUI.colorRanges.get(selectedIndex).set(colorByKey, range) }) ) maximumInput.addEventListener( 'change', action(event => { event.preventDefault() event.stopPropagation() const selectedIndex = store.geometriesUI.selectedGeometryIndex const colorByKey = store.geometriesUI.colorBy[selectedIndex].value const range = store.geometriesUI.colorRanges .get(selectedIndex) .get(colorByKey) range[1] = Number(event.target.value) store.geometriesUI.colorRanges.get(selectedIndex).set(colorByKey, range) }) ) const colorMapSelector = document.createElement('div') colorMapSelector.id = `${store.id}-geometryColorMapSelector` const iconSelector = createColorMapIconSelector(colorMapSelector) function updateColorCanvas() { const geometryIndex = store.geometriesUI.selectedGeometryIndex if ( !store.geometriesUI.hasScalars[selectedIndex] || store.geometriesUI.hasOnlyDirectColors[geometryIndex] ) { return } const colorMap = store.geometriesUI.colorMaps[geometryIndex] const lookupTableProxy = store.geometriesUI.selectedLookupTableProxy const colorTransferFunction = lookupTableProxy.getLookupTable() if (colorMap.startsWith('Custom')) { lookupTableProxy.setMode(vtkLookupTableProxy.Mode.RGBPoints) const colorRange = store.geometriesUI.selectedColorRange const isIcons = iconSelector.getIcons() if (!!!customIcon) { const colorMapIcon = customColorMapIcon( colorTransferFunction, colorDataRange ) customIcon = { iconFilePath: colorMapIcon, iconValue: colorMap } icons.push(customIcon) iconSelector.refresh(icons) } else if (isIcons[isIcons.length - 1].iconValue !== colorMap) { const colorMapIcon = customColorMapIcon( colorTransferFunction, colorDataRange ) isIcons[isIcons.length - 1].element.src = colorMapIcon isIcons[isIcons.length - 1].iconFilePath = colorMapIcon isIcons[isIcons.length - 1].iconValue = colorMap isIcons[isIcons.length - 1].element.setAttribute('icon-value', colorMap) isIcons[isIcons.length - 1].element.setAttribute('alt', colorMap) isIcons[isIcons.length - 1].element.setAttribute('title', colorMap) } } else { lookupTableProxy.setPresetName(colorMap) lookupTableProxy.setMode(vtkLookupTableProxy.Mode.Preset) } iconSelector.setSelectedValue(colorMap) if (!store.renderWindow.getInteractor().isAnimating()) { store.renderWindow.render() } } let customIcon = null reaction( () => { return store.geometriesUI.colorMaps.slice() }, colorMaps => { updateColorCanvas() } ) colorMapSelector.addEventListener('changed', event => { event.preventDefault() event.stopPropagation() const geometryIndex = store.geometriesUI.selectedGeometryIndex store.geometriesUI.colorMaps[ geometryIndex ] = iconSelector.getSelectedValue() }) const geometryIndex = store.geometriesUI.selectedGeometryIndex iconSelector.setSelectedValue(store.geometriesUI.colorMaps[geometryIndex]) reaction( () => { return store.geometriesUI.colorByOptions.slice() }, () => { setDefaultColorRangesColorMaps() } ) reaction( () => { return store.geometriesUI.selectedGeometryIndex }, selectedIndex => { const directColors = store.geometriesUI.hasOnlyDirectColors if (directColors[selectedIndex]) { uiContainer.style.display = 'flex' updateColorRangeInput() updateColorCanvas() } else { uiContainer.style.display = 'none' } } ) reaction( () => { return store.geometriesUI.colorBy.slice() }, () => { updateColorRangeInput() updateColorCanvas() } ) updateColorCanvas() const directColors = store.geometriesUI.hasOnlyDirectColors const selectedIndex = store.geometriesUI.selectedGeometryIndex if (directColors[selectedIndex]) { uiContainer.style.display = 'flex' } else { uiContainer.style.display = 'none' } uiContainer.appendChild(minimumInput) uiContainer.appendChild(colorMapSelector) uiContainer.appendChild(maximumInput) } export default createColorRangeInput ================================================ FILE: src/UserInterface/Geometries/createGeometryColorWidget.js ================================================ import style from '../ItkVtkViewer.module.css' import createGeometryColorChooser from './createGeometryColorChooser' import createGeometryOpacitySlider from './createGeometryOpacitySlider' import createGeometryColorBySelector from './createGeometryColorBySelector' import createGeometryColorRangeInput from './createGeometryColorRangeInput' function createGeometryColorWidget(store, geometriesUIGroup) { const colorByRow = document.createElement('div') colorByRow.setAttribute('class', style.uiRow) colorByRow.className += ` ${store.id}-collapsible` createGeometryColorBySelector(store, colorByRow) geometriesUIGroup.appendChild(colorByRow) const geometryColorRow = document.createElement('div') geometryColorRow.setAttribute('class', style.uiRow) geometryColorRow.className += ` ${store.id}-collapsible` createGeometryColorChooser(store, geometryColorRow) createGeometryOpacitySlider(store, geometryColorRow) geometriesUIGroup.appendChild(geometryColorRow) const colorRangeInputRow = document.createElement('div') colorRangeInputRow.setAttribute('class', style.uiRow) createGeometryColorRangeInput(store, colorRangeInputRow) colorRangeInputRow.className += ` ${store.id}-collapsible` geometriesUIGroup.appendChild(colorRangeInputRow) if (store.mainUI.collapsed) { colorByRow.style.display = 'none' geometryColorRow.style.display = 'none' geometriesUIGroup.style.display = 'none' } } export default createGeometryColorWidget ================================================ FILE: src/UserInterface/Geometries/createGeometryOpacitySlider.js ================================================ import { reaction, action } from 'mobx' import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyle from '../applyContrastSensitiveStyle' import opacityIcon from '../icons/opacity.svg' function createGeometryOpacitySlider(store, geometryColorRow) { const defaultGeometryOpacity = 1.0 const sliderEntry = document.createElement('div') sliderEntry.setAttribute('class', style.sliderEntry) sliderEntry.innerHTML = `
${opacityIcon}
` const opacityElement = sliderEntry.querySelector( `#${store.id}-geometryOpacitySlider` ) const sliderEntryDiv = sliderEntry.children[0] applyContrastSensitiveStyle(store, 'invertibleButton', sliderEntryDiv) reaction( () => { return store.geometriesUI.geometries.slice() }, geometries => { if (!!!geometries || geometries.length === 0) { return } geometries.forEach((geometry, index) => { if (store.geometriesUI.opacities.length <= index) { store.geometriesUI.opacities.push(defaultGeometryOpacity) } }) const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex opacityElement.value = store.geometriesUI.opacities[selectedGeometryIndex] } ) reaction( () => { return store.geometriesUI.selectedGeometryIndex }, selectedGeometryIndex => { opacityElement.value = store.geometriesUI.opacities[selectedGeometryIndex] } ) function applyOpacities(opacities) { opacities.forEach((opacity, index) => { store.geometriesUI.representationProxies[index].setOpacity(opacity) }) store.renderWindow.render() const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex opacityElement.value = opacities[selectedGeometryIndex] } reaction(() => { return store.geometriesUI.opacities.slice() }, applyOpacities) opacityElement.addEventListener( 'input', action(event => { event.preventDefault() event.stopPropagation() const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex store.geometriesUI.opacities[selectedGeometryIndex] = Number( event.target.value ) }) ) const defaultGeometryOpacities = new Array( store.geometriesUI.geometries.length ) defaultGeometryOpacities.fill(defaultGeometryOpacity) opacityElement.value = defaultGeometryOpacity store.geometriesUI.opacities = defaultGeometryOpacities applyOpacities(store.geometriesUI.opacities) geometryColorRow.appendChild(sliderEntry) } export default createGeometryOpacitySlider ================================================ FILE: src/UserInterface/Geometries/createGeometryRepresentationSelector.js ================================================ import { reaction } from 'mobx' import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyle from '../applyContrastSensitiveStyle' import hiddenIcon from '../icons/hidden.svg' import wireframeIcon from '../icons/geometry-wireframe.svg' import surfaceIcon from '../icons/geometry-surface.svg' import surfaceWithEdgesIcon from '../icons/geometry-surface-with-edges.svg' function createGeometryRepresentationSelector( store, geometryRepresentationRow ) { const viewerDOMId = store.id const geometryHiddenButton = document.createElement('div') geometryHiddenButton.innerHTML = `` geometryHiddenButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex store.geometriesUI.representations[selectedGeometryIndex] = 'Hidden' }) geometryRepresentationRow.appendChild(geometryHiddenButton) const geometryHiddenButtonInput = geometryHiddenButton.children[0] const geometryHiddenButtonLabel = geometryHiddenButton.children[1] applyContrastSensitiveStyle(store, 'tooltipButton', geometryHiddenButtonLabel) const geometryWireframeButton = document.createElement('div') geometryWireframeButton.innerHTML = `` geometryWireframeButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex store.geometriesUI.representations[selectedGeometryIndex] = 'Wireframe' }) geometryRepresentationRow.appendChild(geometryWireframeButton) const geometryWireframeButtonInput = geometryWireframeButton.children[0] const geometryWireframeButtonLabel = geometryWireframeButton.children[1] applyContrastSensitiveStyle( store, 'tooltipButton', geometryWireframeButtonLabel ) const geometrySurfaceButton = document.createElement('div') geometrySurfaceButton.innerHTML = `` geometrySurfaceButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex store.geometriesUI.representations[selectedGeometryIndex] = 'Surface' }) geometryRepresentationRow.appendChild(geometrySurfaceButton) const geometrySurfaceButtonInput = geometrySurfaceButton.children[0] const geometrySurfaceButtonLabel = geometrySurfaceButton.children[1] applyContrastSensitiveStyle( store, 'tooltipButton', geometrySurfaceButtonLabel ) const geometrySurfaceWithEdgesButton = document.createElement('div') geometrySurfaceWithEdgesButton.innerHTML = `` geometrySurfaceWithEdgesButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex store.geometriesUI.representations[selectedGeometryIndex] = 'Surface with edges' }) geometryRepresentationRow.appendChild(geometrySurfaceWithEdgesButton) const geometrySurfaceWithEdgesButtonInput = geometrySurfaceWithEdgesButton.children[0] const geometrySurfaceWithEdgesButtonLabel = geometrySurfaceWithEdgesButton.children[1] applyContrastSensitiveStyle( store, 'tooltipButton', geometrySurfaceWithEdgesButtonLabel ) function updateEnabledRepresentationButtons(selectedGeometryRepresentation) { switch (selectedGeometryRepresentation) { case 'Hidden': geometryHiddenButtonInput.checked = true geometryWireframeButtonInput.checked = false geometrySurfaceButtonInput.checked = false geometrySurfaceWithEdgesButtonInput.checked = false break case 'Wireframe': geometryHiddenButtonInput.checked = false geometryWireframeButtonInput.checked = true geometrySurfaceButtonInput.checked = false geometrySurfaceWithEdgesButtonInput.checked = false break case 'Surface': geometryHiddenButtonInput.checked = false geometryWireframeButtonInput.checked = false geometrySurfaceButtonInput.checked = true geometrySurfaceWithEdgesButtonInput.checked = false break case 'Surface with edges': geometryHiddenButtonInput.checked = false geometryWireframeButtonInput.checked = false geometrySurfaceButtonInput.checked = false geometrySurfaceWithEdgesButtonInput.checked = true break default: console.error( 'Invalid geometry representation: ' + selectedGeometryRepresentation ) } } function setRepresentation(value, index) { if (value === 'Hidden') { store.geometriesUI.representationProxies[index].setVisibility(false) } else { store.geometriesUI.representationProxies[index].setRepresentation(value) store.geometriesUI.representationProxies[index].setVisibility(true) } updateEnabledRepresentationButtons(value) store.renderWindow.render() } reaction( () => { return store.geometriesUI.representations.slice() }, representations => { const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex const representation = store.geometriesUI.representations[selectedGeometryIndex] setRepresentation(representation, selectedGeometryIndex) store.renderWindow.render() } ) reaction( () => { return store.geometriesUI.selectedGeometryIndex }, selectedIndex => { const selectedGeometryRepresentation = store.geometriesUI.representations[selectedIndex] updateEnabledRepresentationButtons(selectedGeometryRepresentation) } ) const defaultGeometryRepresentation = 'Surface' reaction( () => { return store.geometriesUI.geometries.slice() }, geometries => { if (!!!geometries || geometries.length === 0) { return } geometries.forEach((geometry, index) => { if (store.geometriesUI.representations.length <= index) { store.geometriesUI.representations.push(defaultGeometryRepresentation) } }) const selectedGeometryIndex = store.geometriesUI.selectedGeometryIndex updateEnabledRepresentationButtons( store.geometriesUI.representations[selectedGeometryIndex] ) } ) const defaultGeometryRepresentations = new Array( store.geometriesUI.geometries.length ) defaultGeometryRepresentations.fill(defaultGeometryRepresentation) updateEnabledRepresentationButtons(defaultGeometryRepresentation) store.geometriesUI.representations = defaultGeometryRepresentations } export default createGeometryRepresentationSelector ================================================ FILE: src/UserInterface/ItkVtkViewer.module.css ================================================ .loading { border: 16px solid #f3f3f3; /* Light grey */ border-top: 16px solid #3498db; /* Blue */ border-radius: 50%; width: 120px; height: 120px; position: absolute; left: calc(50% - 60px); top: calc(50% - 60px); animation: spin 2s linear infinite; box-sizing: border-box; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .uiContainer { display: flex; align-items: stretch; flex-direction: column; justify-content: space-between; position: absolute; top: 5px; left: 5px; padding: 2px; border: 0px; box-sizing: border-box; z-index: 1000; } .uiGroup { background: rgba(128, 128, 128, 0.5); border-radius: 4px; margin: 2px; } .uiRow { display: flex; flex-direction: row; flex: 1; align-items: center; justify-content: space-between; padding: 5px; } .mainUIRow { composes: uiRow; justify-content: space-around; max-width: 420px; } .progress { color: white; font-size: 200%; height: 100vh; width: 100vw; text-align: center; vertical-align: middle; line-height: 100vh; } .piecewiseWidget { flex: 1; background: rgba(255, 255, 255, 0.2); border-radius: 3px; z-index: 1000; } .logo { position: absolute; top: 5px; right: 5px; height: 2em; width: 2em; cursor: pointer; z-index: 100; } .fpsMonitor { position: absolute; top: 5px; right: 5px; border-radius: 5px; background: rgba(255, 255, 255, 0.6); cursor: pointer; z-index: 101; } [itk-vtk-tooltip] { position: relative; } [itk-vtk-tooltip]::before { content: attr(itk-vtk-tooltip-content); visibility: hidden; position: absolute; top: 50%; right: calc(100% + 16px); width: 400%; padding: 4px 6px; text-align: center; text-transform: none; font-size: 0.9em; font-family: monospace; border-radius: 3px; background: rgba(0.9, 0.9, 0.9, 0.95); color: white; opacity: 0; transform: translate(15px, -50%); transition-property: all; transition-duration: 0.3s; transition-timing-function: ease-in-out; transition-delay: 0.8s; z-index: 1; } [itk-vtk-tooltip]:hover::before { opacity: 1; visibility: visible; transform: translate(0, -50%); } [itk-vtk-tooltip-bottom]::before { top: calc(100% + 16px); left: 50%; right: initial; transform: translate(-50%, -15px); } [itk-vtk-tooltip-bottom]:hover::before { transform: translate(-50%, 0); } [itk-vtk-tooltip-right]::before { top: 50%; left: calc(100% + 16px); right: initial; transform: translate(-15px, -50%); } [itk-vtk-tooltip-right]:hover::before { transform: translate(0, -50%); } [itk-vtk-tooltip-top-screenshot]::before { top: initial; left: 260%; right: initial; bottom: calc(100% + 8px); transform: translate(-50%, 15px); } [itk-vtk-tooltip-top-screenshot]:hover::before { transform: translate(-50%, 0); } [itk-vtk-tooltip-top-annotation]::before { top: initial; left: 160%; right: initial; bottom: calc(100% + 10px); transform: translate(-50%, 15px); } [itk-vtk-tooltip-top-annotation]:hover::before { transform: translate(-50%, 0); } [itk-vtk-tooltip-top-axes]::before { top: initial; left: 160%; right: initial; bottom: calc(100% + 10px); transform: translate(-50%, 15px); } [itk-vtk-tooltip-top-axes]:hover::before { transform: translate(-50%, 0); } [itk-vtk-tooltip-top-fullscreen]::before { top: initial; left: 120%; right: initial; bottom: calc(100% + 10px); transform: translate(-50%, 15px); width: 400%; } [itk-vtk-tooltip-top-fullscreen]:hover::before { transform: translate(-50%, 0); } [itk-vtk-tooltip-top]::before { top: initial; left: 60%; right: initial; bottom: calc(100% + 10px); transform: translate(-50%, 15px); } [itk-vtk-tooltip-top]:hover::before { transform: translate(-50%, 0); } [itk-vtk-tooltip-top-fullscreen]::before { top: initial; left: 120%; right: initial; bottom: calc(100% + 10px); transform: translate(-50%, 15px); width: 400%; } .tooltipButtonBrightBG::before { } .tooltipButtonDarkBG::before { filter: invert(100%); -webkit-filter: invert(100%); } .invertibleButtonBrightBG { } .invertibleButtonDarkBG { filter: invert(100%); -webkit-filter: invert(100%); } .toggleUserInterfaceButton { height: 1.5em; width: 1.5em; cursor: pointer; z-index: 1000; } .screenshotButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .screenshotButton svg { height: 1.2em; width: 1.2em; } .annotationButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .annotationButton svg { height: 1.2em; width: 1.2em; } .axesButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .axesButton svg { height: 1.2em; width: 1.2em; } .fullscreenButton { flex: 1; width: 8m; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .fullscreenButton svg { height: 1.2em; width: 1.2em; } .interpolationButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .interpolationButton svg { height: 1.2em; width: 1.2em; } .cropButton { flex: 1; height: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .cropButton svg { height: 1.2em; width: 1.2em; } .resetCropButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .resetCropButton svg { height: 1.2em; width: 1.2em; } .distanceEntry { flex: 1; display: flex; flex-direction: row; align-items: self-start; } .distanceButton { flex: 1; height: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .distanceButton svg { height: 1.2em; width: 1.2em; } .distanceLabelCommon { border: none; background: transparent; font-size: 1.2em; margin-right: 10px; z-index: 1000; } .distanceLabelBrightBG { color: black; } .distanceLabelDarkBG { color: white; } .distanceInput { background: transparent; color: white; font-size: 1em; width: 80px; } .resetCameraButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .resetCameraButton svg { height: 1.2em; width: 1.2em; } .bgColorButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .bgColorButton svg { height: 1.2em; width: 1.2em; } .viewModeButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 6px; padding-right: 6px; cursor: pointer; z-index: 1000; } .viewModeButton svg { width: 1.3em; height: 1.3em; } .shadowButton { width: 8mm; padding: 4px; padding-left: 0px; cursor: pointer; z-index: 1000; } .shadowButton svg { width: 1.3em; height: 1.3em; } .viewPlanesButton { flex: 1; width: 8mm; padding-top: 2px; padding-bottom: 2px; padding-left: 0px; padding-right: 6px; cursor: pointer; z-index: 1000; } .viewPlanesButton svg { width: 1.3em; height: 1.3em; } .toggleInput { margin: 0px; width: 0%; opacity: 0; box-sizing: content-box; } .toggleButton { cursor: pointer; border-radius: 0.2em; opacity: 0.45; } input:checked.toggleInput + label { opacity: 1; } .numberInput { color: white; background: transparent; font-size: 1em; width: 80px; } .selector { display: flex; direction: row; font-size: 1.2em; z-index: 1000; } .componentTab { position: absolute; opacity: 0; pointer-events: none; } .componentDisabled { pointer-events: none; opacity: 0.5; } .componentTab + .compTabLabel { background: rgba(40, 40, 40, 0.5); padding: 5px; margin-right: 2px; border-radius: 5px 5px 0px 0px; color: #777; } .componentTab:hover + .compTabLabel { background: rgba(90, 90, 90, 0.5); } .componentTab:checked + .compTabLabel { background: rgba(127, 127, 127, 0.5); color: #fff; } .componentVisibility { position: relative; top: -2px; margin-left: 10px; } select { -moz-appearance: none; } select option { color: black; } select:focus { outline: none; border: none; } .sampleDistanceButton { width: 8mm; padding: 4px; padding-left: 6px; z-index: 1000; } .sampleDistanceButton svg { width: 1.2em; height: 1.2em; } .blendModeButton { width: 8mm; padding: 4px; padding-left: 8px; padding-right: 0px; z-index: 1000; } .blendModeButton svg { width: 1.2em; height: 1.2em; } .gradientOpacitySlider { width: 8mm; padding: 4px; padding-left: 6px; padding-right: 0px; z-index: 1000; } .gradientOpacitySlider svg { width: 1.2em; height: 1.2em; } .sliderEntry { flex: 1; display: flex; flex-direction: row; align-items: center; } .slider { flex: 1; min-height: 1rem; width: 5px; } .sliderLabelCommon { padding-left: 6px; padding-right: 6px; font-size: 1.1em; font-family: monospace; } .sliderLabelBrightBG { color: black; } .sliderLabelDarkBG { color: white; } .bigFileDrop { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); background-color: white; background-image: url('./dropBG.jpg'); background-repeat: no-repeat; background-position: center; background-size: contain; border-radius: 10px; width: 50px; padding: calc(50vh - 2em) calc(50vw - 25px - 2em); } .fullscreenContainer { position: absolute; width: 100vw; height: 100vh; top: 0; left: 0; overflow: hidden; background: black; margin: 0; padding: 0; } ================================================ FILE: src/UserInterface/PointSets/createPointSetColorBySelector.js ================================================ import { reaction } from 'mobx' import style from '../ItkVtkViewer.module.css' import { ColorMode, ScalarMode, } from 'vtk.js/Sources/Rendering/Core/Mapper/Constants' function createPointSetColorBySelector(store, colorByRow) { const colorBySelector = document.createElement('select') colorBySelector.setAttribute('class', style.selector) colorBySelector.id = `${store.id}-colorBySelector` reaction( () => { return store.pointSetsUI.pointSets.slice() }, pointSets => { if (!!!pointSets || pointSets.length === 0) { return } const hasScalars = store.pointSetsUI.hasScalars const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex const colorByOptions = store.pointSetsUI.colorByOptions if ( store.pointSetsUI.hasScalars[selectedPointSetIndex] && colorByOptions[selectedPointSetIndex].length > 1 ) { colorByRow.style.display = 'flex' } else { colorByRow.style.display = 'none' } const colorByDefault = store.pointSetsUI.colorByDefault pointSets.forEach((pointSet, index) => { if (store.pointSetsUI.colorBy.length <= index) { store.pointSetsUI.colorBy.push(colorByDefault[index]) } else { const current = store.pointSetsUI.colorBy[index] if ( !!store.pointSetsUI.colorByOptions[index] && !!!store.pointSetsUI.colorByOptions[index].filter(option => { return ( option.label === current.label && option.value === current.value ) }).length ) { store.pointSetsUI.colorBy[index] = colorByDefault[index] } } }) if (hasScalars[selectedPointSetIndex]) { colorBySelector.value = store.pointSetsUI.colorBy[selectedPointSetIndex].value } } ) reaction( () => { return store.pointSetsUI.selectedPointSetIndex }, selectedPointSetIndex => { const colorByOptions = store.pointSetsUI.colorByOptions if ( !!colorByOptions[selectedPointSetIndex] && !!colorByOptions[selectedPointSetIndex].length ) { colorBySelector.innerHTML = colorByOptions[selectedPointSetIndex] .map( ({ label, value }) => `` ) .join('') colorBySelector.value = store.pointSetsUI.colorBy[selectedPointSetIndex].value } const hasScalars = store.pointSetsUI.hasScalars if ( hasScalars[selectedPointSetIndex] && colorByOptions[selectedPointSetIndex].length > 1 ) { colorByRow.style.display = 'flex' } else { colorByRow.style.display = 'none' } } ) reaction( () => { return store.pointSetsUI.colorBy.slice() }, colorBy => { const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex if (!!!colorBy[selectedPointSetIndex]) { return } const [location, colorByArrayName] = colorBy[ selectedPointSetIndex ].value.split(':') const proxy = store.pointSetsUI.representationProxies[selectedPointSetIndex] const interpolateScalarsBeforeMapping = location === 'pointData' proxy.setInterpolateScalarsBeforeMapping(interpolateScalarsBeforeMapping) proxy.setColorBy(colorByArrayName, location) store.renderWindow.render() const hasScalars = store.pointSetsUI.hasScalars if (hasScalars[selectedPointSetIndex]) { colorBySelector.value = store.pointSetsUI.colorBy[selectedPointSetIndex].value } } ) colorBySelector.addEventListener('change', event => { event.preventDefault() event.stopPropagation() const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex const colorByOptions = store.pointSetsUI.colorByOptions const selectedOption = store.pointSetsUI.colorByOptions[ selectedPointSetIndex ].filter(option => { return option.value === event.target.value })[0] store.pointSetsUI.colorBy[selectedPointSetIndex] = selectedOption }) // Initialize coloring const colorByDefault = store.pointSetsUI.colorByDefault colorByDefault.forEach((colorBy, index) => { if (colorBy) { const [location, colorByArrayName] = colorBy.value.split(':') const proxy = store.pointSetsUI.representationProxies[index] const interpolateScalarsBeforeMapping = location === 'pointData' proxy.setInterpolateScalarsBeforeMapping(interpolateScalarsBeforeMapping) proxy.setColorBy(colorByArrayName, location) } }) const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex const colorByOptions = store.pointSetsUI.colorByOptions if (colorByDefault[selectedPointSetIndex]) { colorBySelector.innerHTML = colorByOptions[selectedPointSetIndex] .map(({ label, value }) => ``) .join('') colorBySelector.value = colorByDefault[selectedPointSetIndex].value } const hasScalars = store.pointSetsUI.hasScalars if ( hasScalars[selectedPointSetIndex] && colorByOptions[selectedPointSetIndex].length > 1 ) { colorByRow.style.display = 'flex' } else { colorByRow.style.display = 'none' } store.pointSetsUI.colorBy = colorByDefault colorByRow.appendChild(colorBySelector) } export default createPointSetColorBySelector ================================================ FILE: src/UserInterface/PointSets/createPointSetColorChooser.js ================================================ import { reaction } from 'mobx' import style from '../ItkVtkViewer.module.css' import hex2rgb from '../hex2rgb' function createPointSetColorChooser(store, pointSetColorRow) { const pointSetColorInput = document.createElement('input') pointSetColorInput.setAttribute('type', 'color') pointSetColorInput.id = `${store.id}-pointSetColorInput` const defaultPointSetColor = '#ffffff' reaction( () => { return store.pointSetsUI.pointSets.slice() }, pointSets => { if (!!!pointSets || pointSets.length === 0) { return } const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex pointSets.forEach((pointSet, index) => { if (store.pointSetsUI.colors.length <= index) { store.pointSetsUI.colors.push(defaultPointSetColor) } }) pointSetColorInput.value = store.pointSetsUI.colors[selectedPointSetIndex] } ) reaction( () => { return store.pointSetsUI.selectedPointSetIndex }, selectedPointSetIndex => { pointSetColorInput.value = store.pointSetsUI.colors[selectedPointSetIndex] if (store.pointSetsUI.hasScalars[selectedPointSetIndex]) { pointSetColorInput.style.display = 'none' } else { pointSetColorInput.style.display = 'inline-block' } } ) reaction( () => { return store.pointSetsUI.colors.slice() }, colors => { colors.forEach((value, index) => { const rgb = hex2rgb(value) store.pointSetsUI.representationProxies[index].setColor(rgb) }) store.renderWindow.render() const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex pointSetColorInput.value = colors[selectedPointSetIndex] } ) pointSetColorInput.addEventListener('input', event => { event.preventDefault() event.stopPropagation() const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex store.pointSetsUI.colors[selectedPointSetIndex] = event.target.value }) const defaultPointSetColors = Array(store.pointSetsUI.pointSets.length) defaultPointSetColors.fill(defaultPointSetColor) pointSetColorInput.value = defaultPointSetColor store.pointSetsUI.colors = defaultPointSetColors const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex if (store.pointSetsUI.hasScalars[selectedPointSetIndex]) { pointSetColorInput.style.display = 'none' } else { pointSetColorInput.style.display = 'inline-block' } pointSetColorRow.appendChild(pointSetColorInput) } export default createPointSetColorChooser ================================================ FILE: src/UserInterface/PointSets/createPointSetColorRangeInput.js ================================================ import { reaction, observable, action, toJS } from 'mobx' import vtkLookupTableProxy from 'vtk.js/Sources/Proxy/Core/LookupTableProxy' import style from '../ItkVtkViewer.module.css' import createColorMapIconSelector from '../createColorMapIconSelector' import customColorMapIcon from '../customColorMapIcon' function createColorRangeInput(store, uiContainer) { const minimumInput = document.createElement('input') minimumInput.type = 'number' minimumInput.setAttribute('class', style.numberInput) const maximumInput = document.createElement('input') maximumInput.type = 'number' maximumInput.setAttribute('class', style.numberInput) function updateColorRangeInput() { const selectedIndex = store.pointSetsUI.selectedPointSetIndex if (!store.pointSetsUI.hasScalars[selectedIndex]) { return } const colorByKey = store.pointSetsUI.colorBy[selectedIndex].value const [location, colorByArrayName] = colorByKey.split(':') const pointSet = store.pointSetsUI.pointSets[selectedIndex] const dataArray = location === 'pointData' ? pointSet.getPointData().getArrayByName(colorByArrayName) : pointSet.getCellData().getArrayByName(colorByArrayName) const range = dataArray.getRange() minimumInput.min = range[0] minimumInput.max = range[1] maximumInput.min = range[0] maximumInput.max = range[1] const data = dataArray.getData() if (data instanceof Float32Array || data instanceof Float64Array) { const step = (range[1] - range[0]) / 100.0 minimumInput.step = step maximumInput.step = step } const colorRange = store.pointSetsUI.selectedColorRange minimumInput.value = colorRange[0] maximumInput.value = colorRange[1] } function addColorRangesReactions(index, colorRanges) { if (store.pointSetsUI.colorRangesReactions.has(index)) { const disposer = store.pointSetsUI.colorRangesReactions.get(index) disposer() } const disposer = reaction( () => { return toJS(store.pointSetsUI.colorRanges) }, colorRanges => { if (index !== store.pointSetsUI.selectedPointSetIndex) { return } const colorRange = store.pointSetsUI.selectedColorRange if (!!colorRange) { minimumInput.value = colorRange[0] maximumInput.value = colorRange[1] const lutProxy = store.pointSetsUI.selectedLookupTableProxy const colorTransferFunction = lutProxy.getLookupTable() colorTransferFunction.setMappingRange(...colorRange) colorTransferFunction.updateRange() if (!store.renderWindow.getInteractor().isAnimating()) { store.renderWindow.render() } } } ) store.pointSetsUI.colorRangesReactions.set(index, disposer) } function setDefaultColorRangesColorMaps() { const colorByOptions = store.pointSetsUI.colorByOptions if (!!!colorByOptions || colorByOptions.length === 0) { return } const pointSets = store.pointSetsUI.pointSets colorByOptions.forEach((options, index) => { const pointSet = pointSets[index] if (!store.pointSetsUI.colorRanges.has(index)) { const colorRanges = observable(new Map()) if (options) { options.forEach(option => { const [location, colorByArrayName] = option.value.split(':') const dataArray = location === 'pointData' ? pointSet.getPointData().getArrayByName(colorByArrayName) : pointSet.getCellData().getArrayByName(colorByArrayName) const range = dataArray.getRange() colorRanges.set(option.value, range) }) } store.pointSetsUI.colorRanges.set(index, colorRanges) addColorRangesReactions(index, colorRanges) } else { // Constrain by min / max of possibly new inputs const colorRanges = store.pointSetsUI.colorRanges.get(index) !!options && options.forEach(option => { const [location, colorByArrayName] = option.value.split(':') const dataArray = location === 'pointData' ? pointSet.getPointData().getArrayByName(colorByArrayName) : pointSet.getCellData().getArrayByName(colorByArrayName) const range = dataArray.getRange() if (colorRanges.has(option.value)) { const current = colorRanges.get(option.value) if (current[0] < range[0] || current[1] > range[1]) { const newRange = current.slice() if (current[0] < range[0]) { newRange[0] = range[0] } if (current[1] > range[1]) { newRange[1] = range[1] } colorRanges.set(option.value, newRange) } } else { colorRanges.set(option.value, range) } }) addColorRangesReactions(index, colorRanges) } if (store.pointSetsUI.colorMaps.length <= index) { const defaultColorMap = 'Viridis (matplotlib)' store.pointSetsUI.colorMaps.push(defaultColorMap) const lutProxy = store.pointSetsUI.selectedLookupTableProxy if (!!lutProxy) { lutProxy.setPresetName(defaultColorMap) } } }) updateColorRangeInput() } setDefaultColorRangesColorMaps() reaction( () => { return store.pointSetsUI.selectedColorRange }, colorRange => { if (!!colorRange) { minimumInput.value = colorRange[0] maximumInput.value = colorRange[1] const lutProxy = store.pointSetsUI.selectedLookupTableProxy const colorTransferFunction = lutProxy.getLookupTable() colorTransferFunction.setMappingRange(...colorRange) colorTransferFunction.updateRange() } } ) minimumInput.addEventListener( 'change', action(event => { event.preventDefault() event.stopPropagation() const selectedIndex = store.pointSetsUI.selectedPointSetIndex const colorByKey = store.pointSetsUI.colorBy[selectedIndex].value const range = store.pointSetsUI.colorRanges .get(selectedIndex) .get(colorByKey) range[0] = Number(event.target.value) store.pointSetsUI.colorRanges.get(selectedIndex).set(colorByKey, range) }) ) maximumInput.addEventListener( 'change', action(event => { event.preventDefault() event.stopPropagation() const selectedIndex = store.pointSetsUI.selectedPointSetIndex const colorByKey = store.pointSetsUI.colorBy[selectedIndex].value const range = store.pointSetsUI.colorRanges .get(selectedIndex) .get(colorByKey) range[1] = Number(event.target.value) store.pointSetsUI.colorRanges.get(selectedIndex).set(colorByKey, range) }) ) const colorMapSelector = document.createElement('div') colorMapSelector.id = `${store.id}-pointSetColorMapSelector` const iconSelector = createColorMapIconSelector(colorMapSelector) function updateColorCanvas() { const selectedIndex = store.pointSetsUI.selectedPointSetIndex if (!store.pointSetsUI.hasScalars[selectedIndex]) { return } const colorMap = store.pointSetsUI.colorMaps[selectedIndex] const lookupTableProxy = store.pointSetsUI.selectedLookupTableProxy const colorTransferFunction = lookupTableProxy.getLookupTable() if (colorMap.startsWith('Custom')) { lookupTableProxy.setMode(vtkLookupTableProxy.Mode.RGBPoints) const colorRange = store.pointSetsUI.selectedColorRange const isIcons = iconSelector.getIcons() if (!!!customIcon) { const colorMapIcon = customColorMapIcon( colorTransferFunction, colorDataRange ) customIcon = { iconFilePath: colorMapIcon, iconValue: colorMap } icons.push(customIcon) iconSelector.refresh(icons) } else if (isIcons[isIcons.length - 1].iconValue !== colorMap) { const colorMapIcon = customColorMapIcon( colorTransferFunction, colorDataRange ) isIcons[isIcons.length - 1].element.src = colorMapIcon isIcons[isIcons.length - 1].iconFilePath = colorMapIcon isIcons[isIcons.length - 1].iconValue = colorMap isIcons[isIcons.length - 1].element.setAttribute('icon-value', colorMap) isIcons[isIcons.length - 1].element.setAttribute('alt', colorMap) isIcons[isIcons.length - 1].element.setAttribute('title', colorMap) } } else { lookupTableProxy.setPresetName(colorMap) lookupTableProxy.setMode(vtkLookupTableProxy.Mode.Preset) } iconSelector.setSelectedValue(colorMap) if (!store.renderWindow.getInteractor().isAnimating()) { store.renderWindow.render() } } let customIcon = null reaction( () => { return store.pointSetsUI.colorMaps.slice() }, colorMaps => { updateColorCanvas() } ) colorMapSelector.addEventListener('changed', event => { event.preventDefault() event.stopPropagation() const selectedIndex = store.pointSetsUI.selectedPointSetIndex store.pointSetsUI.colorMaps[selectedIndex] = iconSelector.getSelectedValue() }) const pointSetIndex = store.pointSetsUI.selectedPointSetIndex iconSelector.setSelectedValue(store.pointSetsUI.colorMaps[pointSetIndex]) reaction( () => { return store.pointSetsUI.colorByOptions.slice() }, () => { setDefaultColorRangesColorMaps() } ) reaction( () => { return store.pointSetsUI.selectedPointSetIndex }, selectedIndex => { const hasScalars = store.pointSetsUI.hasScalars if (hasScalars[selectedIndex]) { uiContainer.style.display = 'flex' updateColorRangeInput() updateColorCanvas() } else { uiContainer.style.display = 'none' } } ) reaction( () => { return store.pointSetsUI.colorBy.slice() }, () => { updateColorRangeInput() updateColorCanvas() } ) updateColorCanvas() const hasScalars = store.pointSetsUI.hasScalars const selectedIndex = store.pointSetsUI.selectedPointSetIndex if (hasScalars[selectedIndex]) { uiContainer.style.display = 'flex' } else { uiContainer.style.display = 'none' } uiContainer.appendChild(minimumInput) uiContainer.appendChild(colorMapSelector) uiContainer.appendChild(maximumInput) } export default createColorRangeInput ================================================ FILE: src/UserInterface/PointSets/createPointSetColorWidget.js ================================================ import style from '../ItkVtkViewer.module.css' import createPointSetColorChooser from './createPointSetColorChooser' import createPointSetOpacitySlider from './createPointSetOpacitySlider' import createPointSetColorBySelector from './createPointSetColorBySelector' import createPointSetSizeSlider from './createPointSetSizeSlider' import createPointSetColorRangeInput from './createPointSetColorRangeInput' function createPointSetColorWidget(store, pointSetsUIGroup) { const colorByRow = document.createElement('div') colorByRow.setAttribute('class', style.uiRow) colorByRow.className += ` ${store.id}-collapsible` createPointSetColorBySelector(store, colorByRow) pointSetsUIGroup.appendChild(colorByRow) const pointSetColorRow = document.createElement('div') pointSetColorRow.setAttribute('class', style.uiRow) pointSetColorRow.className += ` ${store.id}-collapsible` createPointSetColorChooser(store, pointSetColorRow) createPointSetOpacitySlider(store, pointSetColorRow) pointSetsUIGroup.appendChild(pointSetColorRow) const colorRangeInputRow = document.createElement('div') colorRangeInputRow.setAttribute('class', style.uiRow) createPointSetColorRangeInput(store, colorRangeInputRow) colorRangeInputRow.className += ` ${store.id}-collapsible` pointSetsUIGroup.appendChild(colorRangeInputRow) const pointSetSizeRow = document.createElement('div') pointSetSizeRow.setAttribute('class', style.uiRow) pointSetSizeRow.className += ` ${store.id}-collapsible` createPointSetSizeSlider(store, pointSetSizeRow) pointSetsUIGroup.appendChild(pointSetSizeRow) if (store.mainUI.collapsed) { colorByRow.style.display = 'none' colorRangeInputRow.style.display = 'none' pointSetSizeRow.style.display = 'none' } } export default createPointSetColorWidget ================================================ FILE: src/UserInterface/PointSets/createPointSetOpacitySlider.js ================================================ import { reaction, action } from 'mobx' import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyle from '../applyContrastSensitiveStyle' import opacityIcon from '../icons/opacity.svg' function createPointSetOpacitySlider(store, pointSetColorRow) { const defaultPointSetOpacity = 1.0 const sliderEntry = document.createElement('div') sliderEntry.setAttribute('class', style.sliderEntry) sliderEntry.innerHTML = `
${opacityIcon}
` const opacityElement = sliderEntry.querySelector( `#${store.id}-pointSetOpacitySlider` ) const sliderEntryDiv = sliderEntry.children[0] applyContrastSensitiveStyle(store, 'invertibleButton', sliderEntryDiv) reaction( () => { return store.pointSetsUI.pointSets.slice() }, pointSets => { if (!!!pointSets || pointSets.length === 0) { return } pointSets.forEach((pointSet, index) => { if (store.pointSetsUI.opacities.length <= index) { store.pointSetsUI.opacities.push(defaultPointSetOpacity) } }) const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex opacityElement.value = store.pointSetsUI.opacities[selectedPointSetIndex] } ) reaction( () => { return store.pointSetsUI.selectedPointSetIndex }, selectedPointSetIndex => { opacityElement.value = store.pointSetsUI.opacities[selectedPointSetIndex] } ) reaction( () => { return store.pointSetsUI.opacities.slice() }, opacities => { opacities.forEach((opacity, index) => { store.pointSetsUI.representationProxies[index].setOpacity(opacity) }) store.renderWindow.render() const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex opacityElement.value = opacities[selectedPointSetIndex] } ) opacityElement.addEventListener( 'input', action(event => { event.preventDefault() event.stopPropagation() const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex store.pointSetsUI.opacities[selectedPointSetIndex] = Number( event.target.value ) }) ) const defaultPointSetOpacities = new Array(store.pointSetsUI.pointSets.length) defaultPointSetOpacities.fill(defaultPointSetOpacity) opacityElement.value = defaultPointSetOpacity store.pointSetsUI.opacities = defaultPointSetOpacities pointSetColorRow.appendChild(sliderEntry) } export default createPointSetOpacitySlider ================================================ FILE: src/UserInterface/PointSets/createPointSetRepresentationSelector.js ================================================ import { reaction } from 'mobx' import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyle from '../applyContrastSensitiveStyle' import hiddenIcon from '../icons/hidden.svg' import pointsIcon from '../icons/point-set-points.svg' import spheresIcon from '../icons/point-set-spheres.svg' function createPointSetRepresentationSelector( store, pointSetRepresentationRow ) { const viewerDOMId = store.id const pointSetHiddenButton = document.createElement('div') pointSetHiddenButton.innerHTML = `` pointSetHiddenButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex store.pointSetsUI.representations[selectedPointSetIndex] = 'Hidden' }) pointSetRepresentationRow.appendChild(pointSetHiddenButton) const pointSetHiddenButtonInput = pointSetHiddenButton.children[0] const pointSetHiddenButtonLabel = pointSetHiddenButton.children[1] applyContrastSensitiveStyle(store, 'tooltipButton', pointSetHiddenButtonLabel) const pointSetPointsButton = document.createElement('div') pointSetPointsButton.innerHTML = `` pointSetPointsButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex store.pointSetsUI.representations[selectedPointSetIndex] = 'Points' }) pointSetRepresentationRow.appendChild(pointSetPointsButton) const pointSetPointsButtonInput = pointSetPointsButton.children[0] const pointSetPointsButtonLabel = pointSetPointsButton.children[1] applyContrastSensitiveStyle(store, 'tooltipButton', pointSetPointsButtonLabel) const pointSetSpheresButton = document.createElement('div') pointSetSpheresButton.innerHTML = `` pointSetSpheresButton.addEventListener('click', event => { event.preventDefault() event.stopPropagation() const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex store.pointSetsUI.representations[selectedPointSetIndex] = 'Spheres' }) pointSetRepresentationRow.appendChild(pointSetSpheresButton) const pointSetSpheresButtonInput = pointSetSpheresButton.children[0] const pointSetSpheresButtonLabel = pointSetSpheresButton.children[1] applyContrastSensitiveStyle( store, 'tooltipButton', pointSetSpheresButtonLabel ) function updateEnabledRepresentationButtons(selectedPointSetRepresentation) { switch (selectedPointSetRepresentation) { case 'Hidden': pointSetHiddenButtonInput.checked = true pointSetPointsButtonInput.checked = false pointSetSpheresButtonInput.checked = false break case 'Points': pointSetHiddenButtonInput.checked = false pointSetPointsButtonInput.checked = true pointSetSpheresButtonInput.checked = false break case 'Spheres': pointSetHiddenButtonInput.checked = false pointSetPointsButtonInput.checked = false pointSetSpheresButtonInput.checked = true break default: console.error( 'Invalid pointSet representation: ' + selectedPointSetRepresentation ) } } function setRepresentation(value, index) { if (value === 'Hidden') { store.pointSetsUI.representationProxies[index].setVisibility(false) } else { store.pointSetsUI.representationProxies[index].setRepresentation(value) store.pointSetsUI.representationProxies[index].setVisibility(true) } } reaction( () => { return store.pointSetsUI.representations.slice() }, representations => { representations.forEach((representation, index) => { setRepresentation(representation, index) }) const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex const representation = store.pointSetsUI.representations[selectedPointSetIndex] updateEnabledRepresentationButtons(representation) store.renderWindow.render() } ) reaction( () => { return store.pointSetsUI.selectedPointSetIndex }, selectedIndex => { const selectedPointSetRepresentation = store.pointSetsUI.representations[selectedIndex] updateEnabledRepresentationButtons(selectedPointSetRepresentation) } ) const defaultPointSetRepresentation = 'Points' reaction( () => { return store.pointSetsUI.pointSets.slice() }, pointSets => { if (!!!pointSets || pointSets.length === 0) { return } pointSets.forEach((pointSet, index) => { if (store.pointSetsUI.representations.length <= index) { store.pointSetsUI.representations.push(defaultPointSetRepresentation) } }) const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex updateEnabledRepresentationButtons( store.pointSetsUI.representations[selectedPointSetIndex] ) } ) const defaultPointSetRepresentations = new Array( store.pointSetsUI.pointSets.length ) defaultPointSetRepresentations.fill(defaultPointSetRepresentation) updateEnabledRepresentationButtons(defaultPointSetRepresentation) store.pointSetsUI.representations = defaultPointSetRepresentations const pointSetRepresentationProxies = store.pointSetsUI.representationProxies } export default createPointSetRepresentationSelector ================================================ FILE: src/UserInterface/PointSets/createPointSetSizeSlider.js ================================================ import { reaction } from 'mobx' import style from '../ItkVtkViewer.module.css' import applyContrastSensitiveStyle from '../applyContrastSensitiveStyle' import pointSetSizeIcon from '../icons/point-set-size.svg' function createPointSetSizeSlider(store, pointSetSizeRow) { const defaultPointSetSize = 3 const sliderEntry = document.createElement('div') sliderEntry.setAttribute('class', style.sliderEntry) sliderEntry.innerHTML = `
${pointSetSizeIcon}
` const sizeElement = sliderEntry.querySelector( `#${store.id}-pointSetSizeSlider` ) const sliderEntryDiv = sliderEntry.children[0] applyContrastSensitiveStyle(store, 'invertibleButton', sliderEntryDiv) reaction( () => { return store.pointSetsUI.pointSets.slice() }, pointSets => { if (!!!pointSets || pointSets.length === 0) { return } pointSets.forEach((pointSet, index) => { if (store.pointSetsUI.sizes.length <= index) { store.pointSetsUI.sizes.push(defaultPointSetSize) } }) const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex sizeElement.value = store.pointSetsUI.sizes[selectedPointSetIndex] } ) reaction( () => { return store.pointSetsUI.selectedPointSetIndex }, selectedPointSetIndex => { sizeElement.value = store.pointSetsUI.sizes[selectedPointSetIndex] } ) reaction( () => { return store.pointSetsUI.sizes.slice() }, sizes => { for (let index = 0; index < sizes.length; index++) { store.pointSetsUI.representationProxies[index].setPointSize( sizes[index] ) } store.renderWindow.render() sizeElement.value = sizes[store.pointSetsUI.selectedPointSetIndex] } ) sizeElement.addEventListener('input', event => { event.preventDefault() event.stopPropagation() const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex store.pointSetsUI.sizes[selectedPointSetIndex] = Number(event.target.value) }) const defaultPointSetSizes = new Array(store.pointSetsUI.pointSets.length) defaultPointSetSizes.fill(defaultPointSetSize) sizeElement.value = defaultPointSetSize store.pointSetsUI.sizes = defaultPointSetSizes store.pointSetsUI.representationProxies.forEach(proxy => { proxy.setPointSize(defaultPointSetSize) }) pointSetSizeRow.appendChild(sliderEntry) } export default createPointSetSizeSlider ================================================ FILE: src/UserInterface/addLogo.js ================================================ import style from './ItkVtkViewer.module.css' import logoIcon from './icons/logo.png' import vtkFPSMonitor from 'vtk.js/Sources/Interaction/UI/FPSMonitor' function addLogo(store) { const logo = new Image() logo.src = logoIcon logo.setAttribute('class', style.logo) store.container.appendChild(logo) const showCredits = () => { logo.style.display = 'none' if (!store.appAttribution) { const appAttribution = document.createElement('div') appAttribution.setAttribute('class', style.fpsMonitor) appAttribution.innerHTML = `

itk-vtk-viewer development is
lead by the hearts and minds at
Kitware.

` store.container.appendChild(appAttribution) store.appAttribution = appAttribution } } const showFps = () => { logo.style.display = 'none' if (!store.fpsMonitor) { const fpsMonitor = vtkFPSMonitor.newInstance() const fpsElement = fpsMonitor.getFpsMonitorContainer() fpsElement.setAttribute('class', style.fpsMonitor) fpsMonitor.setContainer(store.container) fpsMonitor.setBufferSize(100) fpsMonitor.setRenderWindow(store.renderWindow) fpsMonitor.update() store.fpsMonitor = fpsMonitor } } logo.addEventListener('mousedown', e => { if (e.button === 0) { showCredits() } else { showFps() } }) } export default addLogo ================================================ FILE: src/UserInterface/applyContrastSensitiveStyle.js ================================================ import style from './ItkVtkViewer.module.css' import { autorun, reaction } from 'mobx' function applyContrastSensitiveStyle(store, cssClass, element) { autorun(() => { const isBackgroundDark = store.isBackgroundDark const addPostFix = isBackgroundDark ? 'DarkBG' : 'BrightBG' const removePostFix = !isBackgroundDark ? 'DarkBG' : 'BrightBG' const removeClass = style[`${cssClass}${removePostFix}`] if (element.classList.contains(removeClass)) { element.classList.remove(removeClass) } element.classList.add(style[`${cssClass}${addPostFix}`]) }) } export default applyContrastSensitiveStyle ================================================ FILE: src/UserInterface/checkForWebGL.js ================================================ function checkForWebGL(container) { const testCanvas = document.createElement('canvas') const gl = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl') if (!(gl && gl instanceof WebGLRenderingContext)) { const suggestion = document.createElement('p') const preSuggestionText = document.createTextNode( 'WebGL could not be loaded. ' ) suggestion.appendChild(preSuggestionText) const getWebGLA = document.createElement('a') getWebGLA.setAttribute('href', 'http://get.webgl.org/troubleshooting') const getWebGLAText = document.createTextNode( 'Try a different browser or video drivers for WebGL support.' ) getWebGLA.appendChild(getWebGLAText) suggestion.appendChild(getWebGLA) const suggestionText = document.createTextNode( ' This is required to view interactive 3D visualizations.' ) suggestion.appendChild(suggestionText) container.appendChild(suggestion) return false } return true } export default checkForWebGL ================================================ FILE: src/UserInterface/createCategoricalColorIconSelector.js ================================================ import { IconSelect } from '@thewtex/iconselect.js/lib/control/iconselect' import { CategoricalColorIcons } from 'itk-viewer-color-maps' function createCategoricalColorIconSelector(categoricalColorSelectorDiv) { const rows = 4 const cols = 2 const iconSelectParameters = { selectedIconWidth: 140, selectedIconHeight: 22, selectedBoxPadding: 1, iconsWidth: 60, iconsHeight: 22, boxIconSpace: 1, vectoralIconNumber: cols, horizontalIconNumber: rows, } const iconSelect = new IconSelect( `${categoricalColorSelectorDiv.id}`, categoricalColorSelectorDiv, iconSelectParameters ) categoricalColorSelectorDiv.style.width = '154px' const icons = new Array(rows * cols) let count = 0 for (let [key, value] of CategoricalColorIcons.entries()) { const index = Math.floor(count % rows) * cols + Math.floor(count / rows) icons[index] = { iconFilePath: value, iconValue: key } count++ } iconSelect.refresh(icons) return iconSelect } export default createCategoricalColorIconSelector ================================================ FILE: src/UserInterface/createColorMapIconSelector.js ================================================ import { IconSelect } from '@thewtex/iconselect.js/lib/control/iconselect' import { ColorMapIcons } from 'itk-viewer-color-maps' function createColorMapIconSelector(colorMapSelectorDiv) { const rows = 19 const cols = 4 const iconSelectParameters = { selectedIconWidth: 230, selectedIconHeight: 22, selectedBoxPadding: 1, iconsWidth: 80, iconsHeight: 22, boxIconSpace: 1, vectoralIconNumber: cols, horizontalIconNumber: rows, } const iconSelect = new IconSelect( `${colorMapSelectorDiv.id}`, colorMapSelectorDiv, iconSelectParameters ) colorMapSelectorDiv.style.width = '250px' const icons = new Array(rows * cols) let count = 0 for (let [key, value] of ColorMapIcons.entries()) { const index = Math.floor(count % rows) * cols + Math.floor(count / rows) icons[index] = { iconFilePath: value, iconValue: key } count++ } const contiguousIcons = icons.filter(i => i) iconSelect.refresh(contiguousIcons) return iconSelect } export default createColorMapIconSelector ================================================ FILE: src/UserInterface/createFileDragAndDrop.js ================================================ import vtkURLExtract from 'vtk.js/Sources/Common/Core/URLExtract' import getRootContainer from './getRootContainer' import preventDefaults from './preventDefaults' import style from './ItkVtkViewer.module.css' import Mousetrap from 'mousetrap' const MOUSETRAP = new Mousetrap() function createFileDragAndDrop(container, onDataChange) { const myContainer = getRootContainer(container) const fileContainer = document.createElement('div') fileContainer.innerHTML = `
` myContainer.appendChild(fileContainer) const fileInput = fileContainer.querySelector('input') MOUSETRAP.bind('enter', event => { fileInput.click() }) return new Promise(resolve => { function handleFile(e) { preventDefaults(e) MOUSETRAP.unbind('enter') const dataTransfer = e.dataTransfer const files = e.target.files || dataTransfer.files myContainer.removeChild(fileContainer) const use2D = !!vtkURLExtract.extractURLParameters().use2D resolve( onDataChange(myContainer, { files, use2D }).catch(error => { const message = 'An error occurred while loading the file:\n\n' + error.message alert(message) createFileDragAndDrop(container, onDataChange) }) ) } fileInput.addEventListener('change', handleFile) fileContainer.addEventListener('drop', handleFile) fileContainer.addEventListener('click', e => fileInput.click()) fileContainer.addEventListener('dragover', preventDefaults) }) } export default createFileDragAndDrop ================================================ FILE: src/UserInterface/createGeometriesUI.js ================================================ import { reaction, autorun } from 'mobx' import style from './ItkVtkViewer.module.css' import createGeometryRepresentationSelector from './Geometries/createGeometryRepresentationSelector' import createGeometryColorWidget from './Geometries/createGeometryColorWidget' function createGeometriesUI(store, uiContainer) { const geometriesUIGroup = document.createElement('div') geometriesUIGroup.setAttribute('class', style.uiGroup) const geometryRepresentationRow = document.createElement('div') geometryRepresentationRow.setAttribute('class', style.uiRow) geometryRepresentationRow.className += ` ${store.id}-collapsible` if (store.mainUI.collapsed) { geometryRepresentationRow.style.display = 'none' } const geometrySelector = document.createElement('select') geometrySelector.setAttribute('class', style.selector) geometrySelector.id = `${store.id}-geometrySelector` geometryRepresentationRow.appendChild(geometrySelector) geometrySelector.addEventListener('change', event => { event.preventDefault() event.stopPropagation() store.geometriesUI.selectedGeometryIndex = geometrySelector.selectedIndex }) function updateGeometryNames(names) { geometrySelector.innerHTML = names .map(name => ``) .join('') if (names.length > 1) { geometrySelector.disabled = false } else { geometrySelector.disabled = true } } reaction( () => { return store.geometriesUI.names.slice() }, names => { updateGeometryNames(names) } ) if (store.geometriesUI.geometries.length > 0) { store.geometriesUI.selectedGeometryIndex = 0 } autorun(() => { const geometries = store.geometriesUI.geometries store.geometriesUI.names = geometries.map((geometry, index) => { const metadata = geometry.getState().metadata return !!metadata && !!metadata.name ? metadata.name : `Geometry ${index}` }) }) createGeometryRepresentationSelector(store, geometryRepresentationRow) geometriesUIGroup.appendChild(geometryRepresentationRow) createGeometryColorWidget(store, geometriesUIGroup) uiContainer.appendChild(geometriesUIGroup) store.geometriesUI.initialized = true } export default createGeometriesUI ================================================ FILE: src/UserInterface/createLoadingProgress.js ================================================ import getRootContainer from './getRootContainer' import style from './ItkVtkViewer.module.css' function createLoadingProgress(container) { const rootContainer = getRootContainer(container) const loading = document.createElement('div') loading.setAttribute('class', style.loading) rootContainer.appendChild(loading) const progressContainer = document.createElement('div') progressContainer.setAttribute('class', style.progress) rootContainer.appendChild(progressContainer) function progressCallback(progressEvent) { const percent = Math.floor( (100 * progressEvent.loaded) / progressEvent.total ) progressContainer.innerHTML = `${percent}%` } return progressCallback } export default createLoadingProgress ================================================ FILE: src/UserInterface/createPointSetsUI.js ================================================ import { reaction, autorun } from 'mobx' import style from './ItkVtkViewer.module.css' import createPointSetRepresentationSelector from './PointSets/createPointSetRepresentationSelector' import createPointSetColorWidget from './PointSets/createPointSetColorWidget' function createPointSetsUI(store, uiContainer) { const pointSetsUIGroup = document.createElement('div') pointSetsUIGroup.setAttribute('class', style.uiGroup) const pointSetRepresentationRow = document.createElement('div') pointSetRepresentationRow.setAttribute('class', style.uiRow) pointSetRepresentationRow.className += ` ${store.id}-collapsible` if (store.mainUI.collapsed) { pointSetRepresentationRow.style.display = 'none' } const pointSetSelector = document.createElement('select') pointSetSelector.setAttribute('class', style.selector) pointSetSelector.id = `${store.id}-pointSetSelector` pointSetRepresentationRow.appendChild(pointSetSelector) pointSetSelector.addEventListener('change', event => { event.preventDefault() event.stopPropagation() store.pointSetsUI.selectedPointSetIndex = pointSetSelector.selectedIndex }) function updatePointSetNames(names) { pointSetSelector.innerHTML = names .map(name => ``) .join('') if (names.length > 1) { pointSetSelector.disabled = false } else { pointSetSelector.disabled = true } } reaction( () => { return store.pointSetsUI.names.slice() }, names => { updatePointSetNames(names) } ) if (store.pointSetsUI.pointSets.length > 0) { store.pointSetsUI.selectedPointSetIndex = 0 } autorun(() => { const pointSets = store.pointSetsUI.pointSets store.pointSetsUI.names = pointSets.map((pointSet, index) => { const metadata = pointSet.getState().metadata return !!metadata && !!metadata.name ? metadata.name : `Point Set ${index}` }) }) createPointSetRepresentationSelector(store, pointSetRepresentationRow) pointSetsUIGroup.appendChild(pointSetRepresentationRow) createPointSetColorWidget(store, pointSetsUIGroup) uiContainer.appendChild(pointSetsUIGroup) store.pointSetsUI.initialized = true } export default createPointSetsUI ================================================ FILE: src/UserInterface/customColorMapIcon.js ================================================ const canvas = document.createElement('canvas') const width = 240 const height = 20 canvas.setAttribute('width', width) canvas.setAttribute('height', height) function customColorMapIcon(colorTransferFunction, range) { const ctx = canvas.getContext('2d') const rgba = colorTransferFunction.getUint8Table(range[0], range[1], width, 4) const pixelsArea = ctx.getImageData(0, 0, width, 256) for (let lineIdx = 0; lineIdx < 256; lineIdx++) { pixelsArea.data.set(rgba, lineIdx * 4 * width) } const nbValues = 256 * width * 4 const lineSize = width * 4 for (let i = 3; i < nbValues; i += 4) { pixelsArea.data[i] = 255 - Math.floor(i / lineSize) } ctx.putImageData(pixelsArea, 0, 0) return canvas.toDataURL('image/png') } export default customColorMapIcon ================================================ FILE: src/UserInterface/emptyContainer.js ================================================ function emptyContainer(container) { if (container) { while (container.firstChild) { container.removeChild(container.firstChild) } } } export default emptyContainer ================================================ FILE: src/UserInterface/getContrastSensitiveStyle.js ================================================ import style from './ItkVtkViewer.module.css' function getContrastSensitiveStyle(cssClasses, isBackgroundDark) { const stylePostFix = isBackgroundDark ? 'DarkBG' : 'BrightBG' const contrastSensitiveStyle = {} cssClasses.forEach(name => { contrastSensitiveStyle[name] = style[`${name}${stylePostFix}`] }) return contrastSensitiveStyle } export default getContrastSensitiveStyle ================================================ FILE: src/UserInterface/getRootContainer.js ================================================ function getRootContainer(container) { const workContainer = document.querySelector('.content') const rootBody = document.querySelector('body') return container || workContainer || rootBody } export default getRootContainer ================================================ FILE: src/UserInterface/hex2rgb.js ================================================ function hex2rgb(hexColor) { const bigint = parseInt(hexColor.substring(1), 16) const r = ((bigint >> 16) & 255) / 255.0 const g = ((bigint >> 8) & 255) / 255.0 const b = (bigint & 255) / 255.0 return [r, g, b] } export default hex2rgb ================================================ FILE: src/UserInterface/index.js ================================================ import addLogo from './addLogo' import createLoadingProgress from './createLoadingProgress' import createGeometriesUI from './createGeometriesUI' import createPointSetsUI from './createPointSetsUI' import emptyContainer from './emptyContainer' import getRootContainer from './getRootContainer' import checkForWebGL from './checkForWebGL' export default { addLogo, createLoadingProgress, createGeometriesUI, createPointSetsUI, emptyContainer, getRootContainer, checkForWebGL, } ================================================ FILE: src/UserInterface/preventDefaults.js ================================================ function preventDefaults(e) { e.preventDefault() e.stopPropagation() } export default preventDefaults ================================================ FILE: src/UserInterface/rgb2hex.js ================================================ function rgb2hex(rgb) { return ( '#' + ((1 << 24) + ((rgb[0] * 255) << 16) + ((rgb[1] * 255) << 8) + rgb[2] * 255) .toString(16) .slice(1) ).substring(0, 7) } export default rgb2hex ================================================ FILE: src/ViewerStore.js ================================================ import { observable, computed, trace } from 'mobx' import EventEmitter from 'eventemitter3' import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData' import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray' import { VtkDataTypes } from 'vtk.js/Sources/Common/Core/DataArray/Constants' const STYLE_RENDERING_VIEW_CONTAINER = { position: 'relative', width: '100%', height: '100%', minHeight: '200px', minWidth: '450px', margin: '0', padding: '0', top: '0', left: '0', overflow: 'hidden', } class MainUIStore { uiContainer = null @observable collapsed = false @observable annotationsEnabled = true @observable axesEnabled = false @observable fullscreenEnabled = false @observable rotateEnabled = false @observable interpolationEnabled = true @observable croppingPlanesEnabled = false @observable viewMode = 'Volume' fps = [60, 60, 60] @observable fpsTooLow = false fpsMonitor = null } class ImageUIStore { constructor(eventEmitter) { this.eventEmitter = eventEmitter } eventEmitter = null @observable.ref image = null @observable.ref multiscaleImage = null source = null @observable.ref representationProxy = null @observable selectedComponent = 0 // Does not include the label map @computed get numberOfComponents() { if (!!!this.image) { return 0 } const dataArray = this.fusedImageLabelMap.getPointData().getScalars() if (!!this.labelMap) { return dataArray.getNumberOfComponents() - 1 } return dataArray.getNumberOfComponents() } totalIntensityComponents = 0 maxIntensityComponents = 3 piecewiseFunctionProxies = [] @observable componentVisibilities = [] lastVisualizedComponents = [] fusedImageData = null @observable visualizedComponents = [] lastComponentVisibilityChanged = 0 transferFunctionManipulator = { rangeManipulator: null, windowMotionScale: 150.0, levelMotionScale: 150.0, windowGet: null, windowSet: null, levelGet: null, levelSet: null, } independentComponents = true imageUIGroup = null croppingWidget = null addCroppingPlanesChangedHandler = () => {} addResetCropHandler = () => {} @observable colorMaps = null @observable colorRanges = [] @observable opacityGaussians = [] @observable blendMode = 0 @observable useShadow = true @observable slicingPlanesEnabled = false @observable gradientOpacity = 0.2 @observable volumeSampleDistance = 0.25 @observable xSlice = null @observable ySlice = null @observable zSlice = null @observable.ref labelMap = null @observable.ref multiscaleLabelMap = null // @observable fusingImages = false @computed get fusedImageLabelMap() { const image = this.image const labelMap = this.labelMap if (!!!image && !!!labelMap) { return null } if (!!!image) { return labelMap } if (this.visualizedComponents.length === 0) { return null } else if (!!labelMap && this.visualizedComponents.length === 4) { return null } const imageScalars = image.getPointData().getScalars() const imageData = imageScalars.getData() const imageComponents = imageScalars.getNumberOfComponents() this.totalIntensityComponents = imageComponents if (!!!labelMap && imageComponents <= 4) { return image } const visualizedComponents = this.visualizedComponents.map(idx => idx) const fusedImage = vtkImageData.newInstance() fusedImage.setOrigin(image.getOrigin()) fusedImage.setSpacing(image.getSpacing()) fusedImage.setDirection(image.getDirection()) const imageDimensions = image.getDimensions() if (!!labelMap) { const labelMapDimensions = labelMap.getDimensions() const dimensionsEqual = imageDimensions.every((dim, index) => { return labelMapDimensions[index] === dim }) if (!dimensionsEqual) { console.error( `Dimensions not equal! Not fusing. Image: ${imageDimensions} Label map: ${labelMapDimensions}` ) return image } } const numVisualizedComponents = this.visualizedComponents.length fusedImage.setDimensions(image.getDimensions()) const imageTuples = imageScalars.getNumberOfTuples() let labelMapScalars = null let labelMapData = null if (!!labelMap) { labelMapScalars = labelMap.getPointData().getScalars() labelMapData = labelMapScalars.getData() visualizedComponents.push(-1) } const fusedImageComponents = labelMapData ? numVisualizedComponents + 1 : numVisualizedComponents const length = imageTuples * fusedImageComponents // We only need to construct a new typed array if we don't already // have one of the right length. if (!!!this.fusedImageData || this.fusedImageData.length !== length) { this.fusedImageData = new imageData.constructor(length) } const copyStructure = [] // Loop through comparing to last time and check which components need // to be copied into fusedImageData. This loop doesn't include the // labelmap componentm, it will be checked next. for (let i = 0; i < numVisualizedComponents; i++) { if (visualizedComponents[i] !== this.lastVisualizedComponents[i]) { copyStructure.push({ srcImageData: imageData, imageComponents: imageComponents, copyFromComponent: this.visualizedComponents[i], copyToComponent: i, }) } } // Check if we need to re-copy the labelmap component if ( visualizedComponents[numVisualizedComponents] === -1 && this.lastVisualizedComponents[numVisualizedComponents] !== -1 ) { copyStructure.push({ srcImageData: labelMapData, imageComponents: 1, copyFromComponent: 0, copyToComponent: numVisualizedComponents, }) } // console.log(`Copying ${copyStructure.length} components into fused image`) let fusedIndex = 0 let imageIndex = 0 for (let tuple = 0; tuple < imageTuples; tuple++) { for (let cIdx = 0; cIdx < copyStructure.length; cIdx++) { imageIndex = tuple * copyStructure[cIdx].imageComponents + copyStructure[cIdx].copyFromComponent fusedIndex = tuple * fusedImageComponents + copyStructure[cIdx].copyToComponent this.fusedImageData[fusedIndex] = copyStructure[cIdx].srcImageData[imageIndex] } } const fusedImageScalars = vtkDataArray.newInstance({ name: imageScalars.getName() || 'Scalars', values: this.fusedImageData, numberOfComponents: fusedImageComponents, }) fusedImage.getPointData().setScalars(fusedImageScalars) this.lastVisualizedComponents = visualizedComponents.map(idx => idx) return fusedImage } @computed get haveOnlyLabelMap() { return ( (!!this.labelMap || !!this.multiscaleLabelMap) && !!!this.image && !!!this.multiscaleImage ) } @computed get haveLabelMap() { return !!this.labelMap || !!this.multiscaleLabelMap } labelMapColorUIGroup = null // Sorted array of label values labelMapLabels = null piecewiseFunction = null @observable lastPickedValues = {} @observable labelMapBlend = 0.5 @observable labelMapLookupTable = 'glasbey' @observable labelMapWeights = [] @observable labelMapToggleWeight = 0.1 @observable selectedLabel = 'all' planeIndexUIGroup = null distanceWidget = null distanceUpdateInProgress = false distanceEnabled = false @observable distancePoint1 = 0.0 @observable distancePoint2 = 0.0 } class GeometriesUIStore { constructor(eventEmitter) { this.eventEmitter = eventEmitter } eventEmitter = null @observable.shallow geometries = [] initialized = false sources = [] representationProxies = [] @observable selectedGeometryIndex = 0 @observable names = [] @observable representations = [] @observable colorMaps = [] @observable colorBy = [] @observable colors = [] @observable opacities = [] @observable colorRanges = new Map() colorRangesReactions = new Map() @computed get hasScalars() { return this.geometries.map(geometry => { const pointDataScalars = !!geometry.getPointData().getScalars() const cellDataScalars = !!geometry.getCellData().getScalars() return pointDataScalars || cellDataScalars }) } @computed get hasOnlyDirectColors() { return this.geometries.map(geometry => { const pointDataScalars = geometry.getPointData().getScalars() const pointDataDirectColors = !!pointDataScalars && pointDataScalars.getDataType() === VtkDataTypes.UNSIGNED_CHAR && pointDataScalars.getNumberOfComponents() === 3 const cellDataScalars = geometry.getCellData().getScalars() const cellDataDirectColors = !!cellDataScalars && cellDataScalars.getDataType() === VtkDataTypes.UNSIGNED_CHAR && cellDataScalars.getNumberOfComponents() === 3 return pointDataDirectColors && cellDataDirectColors }) } @computed get colorByOptions() { return this.geometries.map((geometry, index) => { if (!this.hasScalars[index]) { return null } const options = [].concat( geometry .getPointData() .getArrays() .map(a => ({ label: `Points: ${a.getName()}`, value: `pointData:${a.getName()}`, })), geometry .getCellData() .getArrays() .map(a => ({ label: `Cells: ${a.getName()}`, value: `cellData:${a.getName()}`, })) ) return options }) } @computed get colorByDefault() { return this.geometries.map((geometry, index) => { if (!this.hasScalars[index]) { return null } const pointData = geometry.getPointData() if (!!pointData.getScalars()) { const activeIndex = pointData.getActiveScalars() const activeArray = pointData.getArrays()[activeIndex] return observable({ label: `Points: ${activeArray.getName()}`, value: `pointData:${activeArray.getName()}`, }) } const cellData = geometry.getCellData() if (!!cellData.getScalars()) { const activeIndex = cellData.getActiveScalars() const activeArray = cellData.getArrays()[activeIndex] return observable({ label: `Cells: ${activeArray.getName()}`, value: `cellData:${activeArray.getName()}`, }) } throw new Error('Should not reach here.') }) } @computed get selectedColorRange() { const geometryIndex = this.selectedGeometryIndex if (!this.hasScalars[geometryIndex]) { return null } const colorByKey = this.colorBy[geometryIndex].value return this.colorRanges.get(geometryIndex).get(colorByKey) } @computed get selectedLookupTableProxy() { const geometryIndex = this.selectedGeometryIndex if (!this.hasScalars[geometryIndex]) { return null } const proxy = this.representationProxies[geometryIndex] const [colorByArrayName, location] = proxy.getColorBy() return proxy.getLookupTableProxy(colorByArrayName, location) } } class PointSetsUIStore { constructor(eventEmitter) { this.eventEmitter = eventEmitter } eventEmitter = null @observable.shallow pointSets = [] initialized = false sources = [] representationProxies = [] @observable selectedPointSetIndex = 0 @observable names = [] @observable representations = [] @observable colorMaps = [] @observable colorBy = [] @observable colors = [] @observable opacities = [] @observable sizes = [] @observable colorRanges = new Map() colorRangesReactions = new Map() lengthPixelRatio = 0.1 @computed get hasScalars() { return this.pointSets.map(pointSet => { const pointData = pointSet.getPointData() const hasPointDataScalars = !!pointData.getScalars() return hasPointDataScalars }) } @computed get colorByOptions() { return this.pointSets.map((pointSet, index) => { if (!this.hasScalars[index]) { return null } const options = [].concat( pointSet .getPointData() .getArrays() .map(a => ({ label: `${a.getName()}`, value: `pointData:${a.getName()}`, })) ) return options }) } @computed get colorByDefault() { return this.pointSets.map((pointSet, index) => { if (!this.hasScalars[index]) { return null } const pointData = pointSet.getPointData() if (!!pointData.getScalars()) { const activeIndex = pointData.getActiveScalars() const activeArray = pointData.getArrays()[activeIndex] return { label: `${activeArray.getName()}`, value: `pointData:${activeArray.getName()}`, } } throw new Error('Should not reach here.') }) } @computed get selectedColorRange() { const selectedIndex = this.selectedPointSetIndex if (!this.hasScalars[selectedIndex]) { return null } const colorByKey = this.colorBy[selectedIndex].value return this.colorRanges.get(selectedIndex).get(colorByKey) } @computed get selectedLookupTableProxy() { const selectedIndex = this.selectedPointSetIndex if (!this.hasScalars[selectedIndex]) { return null } const proxy = this.representationProxies[selectedIndex] const [colorByArrayName, location] = proxy.getColorBy() return proxy.getLookupTableProxy(colorByArrayName, location) } } class ViewerStore { constructor(proxyManager) { this.eventEmitter = new EventEmitter() this.mainUI = new MainUIStore(this.eventEmitter) this.imageUI = new ImageUIStore(this.eventEmitter) this.geometriesUI = new GeometriesUIStore(this.eventEmitter) this.pointSetsUI = new PointSetsUIStore(this.eventEmitter) this.id = 'itk-vtk-viewer-' + performance .now() .toString() .replace('.', '') this.proxyManager = proxyManager this.itkVtkView = proxyManager.createProxy('Views', 'ItkVtkView') this.container = document.createElement('div') this.itkVtkView.setContainer(this.container) //this.imageUI.source = proxyManager.createProxy( //'Sources', //'TrivialProducer', //{ name: 'Image' } //) } eventEmitter = null container = null id = 'itk-vtk-viewer' proxyManager = null itkVtkView = null get renderWindow() { return this.itkVtkView.getRenderWindow() } @computed get isBackgroundDark() { const backgroundColor = this.style.backgroundColor return backgroundColor[0] + backgroundColor[1] + backgroundColor[2] < 1.5 } @observable style = { backgroundColor: [0.5, 0.5, 0.5], containerStyle: STYLE_RENDERING_VIEW_CONTAINER, } mainUI = null imageUI = null geometriesUI = null pointSetsUI = null } export default ViewerStore ================================================ FILE: src/createViewer.js ================================================ import structuredClone from 'core-js/actual/structured-clone' if (!('structuredClone' in window)) { // Attach the polyfill as a Global function window.structuredClone = structuredClone } import { mat4 } from 'gl-matrix' import { inspect } from '@xstate/inspect' import { interpret } from 'xstate' import vtkProxyManager from 'vtk.js/Sources/Proxy/Core/ProxyManager' import ResizeSensor from 'css-element-queries/src/ResizeSensor' import proxyConfiguration from './Rendering/VTKJS/proxyManagerConfiguration' import UserInterface from './UserInterface' import addKeyboardShortcuts from './UI/addKeyboardShortcuts' import rgb2hex from './UserInterface/rgb2hex' import hex2rgb from './UserInterface/hex2rgb' import ViewerStore from './ViewerStore' import toMultiscaleSpatialImage from './IO/toMultiscaleSpatialImage' import { worldBoundsToIndexBounds } from './IO/MultiscaleSpatialImage' import viewerMachineOptions from './viewerMachineOptions' import createViewerMachine from './createViewerMachine' import ViewerMachineContext from './Context/ViewerMachineContext' import { addCroppingPlanes, getCropWidgetBounds, updateCroppingParameters, } from './Rendering/VTKJS/Main/croppingPlanes' import { reaction, toJS } from 'mobx' import PQueue from 'p-queue' const createViewer = async ( rootContainer, { image, imageName = undefined, labelImage, fixedImage, compare, geometries, pointSets, use2D = undefined, // if undefined, use image dimension if exists rotate = true, config, gradientOpacity, } ) => { UserInterface.emptyContainer(rootContainer) if (!UserInterface.checkForWebGL(rootContainer)) { throw new Error('WebGL could not be loaded.') } const proxyManager = vtkProxyManager.newInstance({ proxyConfiguration }) window.addEventListener('resize', proxyManager.resizeAllViews) const store = new ViewerStore(proxyManager) const publicAPI = {} const debug = false if (debug) { //const stateIFrame = document.createElement('iframe') //store.container.style.height = '50%' //stateIFrame.style.height = '50%' //rootContainer.appendChild(stateIFrame) inspect({ //iframe: stateIFrame, iframe: false, }) } const eventEmitter = store.eventEmitter function eventEmitterCallback(context /*, event*/) { return (callback, onReceive) => { onReceive(event => { switch (event.type) { case 'SET_BACKGROUND_COLOR': eventEmitter.emit('backgroundColorChanged', event.data) break case 'TOGGLE_BACKGROUND_COLOR': eventEmitter.emit( 'backgroundColorChanged', context.main.backgroundColor ) break case 'TOGGLE_FULLSCREEN': eventEmitter.emit( 'toggleFullscreen', publicAPI.getFullscreenEnabled() ) break case 'TOGGLE_UI_COLLAPSED': eventEmitter.emit('toggleUICollapsed', event.data) break case 'TOGGLE_ROTATE': eventEmitter.emit('toggleRotate', event.data) break case 'TOGGLE_ANNOTATIONS': eventEmitter.emit( 'toggleAnnotations', publicAPI.getAnnotationsEnabled() ) break case 'TOGGLE_AXES': eventEmitter.emit('toggleAxes', event.data) break case 'TOGGLE_IMAGE_INTERPOLATION': eventEmitter.emit('toggleImageInterpolation', event.data) break case 'TOGGLE_CROPPING_PLANES': eventEmitter.emit('toggleCroppingPlanes', event.data) break case 'RESET_CROPPING_PLANES': eventEmitter.emit('resetCroppingPlanes', event.data) break case 'CROPPING_PLANES_CHANGED': eventEmitter.emit('croppingPlanesChanged', event.data) break case 'VIEW_MODE_CHANGED': eventEmitter.emit('viewModeChanged', event.data) break case 'TOGGLE_LAYER_VISIBILITY': eventEmitter.emit('toggleLayerVisibility', event.data) break case 'RENDERED_IMAGE_ASSIGNED': eventEmitter.emit('renderedImageAssigned', event.data) break case 'IMAGE_COMPONENT_VISIBILITY_CHANGED': eventEmitter.emit('imageVisualizedComponentChanged', event.data) break case 'IMAGE_PIECEWISE_FUNCTION_GAUSSIANS_CHANGED': eventEmitter.emit( 'imagePiecewiseFunctionGaussiansChanged', event.data ) break case 'IMAGE_PIECEWISE_FUNCTION_POINTS_CHANGED': eventEmitter.emit('imagePiecewiseFunctionPointsChanged', event.data) break case 'IMAGE_COLOR_RANGE_CHANGED': eventEmitter.emit('imageColorRangeChanged', event.data) break case 'IMAGE_COLOR_RANGE_BOUNDS_CHANGED': eventEmitter.emit('imageColorRangeBoundsChanged', event.data) break case 'IMAGE_COLOR_MAP_CHANGED': eventEmitter.emit('imageColorMapChanged', event.data) break case 'TOGGLE_IMAGE_SHADOW': eventEmitter.emit('toggleImageShadow', event.data) break case 'IMAGE_GRADIENT_OPACITY_CHANGED': eventEmitter.emit('imageGradientOpacityChanged', event.data) break case 'IMAGE_GRADIENT_OPACITY_SCALE_CHANGED': eventEmitter.emit('imageGradientOpacityScaleChanged', event.data) break case 'IMAGE_VOLUME_SAMPLE_DISTANCE_CHANGED': eventEmitter.emit('imageVolumeSampleDistanceChanged', event.data) break case 'IMAGE_BLEND_MODE_CHANGED': eventEmitter.emit('imageBlendModeChanged', event.data) break case 'LABEL_IMAGE_LOOKUP_TABLE_CHANGED': eventEmitter.emit('labelImageLookupTableChanged', event.data) break case 'LABEL_IMAGE_BLEND_CHANGED': eventEmitter.emit('labelImageBlendChanged', event.data) break case 'LABEL_IMAGE_WEIGHTS_CHANGED': eventEmitter.emit('labelImageWeightsChanged', event.data) break case 'LABEL_IMAGE_LABEL_NAMES_CHANGED': eventEmitter.emit('labelImageLabelNamesChanged', event.data) break case 'X_SLICE_CHANGED': eventEmitter.emit('xSliceChanged', event.data) break case 'Y_SLICE_CHANGED': eventEmitter.emit('ySliceChanged', event.data) break case 'Z_SLICE_CHANGED': eventEmitter.emit('zSliceChanged', event.data) break case 'SCREENSHOT_TAKEN': eventEmitter.emit('screenshotTaken', event.data) break case 'TAKE_SCREENSHOT': break default: throw new Error(`Unexpected event type: ${event.type}`) } }) } } const context = new ViewerMachineContext(config) const options = { ...viewerMachineOptions } if (context.uiMachineOptions !== 'reference') { const uiMachineOptions = context.uiMachineOptions if (uiMachineOptions.href) { const loadedUIMachineOptions = await import( /* webpackIgnore: true */ uiMachineOptions.href ) if (uiMachineOptions.export) { options.ui = loadedUIMachineOptions[uiMachineOptions.export] } else { options.ui = loadedUIMachineOptions.default } } else if (context.uiMachineOptions === 'pydata-sphinx') { options.ui = { href: 'https://cdn.jsdelivr.net/npm/itk-viewer-bootstrap-ui@0/dist/bootstrapUIMachineOptions.js.es.js', export: 'default', } } else if (context.uiMachineOptions === 'mui') { options.ui = { href: 'https://cdn.jsdelivr.net/npm/itk-viewer-material-ui@0/dist/materialUIMachineOptions.js.es.js', export: 'default', } } else { options.ui = uiMachineOptions } } const imageMultiscale = image && (await toMultiscaleSpatialImage(image, false, context.maxConcurrency)) const labelImageMultiscale = labelImage && (await toMultiscaleSpatialImage(labelImage, true, context.maxConcurrency)) const imageOrLabelImage = imageMultiscale || labelImageMultiscale context.use2D = use2D ?? Boolean(imageOrLabelImage && imageOrLabelImage.imageType.dimension === 2) context.rootContainer = rootContainer // Todo: move to viewer machine context.container = store.container // Todo: move to VTKJS/createRenderer context.itkVtkView = store.itkVtkView context.proxyManager = store.proxyManager context.renderWindow = store.renderWindow context.id = store.id const machine = createViewerMachine(options, context, eventEmitterCallback) const service = interpret(machine, { devTools: debug }) context.service = service service.start() reaction( () => !!store.geometriesUI.geometries && store.geometriesUI.geometries.slice(), geometries => { if (!geometries || geometries.length === 0) { return } geometries.forEach((geometry, index) => { if (store.geometriesUI.sources.length <= index) { const uid = `GeometrySource${index}` const geometrySource = proxyManager.createProxy( 'Sources', 'TrivialProducer', { name: uid, } ) store.geometriesUI.sources.push(geometrySource) store.geometriesUI.sources[index].setInputData(geometry) proxyManager.createRepresentationInAllViews(geometrySource) const geometryRepresentation = proxyManager.getRepresentation( geometrySource, store.itkVtkView ) store.geometriesUI.representationProxies.push(geometryRepresentation) addCroppingPlanes(context, geometryRepresentation) } else { store.geometriesUI.sources[index].setInputData(geometry) store.geometriesUI.representationProxies[index].setVisibility(true) } }) updateCroppingParameters(context) if (geometries.length < store.geometriesUI.representationProxies.length) { const proxiesToDisable = store.geometriesUI.representationProxies.slice( geometries.length ) proxiesToDisable.forEach(proxy => { proxy.setVisibility(false) }) } if (!store.geometriesUI.initialized) { UserInterface.createGeometriesUI(store, context.uiContainer) } store.geometriesUI.names = geometries.map( (geometry, index) => `Geometry ${index}` ) let representations = store.geometriesUI.representations.slice( 0, geometries.length ) const defaultGeometryRepresentations = new Array(geometries.length) defaultGeometryRepresentations.fill('Surface') representations.concat( defaultGeometryRepresentations.slice( 0, geometries.length - representations.length ) ) store.geometriesUI.representations = representations } ) store.geometriesUI.geometries = geometries reaction( () => !!store.pointSetsUI.pointSets && store.pointSetsUI.pointSets.slice(), pointSets => { if (!pointSets || pointSets.length === 0) { return } pointSets.forEach((pointSet, index) => { if (store.pointSetsUI.sources.length <= index) { const uid = `PointSetSource${index}` const pointSetSource = proxyManager.createProxy( 'Sources', 'TrivialProducer', { name: uid, } ) store.pointSetsUI.sources.push(pointSetSource) store.pointSetsUI.sources[index].setInputData(pointSet) const pointSetRepresentationUid = `pointSetRepresentation${index}` const pointSetRepresentation = proxyManager.createProxy( 'Representations', 'PointSet', { name: pointSetRepresentationUid, } ) pointSetRepresentation.setInput(pointSetSource) pointSetRepresentation.setRadiusFactor( store.pointSetsUI.lengthPixelRatio ) store.itkVtkView.addRepresentation(pointSetRepresentation) store.pointSetsUI.representationProxies.push(pointSetRepresentation) addCroppingPlanes(context, pointSetRepresentation) } else { store.pointSetsUI.sources[index].setInputData(pointSet) store.pointSetsUI.representationProxies[index].setVisibility(true) } }) updateCroppingParameters(context) if (pointSets.length < store.pointSetsUI.representationProxies.length) { const proxiesToDisable = store.pointSetsUI.representationProxies.slice( pointSets.length ) proxiesToDisable.forEach(proxy => { proxy.setVisibility(false) }) } if (!store.pointSetsUI.initialized) { UserInterface.createPointSetsUI(store, context.uiContainer) } } ) store.pointSetsUI.pointSets = pointSets store.itkVtkView.resize() // eslint-disable-next-line no-unused-vars const resizeSensor = new ResizeSensor(store.container, function() { store.itkVtkView.resize() }) proxyManager.renderAllViews() setTimeout(() => { store.itkVtkView.resetCamera() // Estimate a reasonable point sphere radius in pixels const lengthPixelRatio = store.itkVtkView.getLengthPixelRatio() store.pointSetsUI.lengthPixelRatio = lengthPixelRatio store.pointSetsUI.representationProxies.forEach(proxy => { proxy.setRadiusFactor(lengthPixelRatio) }) }, 1) UserInterface.addLogo(store) publicAPI.render = () => { service.send('RENDER') } // The `store` is considered an internal implementation detail // and its interface and behavior may change without changes to the major version. publicAPI.getStore = () => { return store } publicAPI.setPointSets = pointSets => { store.pointSetsUI.pointSets = pointSets } publicAPI.setGeometries = geometries => { store.geometriesUI.geometries = geometries } const eventNames = [ 'toggleUICollapsed', 'backgroundColorChanged', 'toggleFullscreen', 'toggleAnnotations', 'toggleAxes', 'toggleRotate', 'toggleCroppingPlanes', 'croppingPlanesChanged', 'resetCroppingPlanes', 'viewModeChanged', 'xSliceChanged', 'ySliceChanged', 'zSliceChanged', 'toggleLayerVisibility', 'imagePicked', 'imagePiecewiseFunctionGaussiansChanged', 'imagePiecewiseFunctionPointsChanged', 'imageVisualizedComponentChanged', 'toggleImageInterpolation', 'imageColorRangeChanged', 'imageColorRangeBoundsChanged', 'imageColorMapChanged', 'toggleImageShadow', 'imageGradientOpacityChanged', 'imageGradientOpacityScaleChanged', 'imageVolumeSampleDistanceChanged', 'imageBlendModeChanged', 'labelImageLookupTableChanged', 'labelImageBlendChanged', 'labelImageLabelNamesChanged', 'labelImageWeightsChanged', 'pointSetColorChanged', 'pointSetOpacityChanged', 'pointSetSizeChanged', 'pointSetRepresentationChanged', 'screenshotTaken', ] publicAPI.getEventNames = () => eventNames publicAPI.on = (...onArgs) => eventEmitter.on(...onArgs) publicAPI.off = (...offArgs) => eventEmitter.off(...offArgs) publicAPI.once = (...onceArgs) => eventEmitter.once(...onceArgs) publicAPI.getEventEmitter = () => eventEmitter publicAPI.getConfig = () => { return context.getConfig() } publicAPI.setUICollapsed = collapse => { if (collapse !== context.uiCollapsed) { service.send('TOGGLE_UI_COLLAPSED') } } publicAPI.getUICollapsed = () => { return context.uiCollapsed } publicAPI.setRenderingViewContainerStyle = containerStyle => { service.send({ type: 'STYLE_RENDERING_VIEW_CONTAINER', data: containerStyle, }) } publicAPI.getRenderingViewContainerStyle = () => { return { ...context.renderingViewContainerStyle } } reaction( () => { return store.imageUI.lastPickedValues }, () => { const lastPickedValues = store.imageUI.lastPickedValues eventEmitter.emit('imagePicked', toJS(lastPickedValues)) } ) publicAPI.setBackgroundColor = bgColor => { service.send({ type: 'SET_BACKGROUND_COLOR', data: bgColor }) } publicAPI.getBackgroundColor = () => { return context.main.backgroundColor.slice() } publicAPI.setUnits = units => { service.send({ type: 'SET_UNITS', data: units }) } publicAPI.getUnits = () => { return context.main.units } // Gaussians not supported publicAPI.setImagePiecewiseFunctionGaussians = ( gaussians, component, name ) => { if (typeof name === 'undefined') { name = context.images.selectedName } if (typeof component === 'undefined') { component = 0 } service.send({ type: 'IMAGE_PIECEWISE_FUNCTION_GAUSSIANS_CHANGED', data: { name, component, gaussians }, }) } // Gaussians not supported publicAPI.getImagePiecewiseFunctionGaussians = (component, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } if (typeof component === 'undefined') { component = 0 } const actorContext = context.images.actorContext.get(name) return actorContext.piecewiseFunctionGaussians.get(component) } publicAPI.setImagePiecewiseFunctionPoints = (points, component, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } if (typeof component === 'undefined') { component = 0 } service.send({ type: 'IMAGE_PIECEWISE_FUNCTION_POINTS_CHANGED', data: { name, component, points }, }) } publicAPI.getImagePiecewiseFunctionPoints = (component, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } if (typeof component === 'undefined') { component = 0 } const actorContext = context.images.actorContext.get(name) return actorContext.piecewiseFunctionPoints.get(component) } // Start collapsed on mobile devices or small pages if ( (config && typeof config.uiCollapsed !== 'undefined' && window.screen.availWidth < 768) || window.screen.availHeight < 800 ) { publicAPI.setUICollapsed(true) } publicAPI.captureImage = () => { return store.itkVtkView.captureImage() } publicAPI.setAnnotationsEnabled = enabled => { if (enabled !== context.main.annotationsEnabled) { service.send('TOGGLE_ANNOTATIONS') } } publicAPI.getAnnotationsEnabled = () => { return context.main.annotationsEnabled } publicAPI.setAxesEnabled = enabled => { if (enabled !== context.main.axesEnabled) { service.send('TOGGLE_AXES') } } publicAPI.getAxesEnabled = () => { return context.main.axesEnabled } publicAPI.setRotateEnabled = enabled => { if (enabled !== context.main.rotateEnabled) { service.send('TOGGLE_ROTATE') } } publicAPI.getRotateEnabled = () => { return context.main.rotateEnabled } publicAPI.setFullscreenEnabled = enabled => { if (enabled !== context.main.fullscreenEnabled) { service.send('TOGGLE_FULLSCREEN') } } publicAPI.getFullscreenEnabled = () => { return context.main.fullscreenEnabled } publicAPI.setViewMode = mode => { if (mode !== context.main.viewMode) { service.send({ type: 'VIEW_MODE_CHANGED', data: mode }) } } publicAPI.getViewMode = () => { return context.main.viewMode } publicAPI.setXSlice = position => { service.send({ type: 'X_SLICE_CHANGED', data: position, }) } publicAPI.getXSlice = () => { return context.main.xSlice } publicAPI.setYSlice = position => { service.send({ type: 'Y_SLICE_CHANGED', data: position, }) } publicAPI.getYSlice = () => { return context.main.ySlice } publicAPI.setZSlice = position => { service.send({ type: 'Z_SLICE_CHANGED', data: position, }) } publicAPI.getZSlice = () => { return context.main.zSlice } publicAPI.getLayerNames = () => { return Array.from(context.layers.actorContext.keys()) } publicAPI.setLayerVisibility = (visible, name) => { const actorContext = context.layers.actorContext.get(name) if (visible !== actorContext.visible) { context.service.send({ type: 'TOGGLE_LAYER_VISIBILITY', data: name }) } } publicAPI.getLayerVisibility = name => { return context.layers.actorContext.get(name).visible } publicAPI.selectLayer = name => { context.service.send({ type: 'SELECT_LAYER', data: name }) } // A shared API queue lets setCompareImage wait for setImage. // Otherwise imageActorContext setting events won't yet have a actor machine to receive them. const apiFunctionQueue = new PQueue({ concurrency: 1 }) const queueApi = funcToQueue => (...args) => apiFunctionQueue.add(() => funcToQueue(...args)) // Queueing setImage syncs the order setImage(s) are called with the order image actorContexts are created, no matter the data passed. // Some images take longer with toMultiscaleSpatialImage, then get sent to state machine later, even if they were called with viewer.setImage first. // The last added image is the context.image.selectedImage publicAPI.setImage = queueApi(async (image, imageName) => { const name = imageName ?? image.name ?? context.images?.selectedName ?? 'Image' const multiscaleImage = await toMultiscaleSpatialImage( image, false, context.maxConcurrency ) multiscaleImage.name = name if (context.images.actorContext.has(name)) { const actorContext = context.images.actorContext.get(name) actorContext.image = multiscaleImage service.send({ type: 'IMAGE_ASSIGNED', data: name }) } else { service.send({ type: 'ADD_IMAGE', data: multiscaleImage }) } }) publicAPI.getImage = name => { if (typeof name === 'undefined' && context.images.selectedName) { name = context.images.selectedName } return context.images.actorContext.get(name).image } publicAPI.getImageInterpolationEnabled = name => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) return actorContext.interpolationEnabled } publicAPI.setImageInterpolationEnabled = (enabled, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } const currentEnabled = publicAPI.getImageInterpolationEnabled(name) if (enabled !== currentEnabled) { service.send({ type: 'TOGGLE_IMAGE_INTERPOLATION', data: name }) } } publicAPI.setImageComponentVisibility = (visibility, component, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } service.send({ type: 'IMAGE_COMPONENT_VISIBILITY_CHANGED', data: { name, component, visibility }, }) } publicAPI.getImageComponentVisibility = (component, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) return actorContext.componentVisibilities[component] } publicAPI.setCroppingPlanesEnabled = enabled => { if (enabled !== context.main.croppingPlanesEnabled) { service.send('TOGGLE_CROPPING_PLANES') } } publicAPI.getCroppingPlanesEnabled = () => { return context.main.croppingPlanesEnabled } publicAPI.resetCroppingPlanes = () => { service.send('RESET_CROPPING_PLANES') } publicAPI.getCroppingPlanes = () => { return context.main.croppingPlanes } publicAPI.setCroppingPlanes = croppingPlanes => { service.send({ type: 'CROPPING_PLANES_CHANGED', data: croppingPlanes, }) } publicAPI.setImageColorRange = (range, component, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } if (typeof component === 'undefined') { component = 0 } const actorContext = context.images.actorContext.get(name) const currentRange = actorContext.colorRanges.get(component) if ( typeof currentRange !== 'undefined' || currentRange[0] !== range[0] || currentRange[1] !== range[1] ) { service.send({ type: 'IMAGE_COLOR_RANGE_CHANGED', data: { name, component, range }, }) } } publicAPI.getImageColorRange = (component, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } if (typeof component === 'undefined') { component = 0 } const actorContext = context.images.actorContext.get(name) return actorContext.colorRanges.get(component) } publicAPI.setImageColorRangeMin = (value, component, name) => { const selectedComponent = component ?? 0 const selectedName = name ?? context.images.selectedName service.send({ type: 'IMAGE_COLOR_RANGE_MIN_CHANGED', data: { name: selectedName, component: selectedComponent, value }, }) } publicAPI.setImageColorRangeMax = (value, component, name) => { const selectedComponent = component ?? 0 const selectedName = name ?? context.images.selectedName service.send({ type: 'IMAGE_COLOR_RANGE_MAX_CHANGED', data: { name: selectedName, component: selectedComponent, value }, }) } publicAPI.setImageColorRangeBounds = (range, component, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } if (typeof component === 'undefined') { component = 0 } const actorContext = context.images.actorContext.get(name) const currentRange = actorContext.colorRanges.get(component) if ( typeof currentRange !== 'undefined' || currentRange[0] !== range[0] || currentRange[1] !== range[1] ) { service.send({ type: 'IMAGE_COLOR_RANGE_BOUNDS_CHANGED', data: { name, component, range }, }) } } publicAPI.getImageColorRangeBounds = (component, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } if (typeof component === 'undefined') { component = 0 } const actorContext = context.images.actorContext.get(name) return actorContext.colorRangeBounds.get(component) } publicAPI.setImageColorMap = (colorMap, componentIndex, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } if (typeof componentIndex === 'undefined') { componentIndex = 0 } const actorContext = context.images.actorContext.get(name) const currentColorMap = actorContext.colorMaps.get(componentIndex) if ( typeof currentColorMap !== 'undefined' || currentColorMap[0] !== colorMap[0] || currentColorMap[1] !== colorMap[1] ) { service.send({ type: 'IMAGE_COLOR_MAP_CHANGED', data: { name, component: componentIndex, colorMap }, }) } } publicAPI.getImageColorMap = (componentIndex, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } if (typeof componentIndex === 'undefined') { componentIndex = 0 } const actorContext = context.images.actorContext.get(name) return actorContext.colorMaps.get(componentIndex) } publicAPI.setLabelImage = queueApi(async (labelImage, layerImageName) => { const multiscaleLabelImage = await toMultiscaleSpatialImage( labelImage, true, context.maxConcurrency ) if (multiscaleLabelImage.name === 'Image') { multiscaleLabelImage.name = 'LabelImage' } const imageName = layerImageName ?? context.images.selectedName ?? multiscaleLabelImage.name const actorContext = context.images.actorContext.get(imageName) if (actorContext?.labelImageName === multiscaleLabelImage.name) { actorContext.labelImage = multiscaleLabelImage service.send({ type: 'LABEL_IMAGE_ASSIGNED', data: imageName }) } else { service.send({ type: 'ADD_LABEL_IMAGE', data: { imageName, labelImage: multiscaleLabelImage }, }) } publicAPI.setImageInterpolationEnabled(false, imageName) }) publicAPI.getLabelImage = () => { const name = context.images.selectedName return context.images.actorContext.get(name).labelImage } publicAPI.setLabelImageLookupTable = (lookupTable, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) const currentLookupTable = actorContext.lookupTable if (currentLookupTable !== lookupTable) { service.send({ type: 'LABEL_IMAGE_LOOKUP_TABLE_CHANGED', data: { name, lookupTable }, }) } } publicAPI.getLabelImageLookupTable = name => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) return actorContext.lookupTable } publicAPI.setLabelImageBlend = (blend, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) const currentBlend = actorContext.labelImageBlend if (currentBlend !== blend) { service.send({ type: 'LABEL_IMAGE_BLEND_CHANGED', data: { name, labelImageBlend: blend }, }) } } publicAPI.getLabelImageBlend = name => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) return actorContext.labelImageBlend } publicAPI.setLabelImageLabelNames = (names, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) const currentLabelNames = actorContext.labelNames if (currentLabelNames !== names) { service.send({ type: 'LABEL_IMAGE_LABEL_NAMES_CHANGED', data: { name, labelNames: names }, }) } } publicAPI.getLabelImageLabelNames = name => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) return actorContext.labelNames } publicAPI.setLabelImageWeights = (weights, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) const currentWeights = actorContext.labelImageWeights if (currentWeights !== weights) { service.send({ type: 'LABEL_IMAGE_WEIGHTS_CHANGED', data: { name, labelImageWeights: weights }, }) } } publicAPI.getLabelImageWeights = name => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) return actorContext.labelImageWeights } // Moving image must have been added last. // See index.md for parameter docs. publicAPI.setCompareImages = queueApi( async (fixedImageName, movingImageName, options) => { service.send({ type: 'COMPARE_IMAGES', data: { name: movingImageName, fixedImageName, options, }, }) } ) publicAPI.getCompareImages = name => { if (typeof name === 'undefined') { name = context.images.selectedName } return context.images.actorContext.get(name).compare } publicAPI.setImageShadowEnabled = (enabled, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) if (enabled !== actorContext.shadowEnabled) { service.send({ type: 'TOGGLE_IMAGE_SHADOW', data: name, }) } } publicAPI.getImageShadowEnabled = name => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) return actorContext.shadowEnabled } publicAPI.setImageGradientOpacity = (opacity, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } service.send({ type: 'IMAGE_GRADIENT_OPACITY_CHANGED', data: { name, gradientOpacity: opacity }, }) } publicAPI.getImageGradientOpacity = name => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) return actorContext.gradientOpacity } publicAPI.setImageGradientOpacityScale = (min, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } service.send({ type: 'IMAGE_GRADIENT_OPACITY_SCALE_CHANGED', data: { name, gradientOpacityScale: min }, }) } publicAPI.getImageGradientOpacityScale = name => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) return actorContext.gradientOpacityScale } publicAPI.setImageVolumeSampleDistance = (distance, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } service.send({ type: 'IMAGE_VOLUME_SAMPLE_DISTANCE_CHANGED', data: { name, volumeSampleDistance: distance }, }) } publicAPI.getImageVolumeSampleDistance = name => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) return actorContext.volumeSampleDistance } publicAPI.setImageBlendMode = (mode, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } service.send({ type: 'IMAGE_BLEND_MODE_CHANGED', data: { name, blendMode: mode }, }) } publicAPI.getImageBlendMode = name => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) return actorContext.blendMode } publicAPI.addPointSet = pointSet => { if (!store.pointSetsUI.pointSets) { store.pointSetsUI.pointSets = [] } store.pointSetsUI.pointSets.push(pointSet) } reaction( () => { return store.pointSetsUI.colors.slice() }, colors => { const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex const color = colors[selectedPointSetIndex] eventEmitter.emit('pointSetColorChanged', selectedPointSetIndex, color) } ) publicAPI.setPointSetColor = (index, rgbColor) => { const hexColor = rgb2hex(rgbColor) if (index < store.pointSetsUI.colors.length) { store.pointSetsUI.colors[index] = hexColor } } publicAPI.getPointSetColor = index => { const hexColor = store.pointSetsUI.colors[index] const rgbColor = hex2rgb(hexColor) return rgbColor } reaction( () => { return store.pointSetsUI.opacities.slice() }, opacities => { const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex const opacity = opacities[selectedPointSetIndex] eventEmitter.emit( 'pointSetOpacityChanged', selectedPointSetIndex, opacity ) } ) publicAPI.setPointSetOpacity = (index, opacity) => { if (index < store.pointSetsUI.opacities.length) { store.pointSetsUI.opacities[index] = opacity } } publicAPI.getPointSetOpacity = index => { return store.pointSetsUI.opacities[index] } reaction( () => { return store.pointSetsUI.sizes.slice() }, sizes => { const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex const size = sizes[selectedPointSetIndex] eventEmitter.emit('pointSetSizeChanged', selectedPointSetIndex, size) } ) publicAPI.setPointSetSize = (index, size) => { if (index < store.pointSetsUI.sizes.length) { store.pointSetsUI.sizes[index] = size } } publicAPI.getPointSetSize = index => { return store.pointSetsUI.sizes[index] } reaction( () => { return store.pointSetsUI.representations.slice() }, representations => { const selectedPointSetIndex = store.pointSetsUI.selectedPointSetIndex const representation = representations[selectedPointSetIndex] eventEmitter.emit( 'pointSetRepresentationChanged', selectedPointSetIndex, representation ) } ) publicAPI.setPointSetRepresentation = (index, representation) => { if (index < store.pointSetsUI.representations.length) { store.pointSetsUI.representations[index] = representation } } publicAPI.setGeometryColor = (index, rgbColor) => { const hexColor = rgb2hex(rgbColor) store.geometriesUI.colors[index] = hexColor } publicAPI.setGeometryOpacity = (index, opacity) => { store.geometriesUI.opacities[index] = opacity } publicAPI.setImageScale = targetScale => { service.send({ type: 'SET_IMAGE_SCALE', targetScale, }) } // The `itkVtkView` is considered an internal implementation detail // and its interface and behavior may change without changes to the major version. publicAPI.getViewProxy = () => { return store.itkVtkView } publicAPI.setImageVolumeScatteringBlend = (scatteringBlend, name) => { if (typeof name === 'undefined') { name = context.images.selectedName } service.send({ type: 'SET_CINEMATIC_PARAMETERS', data: { name: context.images.selectedName, params: { scatteringBlend }, }, }) } publicAPI.getImageVolumeScatteringBlend = name => { if (typeof name === 'undefined') { name = context.images.selectedName } const actorContext = context.images.actorContext.get(name) return actorContext.cinematicParameters.scatteringBlend } publicAPI.setMaxConcurrency = value => { context.maxConcurrency = value } publicAPI.getMaxConcurrency = () => { return context.maxConcurrency } publicAPI.getLoadedScale = name => { const imageName = name ?? context.images.selectedName const actorContext = context.images.actorContext.get(imageName) return actorContext.loadedScale } publicAPI.getCroppedImageWorldBounds = () => { return getCropWidgetBounds(context) } publicAPI.getCroppedIndexBounds = async (scale, name) => { const imageName = name ?? context.images.selectedName const actorContext = context.images.actorContext.get(imageName) if (typeof scale === 'undefined' || scale < 0) { scale = actorContext.loadedScale } const image = actorContext.image const bounds = getCropWidgetBounds(context) const indexToWorld = await image.scaleIndexToWorld(scale) const fullIndexBounds = image.getIndexBounds(scale) return worldBoundsToIndexBounds({ bounds, fullIndexBounds, worldToIndex: mat4.invert([], indexToWorld), }) } addKeyboardShortcuts(context.uiContainer, service) // must come before moving/main image if (fixedImage) { await publicAPI.setImage(fixedImage, 'Fixed') // must await so fixedImage is the first one } if (imageMultiscale) { await publicAPI.setImage(imageMultiscale, imageName) // await for image.name to get assigned for fixedImage and setCompareImages } if (labelImageMultiscale) { publicAPI.setLabelImage(labelImageMultiscale, imageMultiscale?.name) } if (fixedImage && imageMultiscale) { publicAPI.setCompareImages('Fixed', imageMultiscale.name, compare) } if (!context.use2D) { publicAPI.setRotateEnabled(rotate) } // check with isNaN as may be 0 if (!isNaN(gradientOpacity)) { publicAPI.setImageGradientOpacity(gradientOpacity) } return publicAPI } export default createViewer ================================================ FILE: src/createViewerMachine.js ================================================ import { forwardTo, Machine } from 'xstate' import createRenderingMachine from './Rendering/createRenderingMachine' import createUIMachine from './UI/createUIMachine' const createViewerMachine = (options, context, eventEmitterCallback) => { const { ui, rendering } = options const renderingMachine = createRenderingMachine(rendering, context) const uiMachine = createUIMachine(ui, context) return Machine( { id: 'viewer', strict: true, initial: 'idle', context, states: { idle: { always: { target: 'active', actions: [ 'createRenderingViewContainers', 'styleRenderingViewContainers', ], }, }, active: { invoke: [ { id: 'ui', src: uiMachine, }, { id: 'rendering', src: renderingMachine, }, { id: 'eventEmitter', src: eventEmitterCallback, }, ], on: { STYLE_RENDERING_VIEW_CONTAINER: { actions: 'styleRenderingViewContainers', }, SET_BACKGROUND_COLOR: { actions: [forwardTo('rendering'), forwardTo('eventEmitter')], }, TOGGLE_BACKGROUND_COLOR: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, SET_UNITS: { actions: [forwardTo('rendering')], }, TOGGLE_DARK_MODE: { actions: forwardTo('ui'), }, TOGGLE_UI_COLLAPSED: { actions: [forwardTo('ui'), forwardTo('eventEmitter')], }, TOGGLE_FULLSCREEN: { actions: [forwardTo('ui'), forwardTo('eventEmitter')], }, DISABLE_FULLSCREEN: { actions: forwardTo('ui'), }, TAKE_SCREENSHOT: { actions: [forwardTo('rendering'), forwardTo('eventEmitter')], }, TOGGLE_ROTATE: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, TOGGLE_ANNOTATIONS: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, TOGGLE_AXES: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, TOGGLE_CROPPING_PLANES: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, RESET_CROPPING_PLANES: { actions: [forwardTo('rendering'), forwardTo('eventEmitter')], }, CROPPING_PLANES_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, CROPPING_PLANES_CHANGED_BY_USER: { actions: forwardTo('rendering'), }, VIEW_MODE_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, RESET_CAMERA: { actions: forwardTo('rendering'), }, SLICING_PLANES_CHANGED: { actions: [forwardTo('ui'), forwardTo('rendering')], }, X_SLICE_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, Y_SLICE_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, Z_SLICE_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, SELECT_LAYER: { actions: [forwardTo('ui'), forwardTo('rendering')], }, TOGGLE_LAYER_VISIBILITY: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, ADD_IMAGE: { actions: forwardTo('ui'), }, IMAGE_ASSIGNED: { actions: [forwardTo('ui'), forwardTo('rendering')], }, UPDATE_RENDERED_IMAGE: { actions: [forwardTo('rendering')], }, START_DATA_UPDATE: { actions: forwardTo('ui') }, FINISH_DATA_UPDATE: { actions: forwardTo('ui') }, POST_RENDER: { actions: forwardTo('ui') }, RENDERED_IMAGE_ASSIGNED: { actions: [forwardTo('ui'), forwardTo('eventEmitter')], }, IMAGE_RENDERING_ACTIVE: { actions: forwardTo('ui'), }, ADD_LABEL_IMAGE: { actions: forwardTo('ui'), }, LABEL_IMAGE_ASSIGNED: { actions: [forwardTo('ui'), forwardTo('rendering')], }, SELECT_IMAGE_COMPONENT: { actions: forwardTo('ui'), }, TOGGLE_IMAGE_INTERPOLATION: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, IMAGE_COMPONENT_VISIBILITY_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, IMAGE_PIECEWISE_FUNCTION_GAUSSIANS_CHANGED: { actions: [forwardTo('ui'), forwardTo('eventEmitter')], }, IMAGE_PIECEWISE_FUNCTION_CHANGED: { actions: [forwardTo('ui'), forwardTo('rendering')], }, IMAGE_PIECEWISE_FUNCTION_POINTS_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, IMAGE_COLOR_RANGE_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, IMAGE_COLOR_RANGE_MIN_CHANGED: { actions: [forwardTo('rendering')], }, IMAGE_COLOR_RANGE_MAX_CHANGED: { actions: [forwardTo('rendering')], }, IMAGE_COLOR_RANGE_POINTS_CHANGED: { actions: [forwardTo('rendering')], }, IMAGE_COLOR_RANGE_BOUNDS_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, IMAGE_COLOR_MAP_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, IMAGE_COLOR_MAP_DEPENDENCIES_UPDATE: { actions: forwardTo('ui'), }, TOGGLE_IMAGE_SHADOW: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, IMAGE_GRADIENT_OPACITY_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, IMAGE_GRADIENT_OPACITY_SCALE_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, IMAGE_VOLUME_SAMPLE_DISTANCE_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, IMAGE_BLEND_MODE_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, UPDATE_IMAGE_HISTOGRAM: { actions: [forwardTo('rendering')], }, IMAGE_HISTOGRAM_UPDATED: { actions: [forwardTo('ui')], }, LABEL_IMAGE_LOOKUP_TABLE_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, LABEL_IMAGE_BLEND_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, LABEL_IMAGE_WEIGHTS_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, LABEL_IMAGE_LABEL_NAMES_CHANGED: { actions: [ forwardTo('ui'), forwardTo('rendering'), forwardTo('eventEmitter'), ], }, LABEL_IMAGE_SELECTED_LABEL_CHANGED: { actions: [forwardTo('ui'), forwardTo('rendering')], }, RENDER: { actions: forwardTo('rendering'), }, UPDATE_FPS: { actions: forwardTo('rendering'), }, FPS_UPDATED: { actions: forwardTo('rendering'), }, SET_IMAGE_SCALE: { actions: forwardTo('rendering'), }, SET_CINEMATIC_PARAMETERS: { actions: forwardTo('rendering'), }, CINEMATIC_CHANGED: { actions: [forwardTo('ui'), forwardTo('rendering')], }, REQUEST_ANIMATION: { actions: forwardTo('rendering'), }, CANCEL_ANIMATION: { actions: forwardTo('rendering'), }, TOGGLE_DISTANCE_WIDGET: { actions: [forwardTo('ui'), forwardTo('rendering')], }, DISTANCE_WIDGET_VALUE_CHANGED: { actions: [forwardTo('ui')], }, SCREENSHOT_TAKEN: { actions: [forwardTo('eventEmitter')], }, SET_FIXED_IMAGE: { actions: forwardTo('rendering'), }, COMPARE_IMAGES: { actions: forwardTo('rendering'), }, COMPARE_UPDATED: { actions: forwardTo('ui'), }, ANIMATE_IMAGE_MIX: { actions: forwardTo('rendering'), }, COMPONENT_VISIBILITIES_UPDATED: { actions: forwardTo('ui'), }, WINDOW_LEVEL_TOGGLED: { actions: [forwardTo('ui'), forwardTo('rendering')], }, IMAGE_COLOR_RANGE_RESET: { actions: [forwardTo('ui'), forwardTo('rendering')], }, TOGGLE_LAYER_BBOX: { actions: [forwardTo('ui'), forwardTo('rendering')], }, DOWNLOAD_IMAGE: { actions: [forwardTo('rendering')], }, }, }, }, }, options ) } export default createViewerMachine ================================================ FILE: src/imJoyCodecs.js ================================================ import bloscZarrDecompress from './Compression/bloscZarrDecompress.js' import { ImageType, Image, bufferToTypedArray } from 'itk-wasm' async function decodeNumcodecEncoded(numcodecEncoded) { const forBloscZarr = [ { data: numcodecEncoded.buffer, metadata: { compressor: numcodecEncoded.config, dtype: '|u1', chunks: [numcodecEncoded.nbytes], }, }, ] const decompressed = await bloscZarrDecompress(forBloscZarr) return decompressed[0] } async function decodeItkWasmImage(wasmImage) { const wasmImageType = wasmImage.imageType const imageType = new ImageType( wasmImageType.dimension, wasmImageType.componentType, wasmImageType.pixelType, wasmImageType.components ) const image = new Image(imageType) image.origin = wasmImage.origin image.spacing = wasmImage.spacing const directionBuffer = await decodeNumcodecEncoded(wasmImage.direction) image.direction = new Float64Array(directionBuffer) image.size = wasmImage.size const dataBuffer = await decodeNumcodecEncoded(wasmImage.data) image.data = bufferToTypedArray(imageType.componentType, dataBuffer) return image } const imJoyCodecs = [ { name: 'numcodec-encoded', decoder: decodeNumcodecEncoded }, { name: 'itkwasm-image', decoder: decodeItkWasmImage }, ] export default imJoyCodecs ================================================ FILE: src/index.d.ts ================================================ import { Image } from 'itk-wasm' declare interface Store { getItem(): Promise } declare type ndarray = { _rtype: 'ndarray' } declare type LoadableImage = URL | Image | Store | ndarray declare type ViewerOptions = { image?: LoadableImage labelImage?: LoadableImage geometries?: [any] use2D?: boolean rotate?: boolean config?: any } type Viewer = { setBackgroundColor(color: [number, number, number]): void setImageColorMap(mapName: string, actor: number): void } declare namespace itkVtkViewer { function createViewer( rootContainer: HTMLElement, options?: ViewerOptions ): Promise function createViewerFromLocalFiles(container: HTMLElement): Promise function createViewerFromFiles( el: HTMLElement, files: [any], use2D?: boolean ): Promise function createViewerFromUrl( el: HTMLElement, options?: { files?: [string] } & ViewerOptions ): Promise function processURLParameters( container: HTMLElement, addOnParameters?: any ): Promise function initializeEmbeddedViewers(): void type version = string } ================================================ FILE: src/index.js ================================================ import axios from 'axios' import vtkURLExtract from 'vtk.js/Sources/Common/Core/URLExtract' import { readImageArrayBuffer } from 'itk-wasm' import fetchBinaryContent from './IO/fetchBinaryContent' import fetchJsonContent from './IO/fetchJsonContent' import { processFiles } from './IO/processFiles' import UserInterface from './UserInterface' import createFileDragAndDrop from './UserInterface/createFileDragAndDrop' import style from './UserInterface/ItkVtkViewer.module.css' import toMultiscaleSpatialImage from './IO/toMultiscaleSpatialImage' import { ConglomerateMultiscaleSpatialImage } from './IO/ConglomerateMultiscaleSpatialImage' import { isZarr } from './IO/ZarrMultiscaleSpatialImage' import ImJoyPluginAPI from './ImJoyPluginAPI.js' import imJoyCodecs from './imJoyCodecs.js' import packageJson from '../package.json' const { version } = packageJson let doNotInitViewers = false export { version } export { ImJoyPluginAPI } export { imJoyCodecs } export { default as createViewer } from './createViewer' import * as utils from './utils.js' export { utils } // The `UserInterface` is considered an internal implementation detail // and its interface and behavior may change without changes to the major version. export { UserInterface } /** Returns a Promise that revolves with the Viewer created the files. */ export function createViewerFromLocalFiles(container) { doNotInitViewers = true return createFileDragAndDrop(container, processFiles) } export async function createViewerFromFiles(el, files, use2D = false) { return processFiles(el, { files: files, use2D }) } async function makeImage({ image, progressCallback, isLabelImage = false }) { if (!image) return null const imageUrlObj = new URL(image, document.location) if (isZarr(image)) { return toMultiscaleSpatialImage(imageUrlObj, isLabelImage) } const result = await readImageArrayBuffer( null, await fetchBinaryContent(imageUrlObj, progressCallback), imageUrlObj.pathname.split('/').pop() ) result.webWorker.terminate() return toMultiscaleSpatialImage(result.image, isLabelImage) } async function parseImageArg(image, progressCallback) { if (!image) return null const images = await Promise.all( image.split(',').map(image => makeImage({ image, progressCallback })) ) return images.length > 1 ? new ConglomerateMultiscaleSpatialImage(images) : images[0] } export async function createViewerFromUrl( el, { files = [], image, labelImage, fixedImage, config, labelImageNames = null, rotate = true, use2D = false, ...rest } ) { UserInterface.emptyContainer(el) const progressCallback = UserInterface.createLoadingProgress(el) let fetchedImage const fileObjects = [] for (const url of files) { const urlObj = new URL(url, document.location) if (isZarr(url)) { fetchedImage = await toMultiscaleSpatialImage(urlObj) } else { const arrayBuffer = await fetchBinaryContent(urlObj, progressCallback) fileObjects.push( new File([new Blob([arrayBuffer])], urlObj.pathname.split('/').pop()) ) } } // No image in files? Check image arg. fetchedImage = fetchedImage ?? (await parseImageArg(image, progressCallback)) const labelImageObject = await makeImage({ image: labelImage, progressCallback, isLabelImage: true, }) const fixedImageObject = await makeImage({ image: fixedImage, progressCallback, }) let viewerConfig = null if (config) { const response = await axios.get(config, { responseType: 'json', }) viewerConfig = response.data } let labelImageNameObject = null if (labelImageNames) { labelImageNameObject = await fetchJsonContent(labelImageNames) } return processFiles(el, { files: fileObjects, image: fetchedImage, labelImage: labelImageObject, fixedImage: fixedImageObject, config: viewerConfig, labelImageNames: labelImageNameObject, rotate, use2D, ...rest, }) } const parseBoolean = datasetValue => datasetValue !== undefined ? datasetValue.toLowerCase() === 'true' : undefined export function initializeEmbeddedViewers() { if (doNotInitViewers) { return } const viewers = document.querySelectorAll('.itk-vtk-viewer') let count = viewers.length while (count--) { const el = viewers[count] if (!el.dataset.loaded) { el.dataset.loaded = true // Apply size to container const [width, height] = (el.dataset.viewport || '500x500').split('x') el.style.position = 'relative' el.style.width = Number.isFinite(Number(width)) ? `${width}px` : width el.style.height = Number.isFinite(Number(height)) ? `${height}px` : height const files = el.dataset.url.split(',') createViewerFromUrl(el, { files, use2D: parseBoolean(el.dataset.use2d), rotate: parseBoolean(el.dataset.rotate), }).then(viewer => { // Background color handling if (el.dataset.backgroundColor) { const color = el.dataset.backgroundColor const bgColor = [ color.slice(0, 2), color.slice(2, 4), color.slice(4, 6), ].map(v => parseInt(v, 16) / 255) viewer.setBackgroundColor(bgColor) } viewer.setUICollapsed(true) viewer.render() el.dataset.viewer = viewer }) } } } function createCompareOptions(userParams) { if (userParams.compare) { const options = Object.fromEntries( ['pattern', 'swapImageOrder', 'checkerboard', 'imageMix'] .map(key => [key, userParams[key]]) .filter(([, value]) => value) ) options.method = userParams.compare return options } return undefined } export function processURLParameters(container, addOnParameters = {}) { const userParams = Object.assign( {}, vtkURLExtract.extractURLParameters(), addOnParameters ) if (userParams.gradientOpacity && isNaN(userParams.gradientOpacity)) throw new Error('gradientOpacity URL paramter is not a number') const myContainer = UserInterface.getRootContainer(container) if (userParams.fullscreen) { myContainer.classList.add(style.fullscreenContainer) } const files = userParams.fileToLoad?.split(',') ?? [] if (files.length || userParams.image || userParams.labelImage) { return createViewerFromUrl(myContainer, { files, image: userParams.image, labelImage: userParams.labelImage, config: userParams.config, labelImageNames: userParams.labelImageNames, rotate: userParams.rotate ?? true, use2D: !!userParams.use2D, gradientOpacity: userParams.gradientOpacity, fixedImage: userParams.fixedImage, compare: createCompareOptions(userParams), }) } return null } // Ensure processing of embedded viewers setTimeout(initializeEmbeddedViewers, 100) ================================================ FILE: src/internalUtils.js ================================================ import { mat4 } from 'gl-matrix' export function arraysEqual(a, b) { if (a === b) return true if (a == null || b == null) return false if (a.length !== b.length) return false for (var i = 0; i < a.length; ++i) { if (a[i] !== b[i]) return false } return true } const makeMat4 = ({ direction, origin, spacing }) => { const mat = mat4.create() mat4.fromTranslation(mat, origin) mat[0] = direction[0] mat[1] = direction[1] mat[2] = direction[2] mat[4] = direction[3] mat[5] = direction[4] mat[6] = direction[5] mat[8] = direction[6] mat[9] = direction[7] mat[10] = direction[8] return mat4.scale(mat, mat, spacing) } export const makeIndexToWorld = ({ direction: inDirection, origin, spacing, }) => { // ITK (and VTKMath) uses row-major index axis, but gl-matrix uses column-major. Transpose. const DIMENSIONS = 3 const direction = Array(inDirection.length) for (let idx = 0; idx < DIMENSIONS; ++idx) { for (let col = 0; col < DIMENSIONS; ++col) { direction[col + idx * 3] = inDirection[idx + col * DIMENSIONS] } } const origin3d = [...origin] if (origin3d[2] === undefined) origin3d[2] = 0 const spacing3d = [...spacing] if (spacing3d[2] === undefined) spacing3d[2] = 1 return makeMat4({ direction, origin: origin3d, spacing: spacing3d }) } ================================================ FILE: src/itkConfig.js ================================================ /* eslint-disable no-undef */ const itkConfig = { pipelineWorkerUrl: __webpack_public_path__ + 'itk/web-workers/min-bundles/pipeline.worker.js', imageIOUrl: __webpack_public_path__ + 'itk/image-io', meshIOUrl: __webpack_public_path__ + 'itk/mesh-io', pipelinesUrl: __webpack_public_path__ + 'itk/pipeline', } export default itkConfig ================================================ FILE: src/itkConfigCDN.js ================================================ /* eslint-disable no-undef */ const itkConfig = { pipelineWorkerUrl: __webpack_public_path__ + 'itk-wasm@' + __itk_version__ + '/dist/web-workers/min-bundles/pipeline.worker.js', imageIOUrl: __webpack_public_path__ + 'itk-image-io@' + __itk_version__, meshIOUrl: __webpack_public_path__ + 'itk-mesh-io@' + __itk_version__, pipelinesUrl: __webpack_public_path__ + 'itk-vtk-viewer@' + __itk_vtk_viewer_version__ + '/dist/itk/pipeline', } export default itkConfig ================================================ FILE: src/transformBounds.js ================================================ import { vec3 } from 'gl-matrix' // from vtk.js/Sources/Common/DataModel/BoundingBox // Computes the two corners with minimal and miximal coordinates function computeCornerPoints(bounds, point1, point2) { point1[0] = bounds[0] point1[1] = bounds[2] point1[2] = bounds[4] point2[0] = bounds[1] point2[1] = bounds[3] point2[2] = bounds[5] return point1 } // from vtk.js/Sources/Common/Core/Math function computeBoundsFromPoints(point1, point2, bounds) { bounds[0] = Math.min(point1[0], point2[0]) bounds[1] = Math.max(point1[0], point2[0]) bounds[2] = Math.min(point1[1], point2[1]) bounds[3] = Math.max(point1[1], point2[1]) bounds[4] = Math.min(point1[2], point2[2]) bounds[5] = Math.max(point1[2], point2[2]) return bounds } export const transformBounds = (transformingMat4, bounds) => { const in1 = [0, 0, 0] const in2 = [0, 0, 0] computeCornerPoints(bounds, in1, in2) const out1 = [0, 0, 0] const out2 = [0, 0, 0] vec3.transformMat4(out1, in1, transformingMat4) vec3.transformMat4(out2, in2, transformingMat4) return computeBoundsFromPoints(out1, out2, []) } ================================================ FILE: src/utils.js ================================================ import vtkITKHelper from 'vtk.js/Sources/Common/DataModel/ITKHelper' import vtkCoordinate from 'vtk.js/Sources/Rendering/Core/Coordinate' import vtk from 'vtk.js/Sources/vtk' import ndarrayToItkImage from './IO/ndarrayToItkImage' import ndarrayToPointSet from './IO/ndarrayToPointSet' import toMultiscaleSpatialImage from './IO/toMultiscaleSpatialImage' export { vtkITKHelper } export { vtkCoordinate } export { vtk } export { ndarrayToItkImage } export { ndarrayToPointSet } export { toMultiscaleSpatialImage } export { ConglomerateMultiscaleSpatialImage } from './IO/ConglomerateMultiscaleSpatialImage' export { readFiles } from './IO/processFiles' ================================================ FILE: src/viewerMachineOptions.js ================================================ import createRenderingViewContainers from './UI/createRenderingViewContainers' import styleRenderingViewContainers from './UI/styleRenderingViewContainers' import referenceUIMachineOptions from './UI/reference-ui/dist/referenceUIMachineOptions' import vtkJSRenderingMachineOptions from './Rendering/VTKJS/vtkJSRenderingMachineOptions' const ViewerMachineOptions = { actions: { createRenderingViewContainers, styleRenderingViewContainers, }, ui: referenceUIMachineOptions, rendering: vtkJSRenderingMachineOptions, } export default ViewerMachineOptions ================================================ FILE: test/conglomerateTest.js ================================================ import test from 'tape-catch' import { ConglomerateMultiscaleSpatialImage } from '../src/IO/ConglomerateMultiscaleSpatialImage' import { toMultiscaleSpatialImage } from '../src/utils' const takeSnapshot = ({ scaleInfo, imageType }) => { return JSON.stringify({ scaleInfo, imageType }) } const IMAGE_BASELINES = [ { paths: [ 'base/test/data/input/astronaut.zarr', 'base/test/data/input/astronaut.zarr', ], baseline: '{"scaleInfo":[{"dims":["t","c","z","y","x"],"pixelArrayMetadata":{"chunks":[1,1,1,512,512],"compressor":{"blocksize":0,"clevel":5,"cname":"lz4","id":"blosc","shuffle":1},"dtype":" { for (const { paths, baseline } of IMAGE_BASELINES) { const multiscaleImages = await Promise.all( paths .map(path => new URL(path, document.location.origin)) .map(image => toMultiscaleSpatialImage(image)) ) const image = new ConglomerateMultiscaleSpatialImage(multiscaleImages) t.deepEqual( takeSnapshot(image), baseline, `conglomerate ${paths} image matches baseline` ) } t.end() }) ================================================ FILE: test/convertItkImageToVtkImageTest.js ================================================ import test from 'tape-catch' import { readImageBlob, IntTypes, PixelTypes, getMatrixElement } from 'itk-wasm' const cthead1SmallBase64DataURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAAAAABWESUoAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfhBQYVKw8AZTNIAAADdklEQVQ4y2WTa2wUVRiGp6W7O3POnLmc2VrstokJlrBIUBJigjfSICVCCAo/QKM/FFNRIESJQKAws3M7M2f20t3GthRKQQq0kkoXMIq9oFwCXkg0UpMakGLgR9EmJF4TNOvZhRBb31+TvM955/vO+T6Ou69pAgSwKCCAEPc/lYUhFEUkMgH2ESmbYocEEUmKLIQqBKmEgUlERQhAPhyJiDMXPFZZDmRGoP8Q5TwC4ciMpatfXE9zmT2NVRVIQiLi76cDUVRDT/m72zLUc/Srv+gNCi8jhCrupvMAQIWf1zJx58pRj7g7h/sduunhiIIkUAJ4AUBZ0LZev3TondmeS42TuaYms6kOapJUalYQAAKxt+j4qD3yxvMZ0z47NLi/ydhWA7GMinWyAH6G1Wwe/OdUz6dz33T35dPdIxdIYrPGK0qxTnYrobVtjm+3pNvPxGu9/dTRgw8/e89et0AKF1uFItS2u7ZP7fr4K3H19VbP94me/T6fXRifM6+a/QKC6N5+PWGYZhVeNn9pzvUoTVnt3/QEz81dUTONgwjis4UzvS2Z5JbY9JlPdxmEuFZzX9va0yu5WlXmRAlWd3Tmjg980vXBprJZbYPtza0dXw40ZleeP1ZbrWKOXXpsu7Grb3gnsY/27B46+e3ElVuF3w+sm7Pki2VAUxkAo1t0a7TL8YnVPZxy6KG9fX/+2qu/+9DARoAVBiDYaHjnfc/3nHOdicA1Em6WpnOdG/I6zwCA5PCzrn6uw6VO99gBnRBKGUyIMfz3BgmrHHta8cEdu04dN6wjPwy6FinaTNT8emKNzGrgBEmJLLf7T6Tf/60wpFP2oKToB/bNr+pVTWHjghQxZuTzW51C4aIZENdj8gMv+1f3I7iYwPEqrFu+z1/zzI3vHN/ziEd9P0haV39aXxXFRaBMRrCu9Vjj5o/S5C4QBCnjws+pJ9SoqpZmRlqyeNWlPa922El22PMCl5if38q9FGV+CeAaFuK4OZY5nLRoksnsPX19nL5do2GsREoAlCtr68lo4VoXNROWdXD8j7GUNV96AMPye5MtYgU/ujF/887tHy+PXLt9o9/asUipvDfWpc1QNFWKPfla8PHI5Ysnsua2l2dH1Un7WS6rKlamxx9f/MKKhkX1syoxmLqcUMVRDTNMlZGkilPsUrOsJ6wxRSel/wuAkzbenLRf4gAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wNS0wNlQxNzoyNjozNC0wNDowMORO/MMAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDUtMDZUMTc6MjY6MzQtMDQ6MDCVE0R/AAAAAElFTkSuQmCC' const byteString = window.atob(cthead1SmallBase64DataURI.split(',')[1]) const mimeString = cthead1SmallBase64DataURI .split(',')[0] .split(':')[1] .split(';')[0] let intArray = new Uint8Array(byteString.length) for (let ii = 0; ii < byteString.length; ++ii) { intArray[ii] = byteString.charCodeAt(ii) } const cthead1SmallBlob = new window.Blob([intArray], { type: mimeString }) const verifyImage = (t, image) => { t.is(image.imageType.dimension, 2, 'dimension') t.is(image.imageType.componentType, IntTypes.UInt8, 'componentType') t.is(image.imageType.pixelType, PixelTypes.Scalar, 'pixelType') t.is(image.imageType.components, 1, 'components') t.is(image.origin[0], 0.0, 'origin[0]') t.is(image.origin[1], 0.0, 'origin[1]') t.is(image.spacing[0], 1.0, 'spacing[0]') t.is(image.spacing[1], 1.0, 'spacing[1]') t.is(getMatrixElement(image.direction, 2, 0, 0), 1.0, 'direction (0, 0)') t.is(getMatrixElement(image.direction, 2, 0, 1), 0.0, 'direction (0, 1)') t.is(getMatrixElement(image.direction, 2, 1, 0), 0.0, 'direction (1, 0)') t.is(getMatrixElement(image.direction, 2, 1, 1), 1.0, 'direction (1, 1)') t.is(image.size[0], 32, 'size[0]') t.is(image.size[1], 32, 'size[1]') t.is(image.data.length, 1024, 'data.length') t.is(image.data[512], 12, 'data[512]') t.end() } test('readImageBlob reads a Blob', t => { return readImageBlob(null, cthead1SmallBlob, 'cthead1Small.png').then( function({ image }) { verifyImage(t, image) } ) }) test('convertItkImageToVtkImage results in the expected image', t => { t.ok(true, 'a check passes') t.end() }) ================================================ FILE: test/createViewerTest.js ================================================ import test from 'tape-catch' import axios from 'axios' import { readImageArrayBuffer } from 'itk-wasm' import testUtils from 'vtk.js/Sources/Testing/testUtils' import vtk from 'vtk.js/Sources/vtk' import createViewer from '../src/createViewer' import './customElementsDefineOverride.js' import referenceUIMachineOptions from '../src/UI/reference-ui' import { MAX_CONCURRENCY } from '../src/Context/ViewerMachineContext' const testImage3DPath = 'base/test/data/input/HeadMRVolume.nrrd' const testLabelImage3DPath = 'base/test/data/input/HeadMRVolumeLabels.nrrd' const test2ComponentImage3DPath = 'base/test/data/input/HeadMRVolume2Components.nrrd' const testImage3DPath2 = 'base/test/data/input/mri3D.nrrd' const testImage2D = 'base/test/data/input/HeadMRVolume2DTop.nrrd' // import createViewerBaseline from './data/baseline/createViewer.png' // import createViewerSetImageBaseline from './data/baseline/createViewerSetImage.png' const TEST_STYLE_RENDERING_VIEW_CONTAINER = { position: 'relative', width: '600px', height: '600px', minHeight: '600px', minWidth: '600px', maxHeight: '600px', maxWidth: '600px', margin: '0', padding: '0', top: '0', left: '0', overflow: 'hidden', } const TEST_VIEWER_STYLE = { backgroundColor: [1, 1, 1], containerStyle: TEST_STYLE_RENDERING_VIEW_CONTAINER, } const baselineConfig = JSON.parse( `{"viewerConfigVersion":"0.3","uiMachineOptions":"reference","xyLowerLeft":false,"renderingViewContainerStyle":{"position":"relative","width":"600px","height":"600px","minHeight":"600px","minWidth":"600px","maxHeight":"600px","maxWidth":"600px","margin":"0","padding":"0","top":"0","left":"0","overflow":"hidden"},"uiCollapsed":true,"main":{"backgroundColor":[0.7,0.2,0.8],"units":"mm"},"maxConcurrency":${MAX_CONCURRENCY}}` ) function makePointSet() { return vtk({ vtkClass: 'vtkPolyData', points: { vtkClass: 'vtkPoints', name: '_points', numberOfComponents: 3, dataType: 'Float32Array', values: new Float32Array([ -0.44442534, -1.1349318, 0.8388769, 2.0538256, -1.9028517, 0.71276945, ]), }, verts: { vtkClass: 'vtkCellArray', name: '_verts', numberOfComponents: 1, dataType: 'Uint32Array', size: 4, values: new Uint16Array([1, 0, 1, 1]), }, }) } test('Test createViewer', async t => { const gc = testUtils.createGarbageCollector(t) t.plan(61) const container = document.querySelector('body') const viewerContainer = gc.registerDOMElement(document.createElement('div')) container.appendChild(viewerContainer) const response = await axios.get(testImage3DPath, { responseType: 'arraybuffer', }) const { image: itkImage, webWorker } = await readImageArrayBuffer( null, response.data, 'data.nrrd' ) webWorker.terminate() const labelResponse = await axios.get(testLabelImage3DPath, { responseType: 'arraybuffer', }) const { image: itkLabelImage, webWorker: labelWebWorker, } = await readImageArrayBuffer(null, labelResponse.data, 'data.nrrd') labelWebWorker.terminate() const uiMachineOptions = { ...referenceUIMachineOptions } const originalCreateInterface = referenceUIMachineOptions.actions.createInterface function testCreateInterface(context) { t.pass('Modified uiMachineOptions') originalCreateInterface(context) } const testUIMachineActions = { ...uiMachineOptions.actions } testUIMachineActions.createInterface = testCreateInterface uiMachineOptions.actions = testUIMachineActions const GRADIENT_OPACITY = 0.1 const viewer = await createViewer(viewerContainer, { image: itkImage, labelImage: itkLabelImage, rotate: false, gradientOpacity: GRADIENT_OPACITY, config: { uiMachineOptions }, }) viewer.setRenderingViewContainerStyle(TEST_VIEWER_STYLE.containerStyle) viewer.setBackgroundColor(TEST_VIEWER_STYLE.backgroundColor) const VALUE = 0.3 viewer.setImageVolumeScatteringBlend(VALUE) t.equal( viewer.getImageVolumeScatteringBlend(), VALUE, 'getImageVolumeScatteringBlend matches setImageVolumeScatteringBlend' ) viewer.setUICollapsed(false) let collapsed = viewer.getUICollapsed() t.equal(collapsed, false, 'setUICollapsed false') viewer.setUICollapsed(true) collapsed = viewer.getUICollapsed() t.equal(collapsed, true, 'setUICollapsed true') t.equal( viewer.getImageGradientOpacity(), GRADIENT_OPACITY, 'gradientOpacity set' ) const bgColor = [0.7, 0.2, 0.8] viewer.once('backgroundColorChanged', () => { t.pass('backgroundColorChanged event') }) viewer.setBackgroundColor(bgColor) const resultBGColor = viewer.getBackgroundColor() t.same(resultBGColor, bgColor, 'background color') viewer.setUnits('mm') const resultUnits = viewer.getUnits() t.same(resultUnits, 'mm', 'units') // skip for screenshot consistency //viewer.once('toggleRotate', () => { //t.pass('toggleRotate event') //}) //viewer.setRotateEnabled(true) //const resultRotate = viewer.getRotateEnabled() //t.same(resultRotate, true, 'rotate') //viewer.setRotateEnabled(false) viewer.once('toggleAnnotations', () => { t.pass('toggleAnnotations event') }) viewer.setAnnotationsEnabled(false) const resultAnnotations = viewer.getAnnotationsEnabled() t.same(resultAnnotations, false, 'annotations') viewer.setAnnotationsEnabled(true) viewer.once('toggleAxes', () => { t.pass('toggleAxes event') }) viewer.setAxesEnabled(true) const resultAxes = viewer.getAxesEnabled() t.same(resultAxes, true, 'axes') viewer.setAxesEnabled(false) viewer.once('toggleImageInterpolation', () => { t.pass('toggleImageInterpolation event') }) viewer.setImageInterpolationEnabled(false) const resultImageInterpolation = viewer.getImageInterpolationEnabled() t.same(resultImageInterpolation, false, 'interpolation') viewer.setImageInterpolationEnabled(true) viewer.once('viewModeChanged', () => { t.pass('viewModeChanged event') }) viewer.setViewMode('XPlane') const resultViewMode = viewer.getViewMode() t.same(resultViewMode, 'XPlane', 'view mode') viewer.setViewMode('Volume') const firstLayer = viewer.getLayerNames()[0] t.same(firstLayer, 'Image', 'getLayerNames') viewer.once('toggleLayerVisibility', () => { t.pass('toggleLayerVisibility event') }) viewer.setLayerVisibility(false, firstLayer) const resultLayerVisibility = viewer.getLayerVisibility(firstLayer) t.same(resultLayerVisibility, false, 'layer visibility') viewer.setLayerVisibility(true, firstLayer) viewer.selectLayer(firstLayer) viewer.once('imageVisualizedComponentChanged', () => { t.pass('imageVisualizedComponentChanged event') }) viewer.setImageComponentVisibility(false, 0) const resultImageComponentVisibility = viewer.getImageComponentVisibility(0) t.same(resultImageComponentVisibility, false, 'image component visibility') viewer.setImageComponentVisibility(true, 0) viewer.setCroppingPlanesEnabled(true) t.same(viewer.getCroppingPlanesEnabled(), true, 'set cropping planes enabled') viewer.setCroppingPlanesEnabled(false) const [iMin, iMax, jMin, jMax, kMin, kMax] = [0, 3, 0, 3, 0, 3] const origin = [iMin, jMin, kMin] // opposite corner from origin const corner = [iMax, jMax, kMax] const planes = [ // X min/max { normal: [1, 0, 0], origin }, { normal: [-1, 0, 0], origin: corner, }, // Y min/max { normal: [0, 1, 0], origin }, { normal: [0, -1, 0], origin: corner, }, // X min/max { normal: [0, 0, 1], origin }, { normal: [0, 0, -1], origin: corner, }, ] viewer.setCroppingPlanes(planes) t.same(viewer.getCroppingPlanes(), planes, 'set cropping planes ') // handle non 6 array of planes const plane = [{ normal: [1, 0, 0], origin }] viewer.setCroppingPlanes(plane) viewer.resetCroppingPlanes() t.notSame(viewer.getCroppingPlanes(), plane, 'reset cropping planes ') const viewProxy = viewer.getViewProxy() const renderWindow = viewProxy.getOpenGLRenderWindow() // Consistent baseline image size for regression testing renderWindow.setSize(600, 600) viewer.render() setTimeout(async () => { viewer.once('imageColorRangeChanged', () => { t.pass('imageColorRangeChanged event') }) const oldRange = viewer.getImageColorRange(0) viewer.setImageColorRange([20, 80], 0) const resultImageColorRange = viewer.getImageColorRange(0) t.same(resultImageColorRange, [20, 80], 'image color range') viewer.setImageColorRange(oldRange, 0) viewer.once('imageColorRangeBoundsChanged', () => { t.pass('imageColorRangeBoundsChanged event') }) const oldBounds = viewer.getImageColorRangeBounds(0) viewer.setImageColorRangeBounds([-20, 800], 0) const resultImageColorRangeBounds = viewer.getImageColorRangeBounds(0) t.same(resultImageColorRangeBounds, [-20, 800], 'image color range bounds') viewer.setImageColorRangeBounds(oldBounds, 0) viewer.once('imageColorMapChanged', () => { t.pass('imageColorMapChanged event') }) const oldColorMap = viewer.getImageColorMap(0) viewer.setImageColorMap('2hot', 0) const resultImageColorMap = viewer.getImageColorMap(0) t.same(resultImageColorMap, '2hot', 'image color range') viewer.setImageColorMap(oldColorMap, 0) viewer.once('imagePiecewiseFunctionGaussiansChanged', () => { t.pass('imagePiecewiseFunctionGaussiansChanged event') }) const oldGaussians = viewer.getImagePiecewiseFunctionGaussians(0) const newGaussians = [ { position: 0.3, height: 0.4, width: 0.5, xBias: 0.21, yBias: 0.6 }, ] viewer.setImagePiecewiseFunctionGaussians(newGaussians, 0) const resultGaussians = viewer.getImagePiecewiseFunctionGaussians(0) t.same(resultGaussians, newGaussians, 'image piecewise function gaussians') viewer.setImagePiecewiseFunctionGaussians(oldGaussians, 0) viewer.once('imagePiecewiseFunctionPointsChanged', () => { t.pass('imagePiecewiseFunctionPointsChanged event') }) const newPoints = [ [0, 0], [0.5, 0.5], [1, 1], ] viewer.setImagePiecewiseFunctionPoints(newPoints, 0) const resultPoints = viewer.getImagePiecewiseFunctionPoints(0) t.same(resultPoints, newPoints, 'image piecewise function points') viewer.once('toggleImageShadow', () => { t.pass('toggleImageShadow event') }) viewer.setImageShadowEnabled(false) const resultImageShadowEnabled = viewer.getImageShadowEnabled() t.same(resultImageShadowEnabled, false, 'image shadow enabled') viewer.setImageShadowEnabled(true) viewer.once('imageGradientOpacityChanged', () => { t.pass('imageGradientOpacityChanged event') }) viewer.setImageGradientOpacity(0.5) const resultImageGradientOpacity = viewer.getImageGradientOpacity() t.same(resultImageGradientOpacity, 0.5, 'image gradient opacity') viewer.setImageGradientOpacity(0.3) viewer.once('imageGradientOpacityScaleChanged', () => { t.pass('imageGradientOpacityScaleChanged event') }) viewer.setImageGradientOpacityScale(0.8) const resultImageGradientOpacityScale = viewer.getImageGradientOpacityScale() t.same(resultImageGradientOpacityScale, 0.8, 'image gradient opacity scale') viewer.setImageGradientOpacity(0.5) viewer.once('xSliceChanged', () => { t.pass('xSliceChanged event') }) const oldXSlice = viewer.getXSlice() viewer.setXSlice(5) const resultXSlice = viewer.getXSlice() t.same(resultXSlice, 5, 'x slice') viewer.setXSlice(oldXSlice) viewer.once('ySliceChanged', () => { t.pass('ySliceChanged event') }) const oldYSlice = viewer.getYSlice() viewer.setYSlice(5) const resultYSlice = viewer.getYSlice() t.same(resultYSlice, 5, 'y slice') viewer.setYSlice(oldYSlice) viewer.once('zSliceChanged', () => { t.pass('zSliceChanged event') }) const oldZSlice = viewer.getZSlice() viewer.setZSlice(5) const resultZSlice = viewer.getZSlice() t.same(resultZSlice, 5, 'z slice') viewer.setZSlice(oldZSlice) viewer.once('imageVolumeSampleDistanceChanged', () => { t.pass('imageVolumeSampleDistanceChanged event') }) viewer.setImageVolumeSampleDistance(0.5) const resultImageVolumeSampleDistance = viewer.getImageVolumeSampleDistance() t.same(resultImageVolumeSampleDistance, 0.5, 'volume sample distance') viewer.setImageVolumeSampleDistance(0.25) viewer.once('imageBlendModeChanged', () => { t.pass('imageBlendModeChanged event') }) viewer.setImageBlendMode('Maximum') const resultImageBlendMode = viewer.getImageBlendMode() t.same(resultImageBlendMode, 'Maximum', 'blend mode') viewer.setImageBlendMode('Composite') viewer.once('labelImageLookupTableChanged', () => { t.pass('labelImageLookupTableChanged event') }) const oldLookupTable = viewer.getLabelImageLookupTable() viewer.setLabelImageLookupTable('glasbey_warm') const resultLabelImageLookupTable = viewer.getLabelImageLookupTable() t.same(resultLabelImageLookupTable, 'glasbey_warm', 'image lookup table') viewer.setLabelImageLookupTable(oldLookupTable) viewer.once('labelImageBlendChanged', () => { t.pass('labelImageBlendChanged event') }) viewer.setLabelImageBlend(0.8) const resultLabelImageBlend = viewer.getLabelImageBlend() t.same(resultLabelImageBlend, 0.8, 'label image blend') viewer.setLabelImageBlend(0.5) viewer.once('labelImageLabelNamesChanged', () => { t.pass('labelImageLabelNamesChanged event') }) const labelNames = new Map([ [0, 'background'], [1, 'brain'], [2, 'csf'], ]) viewer.setLabelImageLabelNames(labelNames) const resultLabelImageLabelNames = viewer.getLabelImageLabelNames() t.same(resultLabelImageLabelNames, labelNames, 'label image label names') viewer.once('labelImageWeightsChanged', () => { t.pass('labelImageWeightsChanged event') }) const labelWeights = new Map([ [0, 0.1], [1, 0.5], [2, 0.9], ]) viewer.setLabelImageWeights(labelWeights) const resultLabelImageWeights = viewer.getLabelImageWeights() t.same(resultLabelImageWeights, labelWeights, 'label image weights') const config = viewer.getConfig() t.same(config, baselineConfig, 'get config') viewer.setImageScale(0) t.same(true, true, 'setImageScale did not error') const points = makePointSet() await createViewer(viewerContainer, { pointSets: [points], rotate: false, }) t.pass('test completed') gc.releaseResources() //testUtils.compareImages( //screenshot, //[createViewerBaseline], //'Test createViewer', //t, //2.0, //gc.releaseResources //) }, 10000) }) test('Test createViewer.setImage', async t => { const gc = testUtils.createGarbageCollector(t) const container = document.querySelector('body') const viewerContainer = gc.registerDOMElement(document.createElement('div')) container.appendChild(viewerContainer) const response = await axios.get(testImage3DPath, { responseType: 'arraybuffer', }) const { image: itkImage, webWorker } = await readImageArrayBuffer( null, response.data, 'data.nrrd' ) webWorker.terminate() const viewer = await createViewer(container, { image: itkImage, rotate: false, }) viewer.setRenderingViewContainerStyle(TEST_VIEWER_STYLE.containerStyle) viewer.setBackgroundColor(TEST_VIEWER_STYLE.backgroundColor) const response2 = await axios.get(testImage3DPath2, { responseType: 'arraybuffer', }) const { image: itkImage2, webWorker: webWorker2, } = await readImageArrayBuffer(null, response2.data, 'data.nrrd') webWorker2.terminate() viewer.setImage(itkImage2) const viewProxy = viewer.getViewProxy() const renderWindow = viewProxy.getOpenGLRenderWindow() // Consistent baseline image size for regression testing renderWindow.setSize(600, 600) const representation = viewProxy.getRepresentations()[0] /*const volumeMapper = */ representation.getMapper() viewer.render() setTimeout(() => { viewer.captureImage().then( (/*screenshot*/) => { gc.releaseResources() //testUtils.compareImages( //screenshot, //[createViewerSetImageBaseline], //'Test createViewer.setImage', //t, //2.0, //gc.releaseResources //) }, 100 ) }) }) test('Test createViewer with just labelImage', async t => { const gc = testUtils.createGarbageCollector(t) const container = document.querySelector('body') const viewerContainer = gc.registerDOMElement(document.createElement('div')) container.appendChild(viewerContainer) const response = await axios.get(testLabelImage3DPath, { responseType: 'arraybuffer', }) const { image: labelImage, webWorker } = await readImageArrayBuffer( null, response.data, 'data.nrrd' ) webWorker.terminate() const viewer = await createViewer(container, { labelImage, rotate: false, }) t.plan(1) viewer.once('renderedImageAssigned', () => { t.pass('createViewer did not crash with just labelImage') gc.releaseResources() }) }) test('Test setImage and setLabelImage after createViewer', async t => { const gc = testUtils.createGarbageCollector(t) const container = document.querySelector('body') const viewerContainer = gc.registerDOMElement(document.createElement('div')) container.appendChild(viewerContainer) const labelImageResponse = await axios.get(testLabelImage3DPath, { responseType: 'arraybuffer', }) const { image: labelImage, webWorker } = await readImageArrayBuffer( null, labelImageResponse.data, 'data.nrrd' ) webWorker.terminate() const imageResponse = await axios.get(testImage3DPath, { responseType: 'arraybuffer', }) const { image, webWorker: webWorkerForImage } = await readImageArrayBuffer( null, imageResponse.data, 'data.nrrd' ) webWorkerForImage.terminate() const viewer = await createViewer(container, { rotate: false, }) viewer.setImage(image) viewer.setLabelImage(labelImage) t.plan(1) viewer.once('renderedImageAssigned', () => { t.pass( 'createViewer did not crash with with late setImage and setLabelImage' ) gc.releaseResources() }) }) test('Test createViewer custom UI options', async t => { const gc = testUtils.createGarbageCollector(t) const container = document.querySelector('body') const viewerContainer = gc.registerDOMElement(document.createElement('div')) container.appendChild(viewerContainer) const response = await axios.get(testImage3DPath, { responseType: 'arraybuffer', }) const { image: itkImage, webWorker } = await readImageArrayBuffer( null, response.data, 'data.nrrd' ) webWorker.terminate() const referenceUIUrl = new URL( '/base/src/UI/reference-ui/dist/referenceUIMachineOptions.js', document.location.origin ) const referenceUIMachineOptionsHref = { href: referenceUIUrl.href } await createViewer(container, { image: itkImage, rotate: false, config: { uiMachineOptions: referenceUIMachineOptionsHref }, }) t.pass('Viewer with UI module URL') await createViewer(container, { image: itkImage, rotate: false, config: { uiMachineOptions: { href: referenceUIUrl.href, export: 'default' }, }, }) t.pass('Viewer with UI module URL, explicit export') // If missing image.service.scaleSelector in options, test there is no warning // Avoids this later occurring Error: Unable to send event to child 'scaleSelector' from service 'images' const uiMachineOptionsNoImageServices = { ...referenceUIMachineOptions, images: { ...referenceUIMachineOptions.images }, } delete uiMachineOptionsNoImageServices.images.services let isWarningLogged = false const consoleWarn = console.warn console.warn = message => { if (message.includes("Warning: No service found for invocation '")) { isWarningLogged = true } } await createViewer(container, { image: itkImage, rotate: false, config: { uiMachineOptions: uiMachineOptionsNoImageServices, }, }) console.warn = consoleWarn t.same( isWarningLogged, false, 'custom options with no images.services has no warning' ) gc.releaseResources() }) const makeImages = async paths => { return Promise.all( paths.map(async path => { const response = await axios.get(path, { responseType: 'arraybuffer', }) const { image, webWorker } = await readImageArrayBuffer( null, response.data, 'data.nrrd' ) webWorker.terminate() return image }) ) } test('Test createViewer setCompareImage with checkerboard', async t => { const gc = testUtils.createGarbageCollector(t) const container = document.querySelector('body') const viewerContainer = gc.registerDOMElement(document.createElement('div')) container.appendChild(viewerContainer) const [image, fixedImage] = await makeImages([ testImage3DPath, testLabelImage3DPath, ]) const viewer = await createViewer(container, { rotate: false, }) viewer.setImage(fixedImage, 'fixed') await viewer.setImage(image, 'moving') const compareOptions = { method: 'checkerboard', pattern: [10, 5, 2], } viewer.setCompareImages('fixed', 'moving', compareOptions) t.plan(1) viewer.once('renderedImageAssigned', () => { const { method } = viewer.getCompareImages('moving') t.same(method, compareOptions.method, 'compare method matches') gc.releaseResources() }) }) test('Test createViewer setCompareImage with checkerboard and 2 component image', async t => { const gc = testUtils.createGarbageCollector(t) const container = document.querySelector('body') const viewerContainer = gc.registerDOMElement(document.createElement('div')) container.appendChild(viewerContainer) const [image, fixedImage] = await makeImages([ testImage3DPath, test2ComponentImage3DPath, ]) const viewer = await createViewer(container, { fixedImage, rotate: false, }) await viewer.setImage(image, 'moving') const compareOptions = { method: 'checkerboard', pattern: [10, 5, 2], } viewer.setCompareImages('Fixed', 'moving', compareOptions) t.plan(1) viewer.once('renderedImageAssigned', () => { t.pass( 'createViewer did not crash right after setFixedImage and setCheckerboard' ) gc.releaseResources() }) }) test('Test setCompareImage with checkerboard and 2D image', async t => { const gc = testUtils.createGarbageCollector(t) const container = document.querySelector('body') const viewerContainer = gc.registerDOMElement(document.createElement('div')) container.appendChild(viewerContainer) const [image, fixedImage] = await makeImages([testImage2D, testImage2D]) const compareOptions = { method: 'checkerboard', } const viewer = await createViewer(container, { image, fixedImage, compare: compareOptions, rotate: false, }) t.plan(1) viewer.once('renderedImageAssigned', () => { // first rendered image is just the image, not the fused compare image viewer.once('renderedImageAssigned', () => { t.pass('createViewer did not crash right after compare.') gc.releaseResources() }) }) }) test('Test cyan-magenta compare', async t => { const gc = testUtils.createGarbageCollector(t) const container = document.querySelector('body') const viewerContainer = gc.registerDOMElement(document.createElement('div')) container.appendChild(viewerContainer) const [image, fixedImage] = await makeImages([ testImage3DPath, test2ComponentImage3DPath, ]) const viewer = await createViewer(container, { fixedImage, rotate: false, }) await viewer.setImage(image, 'moving') const compareOptions = { method: 'cyan-magenta', } viewer.setCompareImages('Fixed', 'moving', compareOptions) t.plan(1) viewer.once('renderedImageAssigned', () => { t.pass('createViewer did not crash right after compare.') gc.releaseResources() }) }) test('Test blend compare', async t => { const gc = testUtils.createGarbageCollector(t) const container = document.querySelector('body') const viewerContainer = gc.registerDOMElement(document.createElement('div')) container.appendChild(viewerContainer) const [image, fixedImage] = await makeImages([testImage2D, testImage2D]) const compareOptions = { method: 'blend', imageMix: 0.25, } const viewer = await createViewer(container, { image, fixedImage, compare: compareOptions, rotate: false, }) t.plan(1) viewer.once('renderedImageAssigned', () => { t.pass('createViewer did not crash right after compare.') gc.releaseResources() }) }) ================================================ FILE: test/customElementsDefineOverride.js ================================================ // Avoids error importing config UIs multiple times in tests when // customElements.define will be called multiple times by Material-Web components function safeDecorator(fn) { return function(...args) { try { return fn.apply(this, args) } catch (error) { if ( error instanceof DOMException && [ 'has already been used with this registry', // Chrome 'has already been defined as a custom element', // Firefox ].some(msg => error.message.includes(msg)) ) { return false } throw error } } } customElements.define = safeDecorator(customElements.define) ================================================ FILE: test/downloadData.mjs ================================================ // To update the testing data, add new data test at ./test/data, then: // // // Add your token from https://web3.storage, if not present on this system // npx w3 token // // npx w3 put ./test/data/ --no-wrap -n itk-vtk-viewer-testing-data-topic-name -H // // And update the resulting CID below. // const IPFS_CID = 'bafybeihm5fr5s5s2g63pxsndmiswiayk6oz2h2kde5m32pqm7ejml24l6e' import fs from 'fs' import { config, getClient } from '@web3-storage/w3/lib.js' import { writeFiles } from 'ipfs-car/unpack/fs' async function downloadData(cid, token, outputDir) { const client = getClient({ token }) const res = await client.get(cid) await writeFiles(res.unixFsIterator(), outputDir) } const testDataDir = './test/data' if (!fs.existsSync(testDataDir)) { console.log('Test data not found. Downloading.') // eslint-disable-next-line no-undef let token = process.env.WEB3STORAGE_TOKEN if (!token) { token = config.get('token') } await downloadData(IPFS_CID, token, testDataDir) } else { console.log('Test data found.') } ================================================ FILE: test/imjoyTest.js ================================================ import test from 'tape-catch' import axios from 'axios' import { readImageArrayBuffer } from 'itk-wasm' import testUtils from 'vtk.js/Sources/Testing/testUtils' const testImage3DPath = 'base/test/data/input/HeadMRVolume.nrrd' import * as imjoyCore from 'imjoy-core' import ndarray from 'ndarray' const TEST_STYLE_RENDERING_VIEW_CONTAINER = { position: 'relative', width: '600px', height: '600px', minHeight: '600px', minWidth: '600px', maxHeight: '600px', maxWidth: '600px', margin: '0', padding: '0', top: '0', left: '0', overflow: 'hidden', } function applyStyle(el, style) { Object.keys(style).forEach(key => { el.style[key] = style[key] }) } function encodeArray(array) { return { _rtype: 'ndarray', _rdtype: array.dtype, _rshape: array.shape, _rvalue: array.data.buffer, } } const testConfig = JSON.parse( '{"viewerConfigVersion":"0.2","xyLowerLeft":true,"renderingViewContainerStyle":{"position":"relative","width":"600px","height":"600px","minHeight":"600px","minWidth":"600px","maxHeight":"600px","maxWidth":"600px","margin":"0","padding":"0","top":"0","left":"0","overflow":"hidden"},"main":{"backgroundColor":[0.7,0.2,0.8],"units":"mm"}}' ) test('Test ImJoy Plugin', async t => { t.plan(7) const gc = testUtils.createGarbageCollector(t) const container = document.querySelector('body') const viewerContainer = gc.registerDOMElement(document.createElement('div')) container.appendChild(viewerContainer) const response = await axios.get(testImage3DPath, { responseType: 'arraybuffer', }) const { image: itkImage, webWorker } = await readImageArrayBuffer( null, response.data, 'data.nrrd' ) webWorker.terminate() const array = ndarray(itkImage.data, itkImage.size.slice().reverse()) const imjoy_api = { showMessage(plugin, info /*,duration*/) { console.log(info) }, } const imjoy = new imjoyCore.ImJoy({ imjoy_api, }) imjoy.event_bus.on('show_message', console.log) imjoy.event_bus.on('add_window', async w => { viewerContainer.id = w.window_id applyStyle(viewerContainer, TEST_STYLE_RENDERING_VIEW_CONTAINER) }) await imjoy.start({ workspace: 'default' }) console.log('ImJoy started') const points = ndarray(new Float32Array([1, 0, 0, 0, 1, 0]), [2, 3]) const pointSets = [encodeArray(points)] const viewerIndex = new URL('/base/dist/index.html', document.location.origin) let viewer = await imjoy.pm.createWindow(null, { src: viewerIndex.href, data: { pointSets }, config: testConfig, }) const points2D = ndarray(new Float32Array([1, 0, 0, 1]), [2, 2]) const pointSets2D = [encodeArray(points2D)] await viewer.setPointSets(pointSets2D) t.pass('setPointSets ndarray') const uiMachineOptions = { href: new URL( '/base/test/testUINoPlaneSlidersBundle.js', document.location.origin ).href, } testConfig.uiMachineOptions = uiMachineOptions viewer = await imjoy.pm.createWindow(null, { src: viewerIndex.href, data: { image: itkImage, uiMachineOptions }, config: testConfig, }) await viewer.setImage(encodeArray(array)) t.pass('setImage ndarray') const bgColor = [0.2, 0.8, 0.7] await viewer.setBackgroundColor(bgColor) const resultBGColor = await viewer.getBackgroundColor() t.same(bgColor, resultBGColor, 'background color') await viewer.setImage(itkImage) t.pass('setImage itk.js Image') const imageURL = new URL(testImage3DPath, document.location.origin) await viewer.setImage(imageURL) t.pass('setImage URL') const image = await viewer.captureImage() t.same( image.startsWith('data:image/png;base64,'), true, 'captured image format' ) imjoy.destroy() console.log('ImJoy destroyed') t.pass('test completed') gc.releaseResources() }) ================================================ FILE: test/itkConfigBrowserTest.js ================================================ const itkConfig = { pipelineWorkerUrl: '/base/dist/itk/web-workers/bundles/pipeline.worker.js', // eslint-disable-line no-undef imageIOUrl: '/base/dist/itk/image-io', // eslint-disable-line no-undef meshIOUrl: '/base/dist/itk/mesh-io', // eslint-disable-line no-undef pipelinesUrl: '/base/dist/itk/pipeline', // eslint-disable-line no-undef } module.exports = itkConfig ================================================ FILE: test/multiscaleSpatialImageTest.js ================================================ import test from 'tape-catch' import ZarrMultiscaleSpatialImage from '../src/IO/ZarrMultiscaleSpatialImage' import { takeSnapshot } from './zarrImageBaselines' const IMAGE_BASELINES = [ [ 'base/test/data/input/astronaut.zarr', [ Number.MAX_VALUE, -Number.MAX_VALUE, // X Number.MAX_VALUE, -Number.MAX_VALUE, // Y Number.MAX_VALUE, -Number.MAX_VALUE, // Z ], '{"imageType":{"dimension":2,"pixelType":"VariableLengthVector","componentType":"float32","components":3},"origin":[0,0],"spacing":[8,8],"direction":{"0":1,"1":0,"2":0,"3":1},"size":[256,256],"ranges":[[0,0.9763471484184265],[0,0.9741398692131042],[0,0.9760480523109436]],"data":[0.6908552050590515,0.6688187122344971,0.674207329750061,0.27221277356147766,0.2442161589860916,0.30864495038986206,0.04838373512029648,0.021947167813777924,0.11139355599880219,0.12586522102355957,0.08368028700351715,0.2073654979467392,0.29955506324768066,0.26995500922203064,0.3033413887023926,0.4970903694629669,0.46638235449790955,0.4774773120880127,0.6510546803474426,0.6293986439704895,0.6291141510009766,0.652686595916748,0.6369064450263977,0.6407243609428406,0.6563014984130859,0.645758330821991,0.6505763530731201,0.6664388179779053,0.6574797630310059,0.6628299951553345,0.6745269298553467,0.6657060384750366,0.6709856390953064,0.702728271484375,0.6728822588920593,0.6759136915206909,0.3145504593849182,0.27835237979888916,0.33366164565086365,0.12892097234725952,0.08835494518280029,0.16398540139198303,0.18125581741333008,0.09787159413099289,0.20692530274391174,0.32659101486206055,0.250407338142395,0.27853235602378845,0.5059590339660645,0.4235151708126068,0.41403526067733765,0.64797443151474,0.5656358003616333,0.5444427728652954,0.650419294834137,0.5713972449302673,0.5587600469589233,0.6266179084777832,0.5702376365661621,0.5665257573127747,0.6173701882362366,0.57239830493927,0.5740906596183777,0.6674000024795532,0.6029467582702637,0.594350278377533,1.0168959424516899e-11,1.3201037485366385e-11,7.667253984489086e-12,6.150961359176199e-13,6.375242673183068e-13,1.862368982988305e-12,2.6472281398209896e-13,2.687519803055982e-13,1.9147365442305497e-13,8.121141834103313e-15,7.599267894640993e-15,1.278036429805526e-14,1.6719174311674578e-7,1.4385383906301286e-7,1.528643451820244e-7,0.0015648636035621166,0.0013919497141614556,0.0013295512180775404,0.10193789750337601,0.09551996737718582,0.08835914731025696,0.20549945533275604,0.19322900474071503,0.15210646390914917,0.15297368168830872,0.13750818371772766,0.10394243896007538,0.22972600162029266,0.21356536448001862,0.18998141586780548,0.17018358409404755,0.15841108560562134,0.15074452757835388]}', ], [ 'base/test/data/input/astronaut.zarr', [ 100, -Number.MAX_VALUE, // X Number.MAX_VALUE, 200, // Y Number.MAX_VALUE, -Number.MAX_VALUE, // Z ], '{"imageType":{"dimension":2,"pixelType":"VariableLengthVector","componentType":"float32","components":3},"origin":[0,200],"spacing":[8,8],"direction":{"0":1,"1":0,"2":0,"3":1},"size":[14,231],"ranges":[[0.027741413563489914,0.9305924773216248],[0.009815634228289127,0.8219987154006958],[0.03106931783258915,0.8618532419204712]],"data":[0.8434163331985474,0.8005973696708679,0.8331800699234009,0.7815706133842468,0.7346546649932861,0.7513733506202698,0.6643561124801636,0.6075320243835449,0.5917041301727295,0.6853213310241699,0.6227045059204102,0.6025393605232239,0.6865707039833069,0.6216851472854614,0.6030192971229553,0.6473481059074402,0.5493790507316589,0.5326882004737854,0.4837162494659424,0.21639575064182281,0.23379985988140106,0.4325582981109619,0.05030425265431404,0.11512165516614914,0.4712345004081726,0.052832502871751785,0.11249638348817825,0.5143463015556335,0.06659615784883499,0.09130306541919708,0.4708518385887146,0.0675983726978302,0.10576199740171432,0.6128345131874084,0.5687515735626221,0.5649264454841614,0.6761928796768188,0.6405994892120361,0.616477906703949,0.7017715573310852,0.6674644947052002,0.6350312829017639,0.7152020335197449,0.6786738038063049,0.6512665748596191,0.7224043011665344,0.684797465801239,0.6600486636161804,0.727691113948822,0.6899614334106445,0.666928231716156,0.7366620898246765,0.6999374032020569,0.677337646484375,0.43325045704841614,0.38873860239982605,0.527625560760498,0.1498635858297348,0.09285452961921692,0.3016122281551361,0.1429762840270996,0.08937985450029373,0.3059288561344147,0.08375998586416245,0.03814108297228813,0.1900119036436081,0.4801272451877594,0.11964694410562515,0.1514689326286316,0.46954914927482605,0.082619309425354,0.0762876570224762,0.5867084860801697,0.1747123748064041,0.034656163305044174,0.614237904548645,0.16455063223838806,0.033633943647146225,0.6534176468849182,0.19214867055416107,0.07890287041664124,0.5566028952598572,0.1690547615289688,0.08912341296672821,0.4563755393028259,0.13142666220664978,0.07666008919477463,0.5841031670570374,0.20222604274749756,0.11976894736289978,0.7241355180740356,0.29688942432403564,0.1723022609949112,0.6470702886581421,0.2434939593076706,0.12110405415296555,0.346635103225708,0.11826460063457489,0.09175870567560196]}', ], [ 'base/test/data/input/ome-ngff-prototypes/single_image/v0.4/zyx.ome.zarr', [ 1000, 2000, // X 1000, 2000, // Y Number.MAX_VALUE, -Number.MAX_VALUE, // Z ], '{"imageType":{"dimension":3,"pixelType":"Scalar","componentType":"uint8","components":1},"name":"zyx","origin":[768,768,0],"spacing":[256,256,256],"direction":{"0":1,"1":0,"2":0,"3":0,"4":1,"5":0,"6":0,"7":0,"8":1},"size":[6,6,151],"ranges":[[0,199]],"data":[0,0,0,0,0,0,4,8,7,13,28,15,15,26,41,38,106,85,49,70,68,60,116,109,78,101,91,102,164,117,123,140,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}', ], ] test('Test ZarrMultiscaleSpatialImage world bounded chunk assembly', async t => { for (const [path, bounds, baseline] of IMAGE_BASELINES) { const storeURL = new URL(path, document.location.origin) const zarrImage = await ZarrMultiscaleSpatialImage.fromUrl(storeURL) const itkImage = await zarrImage.getImage( zarrImage.scaleInfo.length - 1, bounds ) t.deepEqual( takeSnapshot(itkImage), baseline, `${path} image matches baseline` ) } t.end() }) ================================================ FILE: test/pipelineTest.js ================================================ import test from 'tape-catch' import axios from 'axios' import { readImageArrayBuffer } from 'itk-wasm' import testUtils from 'vtk.js/Sources/Testing/testUtils' import createViewer from '../src/createViewer' import './customElementsDefineOverride.js' const testImage3DPath = 'base/test/data/input/HeadMRVolume.nrrd' const testLabelImage3DPath = 'base/test/data/input/HeadMRVolumeLabelsSmaller.nrrd' test('Test createViewer with smaller size label image', async t => { const gc = testUtils.createGarbageCollector(t) const container = document.querySelector('body') const viewerContainer = gc.registerDOMElement(document.createElement('div')) container.appendChild(viewerContainer) const imageResponse = await axios.get(testImage3DPath, { responseType: 'arraybuffer', }) const { image, webWorker: imageWorker } = await readImageArrayBuffer( null, imageResponse.data, 'data.nrrd' ) imageWorker.terminate() const labelResponse = await axios.get(testLabelImage3DPath, { responseType: 'arraybuffer', }) const { image: labelImage, webWorker: labelWorker, } = await readImageArrayBuffer(null, labelResponse.data, 'data.nrrd') labelWorker.terminate() const viewer = await createViewer(container, { image, labelImage, rotate: false, }) t.plan(1) viewer.once('renderedImageAssigned', () => { t.pass('createViewer did not crash with smaller label image') gc.releaseResources() }) }) ================================================ FILE: test/processFilesTest.js ================================================ import test from 'tape-catch' import axios from 'axios' import { FloatTypes, PixelTypes } from 'itk-wasm' const testVtiPath = 'base/test/data/input/test.vti' import { readFiles } from '../src/IO/processFiles' const verifyImage = (t, image) => { t.is(image.imageType.dimension, 3, 'dimension') t.is(image.imageType.componentType, FloatTypes.Float32, 'componentType') t.is(image.imageType.pixelType, PixelTypes.Scalar, 'pixelType') t.is(image.imageType.components, 1, 'components') t.is(image.origin[0], 0.0, 'origin[0]') t.is(image.origin[1], 0.0, 'origin[1]') t.is(image.origin[2], 0.0, 'origin[2]') t.is(image.spacing[0], 1.0, 'spacing[0]') t.is(image.spacing[1], 1.0, 'spacing[1]') t.is(image.spacing[2], 1.0, 'spacing[2]') t.is(image.size[0], 10, 'size[0]') t.is(image.size[1], 10, 'size[1]') t.is(image.size[2], 10, 'size[2]') t.is(image.data.length, 1000, 'data.length') t.is(image.data[512], 0.9276729822158813, 'data[512]') t.end() } test('Test reading a vti image file', async t => { const response = await axios.get(testVtiPath, { responseType: 'blob', }) const vtiFile = new File([response.data], 'test.vti') const result = await readFiles({ files: [vtiFile] }) verifyImage(t, result.image) }) ================================================ FILE: test/run.sh ================================================ #!/bin/bash # Source: https://github.com/thewtex/docker-opengl/tree/webgl container=webgl image=thewtex/opengl:ubuntu2004 port=6080 extra_run_args="" quiet="" debug="" show_help() { cat << EOF Usage: ${0##*/} [-h] [-q] [-c CONTAINER] [-i IMAGE] [-p PORT] [-r DOCKER_RUN_FLAGS] This script is a convenience script to run Docker images based on thewtex/opengl. It: - Makes sure docker is available - On Windows and Mac OSX, creates a docker machine if required - Informs the user of the URL to access the container with a web browser - Stops and removes containers from previous runs to avoid conflicts - Mounts the present working directory to /home/user/work on Linux and Mac OSX - Prints out the graphical app output log following execution - Exits with the same return code as the graphical app Options: -h Display this help and exit. -c Container name to use (default ${container}). -i Image name (default ${image}). -p Port to expose HTTP server (default ${port}). If an empty string, the port is not exposed. -r Extra arguments to pass to 'docker run'. E.g. --env="APP=test/my-test-command.sh" -d Debug by exposing the graphical environment with noVNC. -q Do not output informational messages. EOF } while [ $# -gt 0 ]; do case "$1" in -h) show_help exit 0 ;; -c) container=$2 shift ;; -i) image=$2 shift ;; -p) port=$2 shift ;; -r) extra_run_args="$extra_run_args $2" shift ;; -d) debug=0 shift ;; -q) quiet=1 ;; *) show_help >&2 exit 1 ;; esac shift done which docker 2>&1 >/dev/null if [ $? -ne 0 ]; then echo "Error: the 'docker' command was not found. Please install docker." exit 1 fi os=$(uname) if [ "${os}" != "Linux" ]; then vm=$(docker-machine active 2> /dev/null || echo "default") if ! docker-machine inspect "${vm}" &> /dev/null; then if [ -z "$quiet" ]; then echo "Creating machine ${vm}..." fi docker-machine -D create -d virtualbox --virtualbox-memory 2048 ${vm} fi docker-machine start ${vm} > /dev/null eval $(docker-machine env $vm --shell=sh) fi ip=$(docker-machine ip ${vm} 2> /dev/null || echo "localhost") url="http://${ip}:$port" cleanup() { docker stop $container >/dev/null docker rm $container >/dev/null } running=$(docker ps -a -q --filter "name=${container}") if [ -n "$running" ]; then if [ -z "$quiet" ]; then echo "Stopping and removing the previous session..." echo "" fi cleanup fi if [ -z "$quiet" ]; then echo "" echo "Setting up the graphical application container..." echo "" if [ -n "$port" ]; then echo "Point your web browser to ${url}" echo "" fi fi pwd_dir="$(pwd)" mount_local="" if [ "${os}" = "Linux" ] || [ "${os}" = "Darwin" ]; then mount_local=" -v ${pwd_dir}:/home/user/work " fi port_arg="" if [ -n "$port" ]; then port_arg="-p $port:6080" fi debug_arg="" if [ -n "$debug" ]; then debug_arg=--env=APP="" fi docker run \ -d \ --name $container \ --privileged \ --workdir /home/user/work \ ${mount_local} \ $port_arg \ --env="APP=npm run test -- --browsers Chrome --dockered" \ $extra_run_args \ $debug_arg \ $image >/dev/null print_app_output() { docker cp $container:/var/log/supervisor/graphical-app-launcher.log - \ | tar xO result=$(docker cp $container:/tmp/graphical-app.return_code - \ | tar xO) cleanup exit $result } trap "docker stop $container >/dev/null && print_app_output" SIGINT SIGTERM docker wait $container >/dev/null print_app_output # vim: noexpandtab shiftwidth=4 tabstop=4 softtabstop=0 ================================================ FILE: test/test-ui-rollup.config.js ================================================ // Configuration for bundling the ReferenceUI and derivatives import autoprefixer from 'autoprefixer' import postcss from 'rollup-plugin-postcss' import svgo from 'rollup-plugin-svgo' import { nodeResolve } from '@rollup/plugin-node-resolve' import commonjs from '@rollup/plugin-commonjs' import { babel } from '@rollup/plugin-babel' import ignore from 'rollup-plugin-ignore' import typescript from '@rollup/plugin-typescript' export default { input: 'test/testUINoPlaneSliders.js', output: { file: 'test/testUINoPlaneSlidersBundle.js', format: 'es' }, plugins: [ typescript({ tsconfig: 'src/UI/reference-ui/tsconfig.json', compilerOptions: { declaration: false, declarationMap: false, }, }), // ignore crypto module ignore(['crypto']), commonjs({ transformMixedEsModules: true, }), babel({ include: ['src/UI/reference-ui/src/**', 'test/testUINoPlaneSliders.js'], extensions: ['.js'], exclude: 'node_modules/**', skipPreflightCheck: true, babelHelpers: 'bundle', }), svgo({ raw: true }), postcss({ modules: true, plugins: [autoprefixer], }), nodeResolve({ preferBuiltins: false, browser: true, }), ], } ================================================ FILE: test/testUINoPlaneSliders.js ================================================ import './customElementsDefineOverride.js' import referenceUIMachineOptions from '../src/UI/reference-ui/src/referenceUIMachineOptions.js' import style from '../src/UI/reference-ui/src/ItkVtkViewer.module.css' import createScreenshotButton from '../src/UI/reference-ui/src/Main/createScreenshotButton.js' import createFullscreenButton from '../src/UI/reference-ui/src/Main/createFullscreenButton.js' import createRotateButton from '../src/UI/reference-ui/src/Main/createRotateButton.js' import createAnnotationsButton from '../src/UI/reference-ui/src/Main/createAnnotationsButton.js' import createAxesButton from '../src/UI/reference-ui/src/Main/createAxesButton.js' import createViewPlanesToggle from '../src/UI/reference-ui/src/Main/createViewPlanesToggle.js' import createPlaneSliders from '../src/UI/reference-ui/src/Main/createPlaneSliders.js' import createBackgroundColorButton from '../src/UI/reference-ui/src/Main/createBackgroundColorButton.js' import createCroppingButtons from '../src/UI/reference-ui/src/Main/createCroppingButtons.js' import createViewModeButtons from '../src/UI/reference-ui/src/Main/createViewModeButtons.js' import createResetCameraButton from '../src/UI/reference-ui/src/Main/createResetCameraButton.js' function modifiedCreateMainInterface(context) { const mainUIGroup = document.createElement('div') mainUIGroup.setAttribute('class', style.uiGroup) context.uiGroups.set('main', mainUIGroup) const mainUIRow1 = document.createElement('div') mainUIRow1.setAttribute('class', style.mainUIRow) mainUIGroup.appendChild(mainUIRow1) createScreenshotButton(context, mainUIRow1) // Leave out the fullscreen button //createFullscreenButton(context, mainUIRow1) //if (!context.use2D) { //createRotateButton(context, mainUIRow1) //} //createAnnotationsButton(context, mainUIRow1) createAxesButton(context, mainUIRow1) createViewPlanesToggle(context, mainUIRow1) // Leave out the plane sliders // createPlaneSliders(context) createBackgroundColorButton(context, mainUIRow1) const mainUIRow2 = document.createElement('div') mainUIRow2.setAttribute('class', style.mainUIRow) if (context.use2D) { createCroppingButtons(context, mainUIRow1) createViewModeButtons(context, mainUIRow2) createResetCameraButton(context, mainUIRow1) } else { createCroppingButtons(context, mainUIRow2) createViewModeButtons(context, mainUIRow2) createResetCameraButton(context, mainUIRow2) mainUIGroup.appendChild(mainUIRow2) } context.uiContainer.appendChild(mainUIGroup) } const uiMachineOptions = { ...referenceUIMachineOptions } const testUIMainActions = { ...uiMachineOptions.main.actions } testUIMainActions.createMainInterface = modifiedCreateMainInterface const testUIMain = { ...uiMachineOptions.main } testUIMain.actions = testUIMainActions uiMachineOptions.main = testUIMain export default uiMachineOptions ================================================ FILE: test/tests.js ================================================ import './convertItkImageToVtkImageTest.js' import './createViewerTest.js' import './pipelineTest.js' import './imjoyTest.js' import './processFilesTest.js' import './zarrTest.js' import './conglomerateTest.js' import './multiscaleSpatialImageTest.js' ================================================ FILE: test/zarrImageBaselines.js ================================================ const SAMPLE_SIZE = 33 export const takeSnapshot = ({ data, ...rest }) => { const innerOffset = data.length / 2 const baseline = { ...rest, data: [ ...data.slice(0, SAMPLE_SIZE), ...data.slice(innerOffset, innerOffset + SAMPLE_SIZE), ...data.slice(-SAMPLE_SIZE), ], } return JSON.stringify(baseline) } const IMAGE_BASELINES = [ { path: 'base/test/data/input/astronaut.zarr', baseline: '{"imageType":{"dimension":2,"pixelType":"VariableLengthVector","componentType":"float32","components":3},"origin":[0,0],"spacing":[8,8],"direction":{"0":1,"1":0,"2":0,"3":1},"size":[256,256],"ranges":[[0,0.9763471484184265],[0,0.9741398692131042],[0,0.9760480523109436]],"data":[0.6908552050590515,0.6688187122344971,0.674207329750061,0.27221277356147766,0.2442161589860916,0.30864495038986206,0.04838373512029648,0.021947167813777924,0.11139355599880219,0.12586522102355957,0.08368028700351715,0.2073654979467392,0.29955506324768066,0.26995500922203064,0.3033413887023926,0.4970903694629669,0.46638235449790955,0.4774773120880127,0.6510546803474426,0.6293986439704895,0.6291141510009766,0.652686595916748,0.6369064450263977,0.6407243609428406,0.6563014984130859,0.645758330821991,0.6505763530731201,0.6664388179779053,0.6574797630310059,0.6628299951553345,0.6745269298553467,0.6657060384750366,0.6709856390953064,0.702728271484375,0.6728822588920593,0.6759136915206909,0.3145504593849182,0.27835237979888916,0.33366164565086365,0.12892097234725952,0.08835494518280029,0.16398540139198303,0.18125581741333008,0.09787159413099289,0.20692530274391174,0.32659101486206055,0.250407338142395,0.27853235602378845,0.5059590339660645,0.4235151708126068,0.41403526067733765,0.64797443151474,0.5656358003616333,0.5444427728652954,0.650419294834137,0.5713972449302673,0.5587600469589233,0.6266179084777832,0.5702376365661621,0.5665257573127747,0.6173701882362366,0.57239830493927,0.5740906596183777,0.6674000024795532,0.6029467582702637,0.594350278377533,1.0168959424516899e-11,1.3201037485366385e-11,7.667253984489086e-12,6.150961359176199e-13,6.375242673183068e-13,1.862368982988305e-12,2.6472281398209896e-13,2.687519803055982e-13,1.9147365442305497e-13,8.121141834103313e-15,7.599267894640993e-15,1.278036429805526e-14,1.6719174311674578e-7,1.4385383906301286e-7,1.528643451820244e-7,0.0015648636035621166,0.0013919497141614556,0.0013295512180775404,0.10193789750337601,0.09551996737718582,0.08835914731025696,0.20549945533275604,0.19322900474071503,0.15210646390914917,0.15297368168830872,0.13750818371772766,0.10394243896007538,0.22972600162029266,0.21356536448001862,0.18998141586780548,0.17018358409404755,0.15841108560562134,0.15074452757835388]}', }, { path: 'base/test/data/input/ome-ngff-prototypes/single_image/v0.4/cyx.ome.zarr', baseline: '{"imageType":{"dimension":2,"pixelType":"VariableLengthVector","componentType":"uint16","components":4},"name":"cyx","origin":[0,0],"spacing":[4,4],"direction":{"0":1,"1":0,"2":0,"3":1},"size":[256,232],"ranges":[[525,6345],[2061,18828],[972,3957],[1352,2986]],"data":[539,2120,1266,1554,550,2170,1141,1607,539,2067,1118,1522,543,2103,995,1428,539,2176,1004,1684,549,2118,1006,1556,547,2290,984,1467,540,2097,1021,1478,542,574,3233,1303,1862,573,3283,1349,1990,583,3304,1329,2004,577,3384,1322,1881,587,3459,1420,2104,583,3419,1366,2012,582,3266,1441,2126,582,3366,1490,2007,585,1628,542,2608,1114,1568,548,2561,1043,1622,545,2563,1058,1580,550,2626,1108,1821,548,2514,1081,1647,542,2552,1112,1639,545,2506,1026,1599,549,2517,1027,1586]}', }, { path: 'base/test/data/input/ome-ngff-prototypes/single_image/v0.4/tczyx.ome.zarr', baseline: '{"imageType":{"dimension":3,"pixelType":"VariableLengthVector","componentType":"int16","components":2},"name":"tczyx","origin":[0,0,0],"spacing":[2.5999999046325684,2.5999999046325684,4],"direction":{"0":1,"1":0,"2":0,"3":0,"4":1,"5":0,"6":0,"7":0,"8":1},"size":[128,66,122],"ranges":[[0,5115],[0,280]],"data":[6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}', }, { path: 'base/test/data/input/ome-ngff-prototypes/single_image/v0.4/zyx.ome.zarr', baseline: '{"imageType":{"dimension":3,"pixelType":"Scalar","componentType":"uint8","components":1},"name":"zyx","origin":[0,0,0],"spacing":[256,256,256],"direction":{"0":1,"1":0,"2":0,"3":0,"4":1,"5":0,"6":0,"7":0,"8":1},"size":[121,98,151],"ranges":[[0,238]],"data":[146,147,147,146,145,145,145,145,145,145,142,140,140,142,143,143,143,143,141,140,137,133,131,129,128,127,126,125,124,121,118,115,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,124,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}', }, { path: 'base/test/data/input/ome-ngff-prototypes/single_image/v0.1/zyx.ome.zarr', baseline: '{"imageType":{"dimension":3,"pixelType":"Scalar","componentType":"uint8","components":1},"name":"zyx","origin":[0,0,0],"spacing":[3.9917354583740234,4.010204315185547,3.993377447128296],"direction":{"0":1,"1":0,"2":0,"3":0,"4":1,"5":0,"6":0,"7":0,"8":1},"size":[121,98,151],"ranges":[[0,238]],"data":[146,147,147,146,145,145,145,145,145,145,142,140,140,142,143,143,143,143,141,140,137,133,131,129,128,127,126,125,124,121,118,115,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,124,126,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}', }, { path: 'base/test/data/input/ome-ngff-prototypes/single_image/v0.1/tczyx.ome.zarr', baseline: '{"imageType":{"dimension":3,"pixelType":"VariableLengthVector","componentType":"int16","components":2},"name":"tczyx","origin":[0,0,0],"spacing":[4,3.9696969985961914,3.9836065769195557],"direction":{"0":1,"1":0,"2":0,"3":0,"4":1,"5":0,"6":0,"7":0,"8":1},"size":[128,66,122],"ranges":[[0,5115],[0,280]],"data":[6,6,6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}', }, { path: 'base/test/data/input/ome-ngff-prototypes/single_image/v0.1/cyx.ome.zarr', baseline: '{"imageType":{"dimension":2,"pixelType":"VariableLengthVector","componentType":"uint16","components":4},"name":"cyx","origin":[0,0],"spacing":[4,4.008620738983154],"direction":{"0":1,"1":0,"2":0,"3":1},"size":[256,232],"ranges":[[525,6345],[2061,18828],[972,3957],[1352,2986]],"data":[539,2120,1266,1554,550,2170,1141,1607,539,2067,1118,1522,543,2103,995,1428,539,2176,1004,1684,549,2118,1006,1556,547,2290,984,1467,540,2097,1021,1478,542,574,3233,1303,1862,573,3283,1349,1990,583,3304,1329,2004,577,3384,1322,1881,587,3459,1420,2104,583,3419,1366,2012,582,3266,1441,2126,582,3366,1490,2007,585,1628,542,2608,1114,1568,548,2561,1043,1622,545,2563,1058,1580,550,2626,1108,1821,548,2514,1081,1647,542,2552,1112,1639,545,2506,1026,1599,549,2517,1027,1586]}', }, { path: 'base/test/data/input/ome-ngff-prototypes/single_image/v0.4/yx.ome.zarr', baseline: '{"imageType":{"dimension":2,"pixelType":"Scalar","componentType":"uint16","components":1},"name":"yx","origin":[0,0],"spacing":[4,4],"direction":{"0":1,"1":0,"2":0,"3":1},"size":[256,232],"ranges":[[525,6345]],"data":[539,550,539,543,539,549,547,540,542,537,540,541,532,548,543,543,541,545,540,543,546,544,545,545,548,553,544,549,546,545,551,547,553,574,573,583,577,587,583,582,582,585,594,584,584,585,594,583,597,598,596,598,592,593,588,597,598,598,598,608,607,600,592,612,599,607,570,560,555,554,558,561,553,554,557,550,554,566,553,546,554,544,555,548,550,542,551,549,548,549,553,542,548,545,550,548,542,545,549]}', }, { path: 'base/test/data/input/idr/5025551.zarr', baseline: '{"imageType":{"dimension":2,"pixelType":"VariableLengthVector","componentType":"uint8","components":27},"origin":[0,0],"spacing":[16,16],"direction":{"0":1,"1":0,"2":0,"3":1},"size":[168,168],"ranges":[[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255],[0,255]],"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,22,29,0,0,0,0,0,0,0,56,0,0,0,0,0,55,0,42,30,36,0,46,0,6,4,23,212,28,0,0,0,0,0,197,0,119,16,145,0,0,7,0,0,0,0,17,0,0,0,0,0,0,0,85,83,0,0,60,36,0,0,0,156,1,151,0]}', }, ] const RESOURCE_ERROR_CAUSING_DOCKER = [ { path: 'base/test/data/input/idr/6001240.zarr', baseline: '{"imageType":{"dimension":3,"pixelType":"VariableLengthVector","componentType":"uint16","components":2},"origin":[0,0,0],"spacing":[4.04477596282959,4.044117450714111,1],"direction":{"0":1,"1":0,"2":0,"3":0,"4":1,"5":0,"6":0,"7":0,"8":1},"size":[67,68,236],"ranges":[[5,4095],[24,2092]],"data":[8,28,8,28,9,27,9,28,9,27,9,27,10,29,9,27,9,28,9,26,10,28,9,26,8,27,9,28,10,26,7,27,10,8,27,8,31,9,28,10,28,8,27,9,28,8,28,10,27,19,28,68,27,23,29,9,26,9,27,7,27,8,28,8,27,8,37,9,28,9,34,23,44,7,27,8,31,9,28,10,28,9,28,7,28,9,27,8,39,7,28,8,27,9,34,8,30,9,45]}', }, { path: 'base/test/data/input/idr/6001240_v3.zarr', baseline: '{"imageType":{"dimension":3,"pixelType":"VariableLengthVector","componentType":"uint16","components":2},"origin":[0,0,0],"spacing":[1,1,1],"direction":{"0":1,"1":0,"2":0,"3":0,"4":1,"5":0,"6":0,"7":0,"8":1},"size":[67,68,236],"ranges":[[5,4095],[24,2092]],"data":[8,28,8,28,9,27,9,28,9,27,9,27,10,29,9,27,9,28,9,26,10,28,9,26,8,27,9,28,10,26,7,27,10,8,27,8,31,9,28,10,28,8,27,9,28,8,28,10,27,19,28,68,27,23,29,9,26,9,27,7,27,8,28,8,27,8,37,9,28,9,34,23,44,7,27,8,31,9,28,10,28,9,28,7,28,9,27,8,39,7,28,8,27,9,34,8,30,9,45]}', }, ] export const getBaselines = () => // eslint-disable-next-line no-undef __karma__.config.args.includes('--dockered') ? IMAGE_BASELINES : [...IMAGE_BASELINES, ...RESOURCE_ERROR_CAUSING_DOCKER] ================================================ FILE: test/zarrTest.js ================================================ import { IntTypes, PixelTypes } from 'itk-wasm' import test from 'tape-catch' const testZarrV1 = 'base/test/data/input/64x64-fake-v0.1.zarr/0' const testZarrV4 = 'base/test/data/input/64x64-fake-v0.4.zarr/0' import HttpStore from '../src/IO/HttpStore' import ZarrStoreParser from '../src/IO/ZarrStoreParser' import toMultiscaleSpatialImage from '../src/IO/toMultiscaleSpatialImage' import ZarrMultiscaleSpatialImage, { computeTransform, isZarr, } from '../src/IO/ZarrMultiscaleSpatialImage' import { getBaselines, takeSnapshot } from './zarrImageBaselines' const verifyImage = (t, image, msgPrefix = '') => { const imageTypeBaseline = { dimension: 2, pixelType: PixelTypes.Scalar, componentType: IntTypes.UInt8, components: 1, } t.deepEqual(image.imageType, imageTypeBaseline, msgPrefix + ' image type set') t.deepEqual(image.origin, [0, 0], msgPrefix + ' image origin set') t.deepEqual(image.spacing, [1, 1], msgPrefix + ' image spacing set') t.deepEqual(image.size, [64, 64], msgPrefix + ' image size set') t.equal(image.data.length, 4096, msgPrefix + ' image data length set') } test('Test isZarr', t => { t.true(isZarr('foo.zarr'), 'no suffix') t.true(isZarr('foo.zarr/0'), '/0 suffix') t.true(isZarr('foo.zarr/asdf'), '/asdf suffix') t.true(isZarr('foo/zarr/asdf'), 'no dot before zarr') t.true( isZarr( 'https://dandiarchive.s3.amazonaws.com/zarr/3d313fc2-0204-496d-bfa1-5c90951ee640' ), 'real url' ) t.true(isZarr('https://site.com/zarr/a/b'), 'deep nested image') t.true( isZarr('https://site.com/foo.zarr/a/b'), 'deep nested image with zarr as extension' ) t.false(isZarr('zarr.asdf'), 'not when .asdf extension') t.false( isZarr('barzarr.asdf'), 'not when .asdf extension withfilename barzarr' ) t.false( isZarr('barzarr.asdf/asdf'), 'not when .asdf extension and has suffix' ) t.false(isZarr('foo.zarrX'), '.zarrX extension, not .zarr') t.end() }) test('Test ZarrStoreParser', async t => { const storeURL = new URL(testZarrV4, document.location.origin) const httpStore = new HttpStore(storeURL) const zarrStoreParser = new ZarrStoreParser(httpStore) const topZattrsBaseline = { multiscales: [ { axes: [ { name: 'y', type: 'space', units: 'micrometer', }, { name: 'x', type: 'space', units: 'micrometer', }, ], datasets: [ { path: '0', coordinateTransformations: [ { scale: [1, 1], type: 'scale', }, ], }, ], name: 'testimage', version: '0.4', }, ], } const topZattrs = await zarrStoreParser.getItem('.zattrs') t.deepEqual(topZattrs, topZattrsBaseline, 'getItem top .zattrs') const arrayBaseline = { chunks: [64, 64], compressor: { clevel: 5, blocksize: 0, shuffle: 1, cname: 'lz4', id: 'blosc', }, dtype: '>u1', fill_value: 0, filters: null, order: 'C', shape: [64, 64], zarr_format: 2, dimension_separator: '/', } const firstArrayPath = topZattrs.multiscales[0].datasets[0].path const arrayMetadataPath = `${firstArrayPath}/.zarray` const arrayMetadata = await zarrStoreParser.getItem(arrayMetadataPath) t.deepEqual(arrayMetadata, arrayBaseline, 'getItem .zarray') const { dimension_separator: separator } = arrayMetadata const firstChunkPath = `${firstArrayPath}${separator}0${separator}0` const firstChunk = await zarrStoreParser.getItem(firstChunkPath) t.equal(firstChunk.byteLength, 128, 'getItem of chunk data has bytes') t.end() }) test('Test ZarrMultiscaleSpatialImage metadata fetching', async t => { const versionTests = [ [testZarrV1, 'v0.1'], [testZarrV4, 'v0.4'], ].map(async ([filePath, version]) => { const storeURL = new URL(filePath, document.location.origin) const zarrImage = await ZarrMultiscaleSpatialImage.fromUrl(storeURL) t.equal(zarrImage.scaleInfo.length, 1, `${version} number of scales`) const viewerImage = await zarrImage.getImage(0) verifyImage(t, viewerImage, version) }) await Promise.all(versionTests) t.end() }) test('Test ZarrMultiscaleSpatialImage chunk assembly', async t => { for (const { path, baseline } of getBaselines()) { const storeURL = new URL(path, document.location.origin) const zarrImage = await ZarrMultiscaleSpatialImage.fromUrl(storeURL) const itkImage = await zarrImage.getImage(zarrImage.scaleInfo.length - 1) t.deepEqual( takeSnapshot(itkImage), baseline, `${path} image matches baseline` ) } t.end() }) test('Test toMultiscaleSpatialImage from store', async t => { const storeURL = new URL(testZarrV4, document.location.origin) const zarrImage = await toMultiscaleSpatialImage(new HttpStore(storeURL)) const viewerImage = await zarrImage.getImage(0) verifyImage(t, viewerImage) t.end() }) test('Test ngff metadata coordinate transformations image and dataset interaction', t => { const { scale, translation } = computeTransform( { coordinateTransformations: [ { scale: [1, 2, 5], type: 'scale', }, ], }, { coordinateTransformations: [ { translation: [1, 1, 2], type: 'translation', }, ], }, 3 ) t.deepEqual(scale, [1, 2, 5], 'dataset scale computed') t.deepEqual(translation, [1, 2, 10], 'dataset translation computed') t.end() }) test('Test ngff metadata coordinate transformations missing image coordinateTransformations', t => { const { scale, translation } = computeTransform( {}, { coordinateTransformations: [ { translation: [3, 3, 3], type: 'translation', }, { scale: [2, 2, 2], type: 'scale', }, ], }, 3 ) t.deepEqual(scale, [2, 2, 2], 'dataset scale computed') t.deepEqual(translation, [6, 6, 6], 'dataset translation computed') t.end() }) test('Test ngff metadata coordinate transformations stacking', t => { const { scale, translation } = computeTransform( {}, { coordinateTransformations: [ { scale: [1, 1, 2], type: 'scale', }, { translation: [1, 1, 2], type: 'translation', }, { scale: [2, 1, 1], type: 'scale', }, { translation: [1, 1, 2], type: 'translation', }, ], }, 3 ) t.deepEqual(scale, [2, 1, 2], 'dataset scale computed') t.deepEqual(translation, [3, 2, 4], 'dataset translation computed') t.end() }) const REAL_TRANSFORMATIONS = { coordinateTransformations: [ { scale: [10, 1, 1, 1, 1], type: 'scale', }, { translation: [2, 0, 0, 0, 2], type: 'translation', }, ], datasets: [ { coordinateTransformations: [ { scale: [1, 1, 1.0, 0.65, 0.65], type: 'scale', }, ], path: 's0', }, { coordinateTransformations: [ { scale: [1, 1, 2.0, 1.3, 1.3], type: 'scale', }, ], path: 's1', }, { coordinateTransformations: [ { scale: [1, 1, 4.0, 2.6, 2.6], type: 'scale', }, ], path: 's2', }, ], } test('Test ngff metadata coordinate transformations with real JSON', t => { const imageMetadata = REAL_TRANSFORMATIONS const datasetMetadata = REAL_TRANSFORMATIONS.datasets[2] const { scale, translation } = computeTransform( imageMetadata, datasetMetadata, 5 ) t.deepEqual(scale, [10, 1, 4.0, 2.6, 2.6], 'dataset scale computed') t.deepEqual(translation, [2, 0, 0, 0, 2], 'dataset translation computed') t.end() }) ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "target": "ESNext", "module": "ESNext", "lib": ["ESNext", "DOM"], "moduleResolution": "Node", "strict": true, "sourceMap": true, "resolveJsonModule": true, "esModuleInterop": true, "noEmit": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true }, "include": ["src/**/*"], "exclude": ["src/UI/reference-ui/src/**"] } ================================================ FILE: webpack.config.js ================================================ /* eslint-env node */ const webpack = require('webpack') const path = require('path') const CopyPlugin = require('copy-webpack-plugin') const { GenerateSW } = require('workbox-webpack-plugin') const WebPackBar = require('webpackbar') const entry = path.join(__dirname, './src/index.js') const vtkRules = require('vtk.js/Utilities/config/dependency.js').webpack.core .rules const cssRules = require('vtk.js/Utilities/config/dependency.js').webpack.css .rules const packageJSON = require('./package.json') const cdnPath = 'https://cdn.jsdelivr.net/npm/' const itkConfigCDN = path.resolve(__dirname, 'src', 'itkConfigCDN.js') const itkConfig = path.resolve(__dirname, 'src', 'itkConfig.js') const devServer = { port: 8082, devMiddleware: { writeToDisk: true, }, // serve test data, allowing this: data-url="test-data/astronaut.zarr" static: [ { publicPath: '/test-data', directory: path.join(__dirname, 'test', 'data', 'input'), staticOptions: { dotfiles: 'allow', }, }, { directory: path.join(__dirname, 'examples'), }, ], headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS', 'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization', }, } const fallback = { path: false, url: false, module: false, fs: false, stream: require.resolve('stream-browserify'), crypto: false, } // Should be same in karma.conf.js const moduleConfigRules = [ { test: /\.js$/, loader: 'babel-loader', dependency: { not: ['url'] } }, { test: /\.worker.js$/, use: [{ loader: 'worker-loader', options: { inline: 'no-fallback' } }], }, { test: /\.(png|jpg)$/, type: 'asset', parser: { dataUrlCondition: { maxSize: 128 * 1024 } }, }, // 128kb { test: /\.svg$/, type: 'asset/source' }, ].concat(vtkRules, cssRules) const performance = { maxAssetSize: 20000000, maxEntrypointSize: 20000000, } module.exports = (env, argv) => [ { name: 'itkVtkViewer.js progressive web app', module: { rules: moduleConfigRules.concat([ { test: entry, loader: 'expose-loader', options: { exposes: 'itkVtkViewer' }, }, ]), }, devtool: argv.mode === 'development' ? 'eval-source-map' : 'source-map', output: { filename: 'itkVtkViewer.js', }, resolve: { alias: { '../itkConfig.js': itkConfig, '../../itkConfig.js': itkConfig, }, fallback, }, plugins: [ new CopyPlugin({ patterns: [ { from: path.join( __dirname, 'node_modules', 'itk-wasm', 'dist', 'web-workers' ), to: path.join(__dirname, 'dist', 'itk', 'web-workers'), }, { from: path.join(__dirname, 'node_modules', 'itk-image-io'), to: path.join(__dirname, 'dist', 'itk', 'image-io'), }, { from: path.join(__dirname, 'node_modules', 'itk-mesh-io'), to: path.join(__dirname, 'dist', 'itk', 'mesh-io'), }, { from: path.join( __dirname, 'src', 'Compression', 'blosc-zarr', 'web-build' ), to: path.join(__dirname, 'dist', 'itk', 'pipeline'), }, { from: path.join( __dirname, 'src', 'IO', 'Downsample', 'emscripten-build' ), to: path.join(__dirname, 'dist', 'itk', 'pipeline'), }, { from: path.join( __dirname, 'src', 'IO', 'ResampleLabelImage', 'emscripten-build' ), to: path.join(__dirname, 'dist', 'itk', 'pipeline'), }, { from: path.join( __dirname, 'src', 'IO', 'Compare', 'emscripten-build' ), to: path.join(__dirname, 'dist', 'itk', 'pipeline'), }, ], }), // workbox plugin should be last plugin. Don't create in development to avoid warning with devServer --watch argv.mode !== 'development' ? new GenerateSW({ cacheId: 'itk-vtk-viewer-', cleanupOutdatedCaches: true, maximumFileSizeToCacheInBytes: 10000000, include: [/(\.js|\.html|\.jpg|\.png)$/], exclude: ['serviceWorker.js', /workbox-.*\.js/], swDest: path.join(__dirname, 'dist', 'serviceWorker.js'), runtimeCaching: [ { urlPattern: /(\.js|\.png|\.wasm)$/, handler: 'StaleWhileRevalidate', options: { cacheName: 'itk-vtk-viewer-StaleWhileRevalidate', expiration: { maxEntries: 50, maxAgeSeconds: 7 * 24 * 60 * 60 * 2, }, }, }, ], }) : undefined, new WebPackBar(), ].filter(Boolean), // filter removes optional workbox placeholder performance, devServer, }, { name: 'itkVtkViewerCDN.js