@l1yp/file-viewer 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/FileViewer.js +2 -0
- package/dist/FileViewer.js.map +1 -0
- package/dist/FileViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/FileViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/AppxViewer.js +2 -0
- package/dist/components/AppxViewer.js.map +1 -0
- package/dist/components/AppxViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/AppxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ArchiveBrowser.js +2 -0
- package/dist/components/ArchiveBrowser.js.map +1 -0
- package/dist/components/ArchiveBrowser.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/ArchiveBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/AudioViewer.js +2 -0
- package/dist/components/AudioViewer.js.map +1 -0
- package/dist/components/AudioViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/AudioViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/BinObjBrowser.js +2 -0
- package/dist/components/BinObjBrowser.js.map +1 -0
- package/dist/components/BinObjBrowser.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/BinObjBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/CertViewer.js +2 -0
- package/dist/components/CertViewer.js.map +1 -0
- package/dist/components/CertViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/CertViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ClassViewer.js +2 -0
- package/dist/components/ClassViewer.js.map +1 -0
- package/dist/components/ClassViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/ClassViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/CodeBlock.js +2 -0
- package/dist/components/CodeBlock.js.map +1 -0
- package/dist/components/CodeBlock.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/CodeBlock.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/CodeView.js +2 -0
- package/dist/components/CodeView.js.map +1 -0
- package/dist/components/CodeView.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/CodeView.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/CsvViewer.js +2 -0
- package/dist/components/CsvViewer.js.map +1 -0
- package/dist/components/CsvViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/CsvViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/DicomViewer.js +2 -0
- package/dist/components/DicomViewer.js.map +1 -0
- package/dist/components/DicomViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/DicomViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/DocxNavigationPane.js +2 -0
- package/dist/components/DocxNavigationPane.js.map +1 -0
- package/dist/components/DocxNavigationPane.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/DocxNavigationPane.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/DocxViewer.js +2 -0
- package/dist/components/DocxViewer.js.map +1 -0
- package/dist/components/DocxViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/DocxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/DrawioViewer.js +2 -0
- package/dist/components/DrawioViewer.js.map +1 -0
- package/dist/components/DrawioViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/DrawioViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/DxfViewer.js +2 -0
- package/dist/components/DxfViewer.js.map +1 -0
- package/dist/components/DxfViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/DxfViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/EbookViewer.js +2 -0
- package/dist/components/EbookViewer.js.map +1 -0
- package/dist/components/EbookViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/EbookViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/EmlViewer.js +2 -0
- package/dist/components/EmlViewer.js.map +1 -0
- package/dist/components/EmlViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/EmlViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/EplViewer.js +2 -0
- package/dist/components/EplViewer.js.map +1 -0
- package/dist/components/EplViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/EplViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/FileViewerBody.js +3 -1
- package/dist/components/FileViewerBody.js.map +1 -0
- package/dist/components/FileViewerBody.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/FileViewerBody.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/FileViewerPanel.js +2 -0
- package/dist/components/FileViewerPanel.js.map +1 -0
- package/dist/components/FileViewerPanel.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/FileViewerPanel.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/FontViewer.js +2 -0
- package/dist/components/FontViewer.js.map +1 -0
- package/dist/components/FontViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/FontViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/FrontmatterCard.js +2 -0
- package/dist/components/FrontmatterCard.js.map +1 -0
- package/dist/components/FrontmatterCard.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/FrontmatterCard.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/GeoViewer.js +2 -0
- package/dist/components/GeoViewer.js.map +1 -0
- package/dist/components/GeoViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/GeoViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/HarViewer.js +2 -0
- package/dist/components/HarViewer.js.map +1 -0
- package/dist/components/HarViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/HarViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/HexViewer.js +2 -0
- package/dist/components/HexViewer.js.map +1 -0
- package/dist/components/HexViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/HexViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/HprofViewer.js +2 -0
- package/dist/components/HprofViewer.js.map +1 -0
- package/dist/components/HprofViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/HprofViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ImageLightbox.js +2 -0
- package/dist/components/ImageLightbox.js.map +1 -0
- package/dist/components/ImageLightbox.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/ImageLightbox.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InlineText.js +2 -0
- package/dist/components/InlineText.js.map +1 -0
- package/dist/components/JksViewer.js +2 -0
- package/dist/components/JksViewer.js.map +1 -0
- package/dist/components/JksViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/JksViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/JsonlViewer.js +3 -1
- package/dist/components/JsonlViewer.js.map +1 -0
- package/dist/components/JsonlViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/JsonlViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/LogViewer.js +2 -0
- package/dist/components/LogViewer.js.map +1 -0
- package/dist/components/LogViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/LogViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/LrcViewer.js +2 -0
- package/dist/components/LrcViewer.js.map +1 -0
- package/dist/components/LrcViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/LrcViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/MarkdownTable.js +2 -0
- package/dist/components/MarkdownTable.js.map +1 -0
- package/dist/components/MarkdownTable.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/MarkdownTable.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/MarkdownText.js +2 -0
- package/dist/components/MarkdownText.js.map +1 -0
- package/dist/components/MhtViewer.js +2 -0
- package/dist/components/MhtViewer.js.map +1 -0
- package/dist/components/MhtViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/MhtViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/MindMapView.js +2 -0
- package/dist/components/MindMapView.js.map +1 -0
- package/dist/components/MindMapView.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/MindMapView.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/MissingDependency.js +2 -0
- package/dist/components/MissingDependency.js.map +1 -0
- package/dist/components/MissingDependency.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/MissingDependency.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ModelViewer.js +2 -0
- package/dist/components/ModelViewer.js.map +1 -0
- package/dist/components/ModelViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/ModelViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/MsiViewer.js +2 -0
- package/dist/components/MsiViewer.js.map +1 -0
- package/dist/components/MsiViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/MsiViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/NotebookViewer.js +2 -0
- package/dist/components/NotebookViewer.js.map +1 -0
- package/dist/components/NotebookViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/NotebookViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/PdfNavigationPane.js +9 -0
- package/dist/components/PdfNavigationPane.js.map +1 -0
- package/dist/components/PdfNavigationPane.vue.d.ts +24 -0
- package/dist/components/PdfNavigationPane.vue_vue_type_script_setup_true_lang.js +149 -0
- package/dist/components/PdfNavigationPane.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/PdfViewer.js +3 -1
- package/dist/components/PdfViewer.js.map +1 -0
- package/dist/components/PdfViewer.vue.d.ts +0 -11
- package/dist/components/PdfViewer.vue_vue_type_script_setup_true_lang.js +133 -69
- package/dist/components/PdfViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/PptxNavigationPane.js +2 -0
- package/dist/components/PptxNavigationPane.js.map +1 -0
- package/dist/components/PptxNavigationPane.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/PptxNavigationPane.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/PptxViewer.js +3 -1
- package/dist/components/PptxViewer.js.map +1 -0
- package/dist/components/PptxViewer.vue_vue_type_script_setup_true_lang.js +55 -49
- package/dist/components/PptxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/PsdViewer.js +2 -0
- package/dist/components/PsdViewer.js.map +1 -0
- package/dist/components/PsdViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/PsdViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/QmcAudioViewer.js +2 -0
- package/dist/components/QmcAudioViewer.js.map +1 -0
- package/dist/components/QmcAudioViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/QmcAudioViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/RasterViewer.js +2 -0
- package/dist/components/RasterViewer.js.map +1 -0
- package/dist/components/RasterViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/RasterViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/RtfViewer.js +2 -0
- package/dist/components/RtfViewer.js.map +1 -0
- package/dist/components/RtfViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/RtfViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/SevenZipBrowser.js +2 -0
- package/dist/components/SevenZipBrowser.js.map +1 -0
- package/dist/components/SevenZipBrowser.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/SevenZipBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/SqliteViewer.js +2 -0
- package/dist/components/SqliteViewer.js.map +1 -0
- package/dist/components/SqliteViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/SqliteViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/SubtitleViewer.js +2 -0
- package/dist/components/SubtitleViewer.js.map +1 -0
- package/dist/components/SubtitleViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/SubtitleViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/XlsxChart.js +2 -0
- package/dist/components/XlsxChart.js.map +1 -0
- package/dist/components/XlsxChart.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/XlsxChart.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/XlsxViewer.js +2 -0
- package/dist/components/XlsxViewer.js.map +1 -0
- package/dist/components/XlsxViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/XlsxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/XmindViewer.js +2 -0
- package/dist/components/XmindViewer.js.map +1 -0
- package/dist/components/XmindViewer.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/XmindViewer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ZipBrowser.js +2 -0
- package/dist/components/ZipBrowser.js.map +1 -0
- package/dist/components/ZipBrowser.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/ZipBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ZoomImage.js +3 -1
- package/dist/components/ZoomImage.js.map +1 -0
- package/dist/components/ZoomImage.vue_vue_type_script_setup_true_lang.js +82 -80
- package/dist/components/ZoomImage.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/EplCodeBlock.js +2 -0
- package/dist/components/epl/EplCodeBlock.js.map +1 -0
- package/dist/components/epl/EplCodeBlock.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/epl/EplCodeBlock.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/EplDoc.js +2 -0
- package/dist/components/epl/EplDoc.js.map +1 -0
- package/dist/components/epl/EplDoc.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/epl/EplDoc.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/EplMethod.js +2 -0
- package/dist/components/epl/EplMethod.js.map +1 -0
- package/dist/components/epl/EplMethod.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/epl/EplMethod.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/EplResource.js +2 -0
- package/dist/components/epl/EplResource.js.map +1 -0
- package/dist/components/epl/EplResource.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/epl/EplResource.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/EplTree.js +2 -0
- package/dist/components/epl/EplTree.js.map +1 -0
- package/dist/components/epl/EplTree.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/epl/EplTree.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/EplVarTable.js +2 -0
- package/dist/components/epl/EplVarTable.js.map +1 -0
- package/dist/components/epl/EplVarTable.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/epl/EplVarTable.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/epl/numbering.js +2 -0
- package/dist/components/epl/numbering.js.map +1 -0
- package/dist/components/icons/Icon.js +2 -0
- package/dist/components/icons/Icon.js.map +1 -0
- package/dist/components/icons/Icon.vue_vue_type_script_setup_true_lang.js +2 -0
- package/dist/components/icons/Icon.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/icons/iconPaths.js +2 -0
- package/dist/components/icons/iconPaths.js.map +1 -0
- package/dist/components/optionalViewer.js +2 -0
- package/dist/components/optionalViewer.js.map +1 -0
- package/dist/components/ui/ClampBlock.js +2 -0
- package/dist/components/ui/ClampBlock.js.map +1 -0
- package/dist/composables/useElementNavigation.js +2 -0
- package/dist/composables/useElementNavigation.js.map +1 -0
- package/dist/composables/useFileViewer.js +2 -0
- package/dist/composables/useFileViewer.js.map +1 -0
- package/dist/composables/useLightbox.js +2 -0
- package/dist/composables/useLightbox.js.map +1 -0
- package/dist/composables/useLogRules.js +2 -0
- package/dist/composables/useLogRules.js.map +1 -0
- package/dist/lib/aes.js +2 -0
- package/dist/lib/aes.js.map +1 -0
- package/dist/lib/appx.js +2 -0
- package/dist/lib/appx.js.map +1 -0
- package/dist/lib/archiveHtml.js +2 -0
- package/dist/lib/archiveHtml.js.map +1 -0
- package/dist/lib/asn1.js +2 -0
- package/dist/lib/asn1.js.map +1 -0
- package/dist/lib/binobj.js +2 -0
- package/dist/lib/binobj.js.map +1 -0
- package/dist/lib/brotli.js +2 -0
- package/dist/lib/brotli.js.map +1 -0
- package/dist/lib/bzip2.js +2 -0
- package/dist/lib/bzip2.js.map +1 -0
- package/dist/lib/classfile.js +2 -0
- package/dist/lib/classfile.js.map +1 -0
- package/dist/lib/codePreview.js +2 -0
- package/dist/lib/codePreview.js.map +1 -0
- package/dist/lib/codefold.js +2 -0
- package/dist/lib/codefold.js.map +1 -0
- package/dist/lib/codeformat.js +2 -0
- package/dist/lib/codeformat.js.map +1 -0
- package/dist/lib/csv.js +2 -0
- package/dist/lib/csv.js.map +1 -0
- package/dist/lib/des.js +2 -0
- package/dist/lib/des.js.map +1 -0
- package/dist/lib/diagram.js +2 -0
- package/dist/lib/diagram.js.map +1 -0
- package/dist/lib/dicom.js +2 -0
- package/dist/lib/dicom.js.map +1 -0
- package/dist/lib/dicomCodecs.js +2 -0
- package/dist/lib/dicomCodecs.js.map +1 -0
- package/dist/lib/dicomRender.js +2 -0
- package/dist/lib/dicomRender.js.map +1 -0
- package/dist/lib/docxNavigation.js +2 -0
- package/dist/lib/docxNavigation.js.map +1 -0
- package/dist/lib/dxf.js +2 -0
- package/dist/lib/dxf.js.map +1 -0
- package/dist/lib/eml.js +2 -0
- package/dist/lib/eml.js.map +1 -0
- package/dist/lib/encoding.js +2 -0
- package/dist/lib/encoding.js.map +1 -0
- package/dist/lib/epl/code/codeDataParser.js +2 -0
- package/dist/lib/epl/code/codeDataParser.js.map +1 -0
- package/dist/lib/epl/code/codeLines.js +2 -0
- package/dist/lib/epl/code/codeLines.js.map +1 -0
- package/dist/lib/epl/code/textCode.js +2 -0
- package/dist/lib/epl/code/textCode.js.map +1 -0
- package/dist/lib/epl/eplDocument.js +2 -0
- package/dist/lib/epl/eplDocument.js.map +1 -0
- package/dist/lib/epl/imageList.js +2 -0
- package/dist/lib/epl/imageList.js.map +1 -0
- package/dist/lib/epl/index.js +2 -0
- package/dist/lib/epl/index.js.map +1 -0
- package/dist/lib/epl/io/binaryReader.js +2 -0
- package/dist/lib/epl/io/binaryReader.js.map +1 -0
- package/dist/lib/epl/io/encoding.js +2 -0
- package/dist/lib/epl/io/encoding.js.map +1 -0
- package/dist/lib/epl/model/eplSystemId.js +2 -0
- package/dist/lib/epl/model/eplSystemId.js.map +1 -0
- package/dist/lib/epl/names/eplLibData.js +2 -0
- package/dist/lib/epl/names/eplLibData.js.map +1 -0
- package/dist/lib/epl/names/idToNameMap.js +2 -0
- package/dist/lib/epl/names/idToNameMap.js.map +1 -0
- package/dist/lib/epl/names/libNameProvider.js +2 -0
- package/dist/lib/epl/names/libNameProvider.js.map +1 -0
- package/dist/lib/epl/program.js +2 -0
- package/dist/lib/epl/program.js.map +1 -0
- package/dist/lib/epl/projectFileReader.js +2 -0
- package/dist/lib/epl/projectFileReader.js.map +1 -0
- package/dist/lib/fb2.js +2 -0
- package/dist/lib/fb2.js.map +1 -0
- package/dist/lib/fileType.js +2 -0
- package/dist/lib/fileType.js.map +1 -0
- package/dist/lib/fonts.js +2 -0
- package/dist/lib/fonts.js.map +1 -0
- package/dist/lib/format.js +2 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/frontmatter.js +2 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/geo.js +2 -0
- package/dist/lib/geo.js.map +1 -0
- package/dist/lib/har.js +2 -0
- package/dist/lib/har.js.map +1 -0
- package/dist/lib/highlight.js +2 -0
- package/dist/lib/highlight.js.map +1 -0
- package/dist/lib/highlightLangs.js +2 -0
- package/dist/lib/highlightLangs.js.map +1 -0
- package/dist/lib/hprof.js +2 -0
- package/dist/lib/hprof.js.map +1 -0
- package/dist/lib/ipynb.js +2 -0
- package/dist/lib/ipynb.js.map +1 -0
- package/dist/lib/jks.js +2 -0
- package/dist/lib/jks.js.map +1 -0
- package/dist/lib/jsonl.js +2 -0
- package/dist/lib/jsonl.js.map +1 -0
- package/dist/lib/lrc.js +2 -0
- package/dist/lib/lrc.js.map +1 -0
- package/dist/lib/lzma.js +2 -0
- package/dist/lib/lzma.js.map +1 -0
- package/dist/lib/markdown/blocks.js +2 -0
- package/dist/lib/markdown/blocks.js.map +1 -0
- package/dist/lib/markdown/inline.js +2 -0
- package/dist/lib/markdown/inline.js.map +1 -0
- package/dist/lib/markdown/math.js +2 -0
- package/dist/lib/markdown/math.js.map +1 -0
- package/dist/lib/markdown/segmentize.js +2 -0
- package/dist/lib/markdown/segmentize.js.map +1 -0
- package/dist/lib/markdown/styles.js +2 -0
- package/dist/lib/markdown/styles.js.map +1 -0
- package/dist/lib/mht.js +2 -0
- package/dist/lib/mht.js.map +1 -0
- package/dist/lib/midi.js +2 -0
- package/dist/lib/midi.js.map +1 -0
- package/dist/lib/mime.js +2 -0
- package/dist/lib/mime.js.map +1 -0
- package/dist/lib/mindmap.js +2 -0
- package/dist/lib/mindmap.js.map +1 -0
- package/dist/lib/mobi.js +2 -0
- package/dist/lib/mobi.js.map +1 -0
- package/dist/lib/msi.js +2 -0
- package/dist/lib/msi.js.map +1 -0
- package/dist/lib/optionalDep.js +2 -0
- package/dist/lib/optionalDep.js.map +1 -0
- package/dist/lib/pdfNavigation.d.ts +11 -0
- package/dist/lib/pdfNavigation.js +32 -0
- package/dist/lib/pdfNavigation.js.map +1 -0
- package/dist/lib/pkcs12.js +2 -0
- package/dist/lib/pkcs12.js.map +1 -0
- package/dist/lib/pptxNavigation.js +2 -0
- package/dist/lib/pptxNavigation.js.map +1 -0
- package/dist/lib/qmc.js +2 -0
- package/dist/lib/qmc.js.map +1 -0
- package/dist/lib/rar.js +2 -0
- package/dist/lib/rar.js.map +1 -0
- package/dist/lib/rawPreview.js +2 -0
- package/dist/lib/rawPreview.js.map +1 -0
- package/dist/lib/rc2.js +2 -0
- package/dist/lib/rc2.js.map +1 -0
- package/dist/lib/resolveInput.js +2 -0
- package/dist/lib/resolveInput.js.map +1 -0
- package/dist/lib/rtf.js +2 -0
- package/dist/lib/rtf.js.map +1 -0
- package/dist/lib/sevenZip.js +2 -0
- package/dist/lib/sevenZip.js.map +1 -0
- package/dist/lib/sha1.js +2 -0
- package/dist/lib/sha1.js.map +1 -0
- package/dist/lib/sha256.js +2 -0
- package/dist/lib/sha256.js.map +1 -0
- package/dist/lib/speechAudio.js +2 -0
- package/dist/lib/speechAudio.js.map +1 -0
- package/dist/lib/sqlite.js +2 -0
- package/dist/lib/sqlite.js.map +1 -0
- package/dist/lib/subtitle.js +2 -0
- package/dist/lib/subtitle.js.map +1 -0
- package/dist/lib/tarball.js +2 -0
- package/dist/lib/tarball.js.map +1 -0
- package/dist/lib/viewerFormats.js +2 -0
- package/dist/lib/viewerFormats.js.map +1 -0
- package/dist/lib/viewerKind.js +2 -0
- package/dist/lib/viewerKind.js.map +1 -0
- package/dist/lib/x509.js +2 -0
- package/dist/lib/x509.js.map +1 -0
- package/dist/lib/xlsx.js +2 -0
- package/dist/lib/xlsx.js.map +1 -0
- package/dist/lib/xlsxCharts.js +2 -0
- package/dist/lib/xlsxCharts.js.map +1 -0
- package/dist/lib/xmind.js +2 -0
- package/dist/lib/xmind.js.map +1 -0
- package/dist/lib/zip.js +2 -0
- package/dist/lib/zip.js.map +1 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dicomCodecs.js","names":[],"sources":["../../src/lib/dicomCodecs.ts"],"sourcesContent":["/**\n * DICOM pixel-data decoding: turn one frame's transfer-syntax-encoded bytes into raw pixel samples.\n *\n * Browsers have no native DICOM/JPEG2000/JPEG-LS decoder, so compressed transfer syntaxes are decoded by the\n * cornerstone WASM codecs (openjpeg / charls / libjpeg-turbo), lazily `import()`-ed only when a compressed\n * frame is actually opened — so the main bundle and non-DICOM users pay nothing. Uncompressed and RLE are\n * handled here directly (RLE is a hand-rolled PackBits unpack — no dependency needed). The *predictive\n * lossless* JPEGs (Process 14 / SV1) are a different beast — libjpeg-turbo is DCT-only and cannot decode\n * them — so they go through `jpeg-lossless-decoder-js` (pure JS), also lazily imported.\n *\n * Output contract: {@link decodeDicomFrame} returns interleaved pixel-sample BYTES laid out exactly like an\n * uncompressed frame would be (little-endian for 16-bit), so the render layer (lib/dicomRender.ts) has a\n * single code path regardless of how the pixels were stored.\n */\nimport type { DicomMeta } from './dicom'\n\n/** Transfer Syntax UIDs we recognise. */\nconst TS = {\n IMPLICIT_LE: '1.2.840.10008.1.2',\n EXPLICIT_LE: '1.2.840.10008.1.2.1',\n EXPLICIT_BE: '1.2.840.10008.1.2.2',\n DEFLATED: '1.2.840.10008.1.2.1.99',\n RLE: '1.2.840.10008.1.2.5',\n JPEG_BASELINE: '1.2.840.10008.1.2.4.50',\n JPEG_EXTENDED: '1.2.840.10008.1.2.4.51',\n JPEG_LOSSLESS_57: '1.2.840.10008.1.2.4.57',\n JPEG_LOSSLESS_70: '1.2.840.10008.1.2.4.70',\n JPEGLS_LOSSLESS: '1.2.840.10008.1.2.4.80',\n JPEGLS_NEAR: '1.2.840.10008.1.2.4.81',\n J2K_LOSSLESS: '1.2.840.10008.1.2.4.90',\n J2K: '1.2.840.10008.1.2.4.91',\n} as const\n\nconst UNCOMPRESSED = new Set<string>([TS.IMPLICIT_LE, TS.EXPLICIT_LE, TS.EXPLICIT_BE])\n\n/** Human-readable transfer-syntax names for the info strip. */\nconst TS_NAMES: Record<string, string> = {\n [TS.IMPLICIT_LE]: '隐式 VR 小端(未压缩)',\n [TS.EXPLICIT_LE]: '显式 VR 小端(未压缩)',\n [TS.EXPLICIT_BE]: '显式 VR 大端(未压缩)',\n [TS.DEFLATED]: 'Deflated 显式 VR',\n [TS.RLE]: 'RLE 无损',\n [TS.JPEG_BASELINE]: 'JPEG Baseline',\n [TS.JPEG_EXTENDED]: 'JPEG Extended (12-bit)',\n [TS.JPEG_LOSSLESS_57]: 'JPEG Lossless (P14)',\n [TS.JPEG_LOSSLESS_70]: 'JPEG Lossless (P14 SV1)',\n [TS.JPEGLS_LOSSLESS]: 'JPEG-LS 无损',\n [TS.JPEGLS_NEAR]: 'JPEG-LS 近无损',\n [TS.J2K_LOSSLESS]: 'JPEG 2000 无损',\n [TS.J2K]: 'JPEG 2000',\n}\n\nexport function transferSyntaxName(uid: string): string {\n return TS_NAMES[uid] ?? uid\n}\n\nexport class UnsupportedTransferSyntaxError extends Error {\n constructor(uid: string) {\n super(`暂不支持的传输语法:${transferSyntaxName(uid)}(${uid})`)\n this.name = 'UnsupportedTransferSyntaxError'\n }\n}\n\nexport interface DecodedFrame {\n /** Interleaved pixel-sample bytes (little-endian for 16-bit), laid out like an uncompressed frame. */\n bytes: Uint8Array\n littleEndian: boolean\n bitsPerSample: number\n componentCount: number\n}\n\n// Lazy, memoised codec module promises — each WASM module instantiates once and is reused across frames.\nlet openjpegMod: Promise<DicomCodecModule> | null = null\nlet charlsMod: Promise<DicomCodecModule> | null = null\nlet libjpegMod: Promise<DicomCodecModule> | null = null\n\nfunction loadOpenJpeg(): Promise<DicomCodecModule> {\n openjpegMod ??= import('@cornerstonejs/codec-openjpeg/decode').then((m) => m.default())\n return openjpegMod\n}\nfunction loadCharls(): Promise<DicomCodecModule> {\n charlsMod ??= import('@cornerstonejs/codec-charls/decode').then((m) => m.default())\n return charlsMod\n}\nfunction loadLibjpeg(): Promise<DicomCodecModule> {\n libjpegMod ??= import('@cornerstonejs/codec-libjpeg-turbo-8bit/decode').then((m) => m.default())\n return libjpegMod\n}\n\n// Predictive-lossless JPEG (Process 14 / SV1) — a pure-JS decoder, since no WASM codec here handles it.\nlet jpegLosslessMod: Promise<typeof import('jpeg-lossless-decoder-js')> | null = null\nfunction loadJpegLossless(): Promise<typeof import('jpeg-lossless-decoder-js')> {\n jpegLosslessMod ??= import('jpeg-lossless-decoder-js')\n return jpegLosslessMod\n}\n\n// Run one decode through a WASM codec: copy encoded bytes in, decode, copy decoded bytes out (before the\n// instance — and its heap views — are freed).\nfunction runCodec(\n mod: DicomCodecModule,\n ctor: (new () => DicomCodecDecoder) | undefined,\n encoded: Uint8Array,\n): { bytes: Uint8Array; info: DicomCodecFrameInfo } {\n if (!ctor) throw new Error('codec 模块缺少解码器')\n const decoder = new ctor()\n try {\n const inBuf = decoder.getEncodedBuffer(encoded.length)\n inBuf.set(encoded)\n decoder.decode()\n const info = decoder.getFrameInfo()\n // Copy out of WASM memory — the view is invalidated by delete().\n const bytes = decoder.getDecodedBuffer().slice()\n return { bytes, info }\n } finally {\n decoder.delete()\n }\n}\n\n/** Decode one frame. Async because compressed transfer syntaxes lazy-load a WASM codec. */\nexport async function decodeDicomFrame(\n meta: DicomMeta,\n encoded: Uint8Array,\n): Promise<DecodedFrame> {\n const ts = meta.transferSyntaxUid\n\n if (UNCOMPRESSED.has(ts)) {\n return {\n bytes: encoded,\n littleEndian: ts !== TS.EXPLICIT_BE,\n bitsPerSample: meta.bitsAllocated,\n componentCount: meta.samplesPerPixel,\n }\n }\n\n if (ts === TS.RLE) {\n return {\n bytes: decodeRle(encoded, meta),\n littleEndian: true,\n bitsPerSample: meta.bitsAllocated,\n componentCount: meta.samplesPerPixel,\n }\n }\n\n // Predictive-lossless JPEG (Process 14, incl. SV1 first-order prediction). libjpeg-turbo/openjpeg can't\n // touch this codec family; the rii-mango decoder returns the samples as a Uint8/Uint16Array in native\n // (little-endian) byte order — exactly our interleaved-LE output contract.\n if (ts === TS.JPEG_LOSSLESS_57 || ts === TS.JPEG_LOSSLESS_70) {\n try {\n const { Decoder } = await loadJpegLossless()\n const decoder = new Decoder()\n const out = decoder.decode(encoded.buffer, encoded.byteOffset, encoded.byteLength)\n return {\n bytes: new Uint8Array(out.buffer, out.byteOffset, out.byteLength),\n littleEndian: true,\n bitsPerSample: (decoder.numBytes || meta.bitsAllocated / 8) * 8,\n componentCount: decoder.numComp || meta.samplesPerPixel,\n }\n } catch (e) {\n throw new Error(\n `${transferSyntaxName(ts)} 解码失败:${e instanceof Error ? e.message : String(e)}`,\n )\n }\n }\n\n let mod: DicomCodecModule\n let ctor: (new () => DicomCodecDecoder) | undefined\n switch (ts) {\n case TS.JPEG_BASELINE:\n case TS.JPEG_EXTENDED:\n mod = await loadLibjpeg()\n ctor = mod.JPEGDecoder\n break\n case TS.JPEGLS_LOSSLESS:\n case TS.JPEGLS_NEAR:\n mod = await loadCharls()\n ctor = mod.JpegLSDecoder\n break\n case TS.J2K_LOSSLESS:\n case TS.J2K:\n mod = await loadOpenJpeg()\n ctor = mod.J2KDecoder\n break\n default:\n throw new UnsupportedTransferSyntaxError(ts)\n }\n\n let info: DicomCodecFrameInfo\n let bytes: Uint8Array\n try {\n ;({ bytes, info } = runCodec(mod, ctor, encoded))\n } catch (e) {\n throw new Error(\n `${transferSyntaxName(ts)} 解码失败:${e instanceof Error ? e.message : String(e)}`,\n )\n }\n\n return {\n bytes,\n littleEndian: true,\n bitsPerSample: info.bitsPerSample || meta.bitsAllocated,\n componentCount: info.componentCount || meta.samplesPerPixel,\n }\n}\n\n// --- DICOM RLE (PackBits) ---------------------------------------------------------------------------------\n//\n// An RLE frame opens with a 64-byte header: a uint32 segment count followed by up to 15 uint32 segment\n// offsets (all little-endian). Each segment is PackBits-compressed and unpacks to rows*columns bytes. For\n// multi-byte samples the segments are ordered most-significant byte first; for colour, all byte-planes of\n// sample 0, then sample 1, … We reassemble them into the same interleaved little-endian layout an\n// uncompressed frame would have.\n\nfunction unpackBits(src: Uint8Array, expected: number): Uint8Array {\n const out = new Uint8Array(expected)\n let o = 0\n let i = 0\n while (i < src.length && o < expected) {\n const n = (src[i++] << 24) >> 24 // sign-extend the control byte to int8\n if (n >= 0) {\n const count = n + 1\n for (let k = 0; k < count && i < src.length && o < expected; k++) out[o++] = src[i++]\n } else if (n !== -128) {\n const count = 1 - n\n const val = i < src.length ? src[i++] : 0\n for (let k = 0; k < count && o < expected; k++) out[o++] = val\n }\n // n === -128 is a no-op per the PackBits spec.\n }\n return out\n}\n\nfunction decodeRle(encoded: Uint8Array, meta: DicomMeta): Uint8Array {\n const { rows, columns, samplesPerPixel, bitsAllocated } = meta\n const pixels = rows * columns\n const bytesPerSample = bitsAllocated / 8\n const dv = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength)\n const segCount = dv.getUint32(0, true)\n const offsets: number[] = []\n for (let i = 0; i < segCount; i++) offsets.push(dv.getUint32(4 + i * 4, true))\n\n const segs: Uint8Array[] = []\n for (let s = 0; s < segCount; s++) {\n const start = offsets[s]\n const end = s + 1 < segCount ? offsets[s + 1] : encoded.length\n segs.push(unpackBits(encoded.subarray(start, end), pixels))\n }\n\n const out = new Uint8Array(pixels * samplesPerPixel * bytesPerSample)\n for (let sp = 0; sp < samplesPerPixel; sp++) {\n for (let b = 0; b < bytesPerSample; b++) {\n const seg = segs[sp * bytesPerSample + b]\n if (!seg) continue\n // Segment byte-plane b is most-significant first → little-endian byte index within the sample.\n const leByte = bytesPerSample - 1 - b\n for (let p = 0; p < pixels; p++) {\n out[(p * samplesPerPixel + sp) * bytesPerSample + leByte] = seg[p]\n }\n }\n }\n return out\n}\n"],"mappings":"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"}
|
package/dist/lib/dicomRender.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dicomRender.js","names":[],"sources":["../../src/lib/dicomRender.ts"],"sourcesContent":["/**\n * DICOM pixel → screen rendering: stored samples to displayable RGBA.\n *\n * Splits into two steps so the interactive window/level (brightness/contrast) drag stays cheap:\n * 1. {@link buildFramePixels} — decode the raw bytes into per-pixel values ONCE per frame (apply the\n * modality rescale LUT, honour bit depth / signedness / endianness, separate grayscale vs RGB vs\n * palette). The expensive parse happens here.\n * 2. {@link renderToImageData} — apply the VOI window + invert and write RGBA. Cheap enough to re-run on\n * every drag frame, since it only maps already-decoded values.\n *\n * The VOI window mapping follows the DICOM PS3.3 C.11.2.1.2 LINEAR formula. MONOCHROME1 (0 = white) is\n * handled by inverting the grayscale ramp.\n */\nimport type { DecodedFrame } from './dicomCodecs'\nimport type { DicomMeta, DicomPalette } from './dicom'\n\nexport interface FramePixels {\n width: number\n height: number\n mode: 'gray' | 'rgb' | 'palette'\n /** Grayscale: per-pixel modality value (rescale slope/intercept already applied). */\n gray?: Float32Array\n /** Palette: per-pixel LUT index. */\n indices?: Int32Array\n palette?: DicomPalette\n /** RGB: interleaved R,G,B bytes (3 per pixel). */\n rgb?: Uint8Array\n /** Grayscale modality-value range, for auto-windowing. */\n minValue: number\n maxValue: number\n /** MONOCHROME1: low values display bright — the grayscale ramp is inverted. */\n monochrome1: boolean\n}\n\nexport interface WindowParams {\n windowCenter: number\n windowWidth: number\n invert: boolean\n}\n\nfunction isColor(meta: DicomMeta): boolean {\n return meta.samplesPerPixel >= 3\n}\n\n// Build per-pixel grayscale modality values from the raw sample bytes (honouring bit depth, the bitsStored\n// mask, two's-complement signedness, and byte order), then apply the modality rescale (slope/intercept).\nfunction buildGray(\n decoded: DecodedFrame,\n meta: DicomMeta,\n): {\n gray: Float32Array\n min: number\n max: number\n} {\n const n = meta.rows * meta.columns\n const gray = new Float32Array(n)\n const bytes = decoded.bytes\n const le = decoded.littleEndian\n const slope = meta.rescaleSlope\n const intercept = meta.rescaleIntercept\n const stored = meta.bitsStored\n const signed = meta.pixelRepresentation === 1\n const signBit = 1 << (stored - 1)\n const signSpan = 1 << stored\n let min = Infinity\n let max = -Infinity\n\n if (meta.bitsAllocated === 16) {\n const mask = stored >= 16 ? 0xffff : signSpan - 1\n for (let i = 0; i < n; i++) {\n const o = i * 2\n let raw = le ? bytes[o] | (bytes[o + 1] << 8) : bytes[o + 1] | (bytes[o] << 8)\n raw &= mask\n if (signed && raw & signBit) raw -= signSpan\n const v = raw * slope + intercept\n gray[i] = v\n if (v < min) min = v\n if (v > max) max = v\n }\n } else {\n const mask = stored >= 8 ? 0xff : signSpan - 1\n for (let i = 0; i < n; i++) {\n let raw = bytes[i] & mask\n if (signed && raw & signBit) raw -= signSpan\n const v = raw * slope + intercept\n gray[i] = v\n if (v < min) min = v\n if (v > max) max = v\n }\n }\n if (!Number.isFinite(min)) {\n min = 0\n max = 0\n }\n return { gray, min, max }\n}\n\nfunction buildPaletteIndices(decoded: DecodedFrame, meta: DicomMeta): Int32Array {\n const n = meta.rows * meta.columns\n const out = new Int32Array(n)\n const bytes = decoded.bytes\n const le = decoded.littleEndian\n if (meta.bitsAllocated === 16) {\n for (let i = 0; i < n; i++) {\n const o = i * 2\n out[i] = le ? bytes[o] | (bytes[o + 1] << 8) : bytes[o + 1] | (bytes[o] << 8)\n }\n } else {\n for (let i = 0; i < n; i++) out[i] = bytes[i]\n }\n return out\n}\n\n// Build interleaved RGB bytes, undoing planar configuration and converting YBR_FULL to RGB when needed.\n// (Codec output for JPEG/JPEG-LS is already interleaved RGB; this mainly covers uncompressed colour.)\nfunction buildRgb(decoded: DecodedFrame, meta: DicomMeta): Uint8Array {\n const n = meta.rows * meta.columns\n const src = decoded.bytes\n const out = new Uint8Array(n * 3)\n const planar = meta.planarConfiguration === 1\n if (planar) {\n // R plane, then G plane, then B plane.\n for (let i = 0; i < n; i++) {\n out[i * 3] = src[i]\n out[i * 3 + 1] = src[n + i]\n out[i * 3 + 2] = src[2 * n + i]\n }\n } else {\n out.set(src.subarray(0, n * 3))\n }\n const photo = meta.photometric\n if (photo === 'YBR_FULL' || photo === 'YBR_FULL_422') {\n for (let i = 0; i < n; i++) {\n const o = i * 3\n const y = out[o]\n const cb = out[o + 1] - 128\n const cr = out[o + 2] - 128\n out[o] = clamp8(y + 1.402 * cr)\n out[o + 1] = clamp8(y - 0.344136 * cb - 0.714136 * cr)\n out[o + 2] = clamp8(y + 1.772 * cb)\n }\n }\n return out\n}\n\nfunction clamp8(v: number): number {\n return v < 0 ? 0 : v > 255 ? 255 : v | 0\n}\n\n/** Decode one frame's bytes into per-pixel values (the expensive step; do this once per frame). */\nexport function buildFramePixels(decoded: DecodedFrame, meta: DicomMeta): FramePixels {\n const base = {\n width: meta.columns,\n height: meta.rows,\n minValue: 0,\n maxValue: 0,\n monochrome1: meta.photometric === 'MONOCHROME1',\n }\n if (meta.photometric === 'PALETTE COLOR' && meta.palette) {\n return {\n ...base,\n mode: 'palette',\n indices: buildPaletteIndices(decoded, meta),\n palette: meta.palette,\n }\n }\n if (isColor(meta)) {\n return { ...base, mode: 'rgb', rgb: buildRgb(decoded, meta) }\n }\n const { gray, min, max } = buildGray(decoded, meta)\n return { ...base, mode: 'gray', gray, minValue: min, maxValue: max }\n}\n\n/** A sensible default window: the file's own VOI when present, else the full modality-value range. */\nexport function autoWindow(fp: FramePixels): { center: number; width: number } {\n const width = Math.max(1, fp.maxValue - fp.minValue)\n const center = (fp.maxValue + fp.minValue) / 2\n return { center, width }\n}\n\n/** Apply the VOI window + invert and produce RGBA pixels for the canvas. Cheap — safe to call on each drag. */\nexport function renderToImageData(fp: FramePixels, params: WindowParams): ImageData {\n const { width, height } = fp\n const out = new Uint8ClampedArray(width * height * 4)\n\n if (fp.mode === 'gray' && fp.gray) {\n const c = params.windowCenter\n const w = params.windowWidth > 1 ? params.windowWidth : 1\n const lo = c - 0.5 - (w - 1) / 2\n const hi = c - 0.5 + (w - 1) / 2\n const denom = w - 1 || 1\n const center = c - 0.5\n // MONOCHROME1 XOR the user's invert toggle.\n const inv = params.invert !== fp.monochrome1\n const g = fp.gray\n for (let i = 0; i < g.length; i++) {\n const v = g[i]\n let y: number\n if (v <= lo) y = 0\n else if (v > hi) y = 255\n else y = ((v - center) / denom + 0.5) * 255\n if (inv) y = 255 - y\n const o = i * 4\n out[o] = y\n out[o + 1] = y\n out[o + 2] = y\n out[o + 3] = 255\n }\n } else if (fp.mode === 'palette' && fp.indices && fp.palette) {\n const { r, g, b } = fp.palette\n const last = r.length - 1\n const idx = fp.indices\n for (let i = 0; i < idx.length; i++) {\n const k = idx[i] < 0 ? 0 : idx[i] > last ? last : idx[i]\n const o = i * 4\n out[o] = r[k]\n out[o + 1] = g[k]\n out[o + 2] = b[k]\n out[o + 3] = 255\n }\n } else if (fp.mode === 'rgb' && fp.rgb) {\n const rgb = fp.rgb\n const n = width * height\n for (let i = 0; i < n; i++) {\n const o = i * 4\n const s = i * 3\n out[o] = rgb[s]\n out[o + 1] = rgb[s + 1]\n out[o + 2] = rgb[s + 2]\n out[o + 3] = 255\n }\n }\n\n return new ImageData(out, width, height)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docxNavigation.js","names":[],"sources":["../../src/lib/docxNavigation.ts"],"sourcesContent":["import type { ElementNavigationItem } from '@/composables/useElementNavigation'\n\ninterface DocxNode {\n type?: string\n styleName?: string\n outlineLevel?: number\n children?: DocxNode[]\n}\n\ninterface DocxStyle {\n id?: string | null\n name?: string | null\n basedOn?: string | null\n paragraphProps?: { outlineLevel?: number }\n}\n\nexport interface ParsedDocxModel {\n documentPart?: { body?: DocxNode }\n stylesPart?: { styles?: DocxStyle[] }\n}\n\nexport interface DocxNavigation {\n outline: ElementNavigationItem[]\n pages: ElementNavigationItem[]\n}\n\nconst HEADING_NAME =\n /(?:heading|标题|標題|titre|überschrift|titolo|encabezado|nagłówek)\\s*[-_. ]*([1-6])/i\n\nfunction cleanTitle(value: string | null | undefined): string {\n const text = (value ?? '').replace(/\\s+/g, ' ').trim()\n return text.length > 120 ? `${text.slice(0, 117)}…` : text\n}\n\nfunction normalizedOutlineLevel(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= 5\n ? value\n : undefined\n}\n\nfunction headingLevelFromName(...values: Array<string | null | undefined>): number | undefined {\n for (const value of values) {\n const match = value?.match(HEADING_NAME)\n if (match) return Number(match[1]) - 1\n }\n return undefined\n}\n\nfunction collectParagraphs(nodes: readonly DocxNode[], output: DocxNode[]): void {\n for (const node of nodes) {\n if (node.type === 'paragraph') {\n output.push(node)\n continue\n }\n if (node.children?.length) collectParagraphs(node.children, output)\n }\n}\n\nexport function buildDocxPageNavigation(renderRoot: HTMLElement): ElementNavigationItem[] {\n return Array.from(\n renderRoot.querySelectorAll<HTMLElement>('.docx-wrapper > section.docx'),\n (target, index): ElementNavigationItem => ({\n id: `docx-page-${index}`,\n title: `第 ${index + 1} 页`,\n level: 0,\n target,\n }),\n )\n}\n\n/** Build Word-style outline/page navigation from docx-preview's parsed model and rendered DOM. */\nexport function buildDocxNavigation(\n document: ParsedDocxModel,\n renderRoot: HTMLElement,\n): DocxNavigation {\n const rawStyles = document.stylesPart?.styles\n const styles = (Array.isArray(rawStyles) ? rawStyles : []).filter(\n (style): style is DocxStyle & { id: string } => !!style.id,\n )\n const stylesById = new Map(styles.map((style) => [style.id, style]))\n const resolvedLevels = new Map<string, number | undefined>()\n\n function styleLevel(styleId: string | undefined, seen = new Set<string>()): number | undefined {\n if (!styleId) return undefined\n if (resolvedLevels.has(styleId)) return resolvedLevels.get(styleId)\n if (seen.has(styleId)) return undefined\n seen.add(styleId)\n const style = stylesById.get(styleId)\n const level =\n normalizedOutlineLevel(style?.paragraphProps?.outlineLevel) ??\n headingLevelFromName(styleId, style?.id, style?.name) ??\n styleLevel(style?.basedOn ?? undefined, seen)\n resolvedLevels.set(styleId, level)\n return level\n }\n\n const paragraphs: DocxNode[] = []\n collectParagraphs(document.documentPart?.body?.children ?? [], paragraphs)\n const renderedParagraphs = Array.from(\n renderRoot.querySelectorAll<HTMLElement>('.docx-wrapper > section.docx > article p'),\n )\n const outline: ElementNavigationItem[] = []\n\n for (let index = 0; index < Math.min(paragraphs.length, renderedParagraphs.length); index += 1) {\n const paragraph = paragraphs[index]\n const target = renderedParagraphs[index]\n const level = normalizedOutlineLevel(paragraph.outlineLevel) ?? styleLevel(paragraph.styleName)\n const title = cleanTitle(target.textContent)\n if (level === undefined || !title) continue\n const id = `docx-outline-${outline.length}`\n target.dataset.documentNavId = id\n outline.push({ id, title, level, target })\n }\n\n const pages = buildDocxPageNavigation(renderRoot)\n\n return { outline, pages }\n}\n"],"mappings":";AA0BA,IAAM,IACJ;AAEF,SAAS,EAAW,GAA0C;CAC5D,IAAM,KAAQ,KAAS,GAAA,CAAI,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;CACrD,OAAO,EAAK,SAAS,MAAM,GAAG,EAAK,MAAM,GAAG,GAAG,EAAE,KAAK;AACxD;AAEA,SAAS,EAAuB,GAAoC;CAClE,OAAO,OAAO,KAAU,YAAY,OAAO,UAAU,CAAK,KAAK,KAAS,KAAK,KAAS,IAClF,IACA,KAAA;AACN;AAEA,SAAS,EAAqB,GAAG,GAA8D;CAC7F,KAAK,IAAM,KAAS,GAAQ;EAC1B,IAAM,IAAQ,GAAO,MAAM,CAAY;EACvC,IAAI,GAAO,OAAO,OAAO,EAAM,EAAE,IAAI;CACvC;AAEF;AAEA,SAAS,EAAkB,GAA4B,GAA0B;CAC/E,KAAK,IAAM,KAAQ,GAAO;EACxB,IAAI,EAAK,SAAS,aAAa;GAC7B,EAAO,KAAK,CAAI;GAChB;EACF;EACA,AAAI,EAAK,UAAU,UAAQ,EAAkB,EAAK,UAAU,CAAM;CACpE;AACF;AAEA,SAAgB,EAAwB,GAAkD;CACxF,OAAO,MAAM,KACX,EAAW,iBAA8B,8BAA8B,IACtE,GAAQ,OAAkC;EACzC,IAAI,aAAa;EACjB,OAAO,KAAK,IAAQ,EAAE;EACtB,OAAO;EACP;CACF,EACF;AACF;AAGA,SAAgB,EACd,GACA,GACgB;CAChB,IAAM,IAAY,EAAS,YAAY,QACjC,KAAU,MAAM,QAAQ,CAAS,IAAI,IAAY,CAAC,EAAA,CAAG,QACxD,MAA+C,CAAC,CAAC,EAAM,EAC1D,GACM,IAAa,IAAI,IAAI,EAAO,KAAK,MAAU,CAAC,EAAM,IAAI,CAAK,CAAC,CAAC,GAC7D,oBAAiB,IAAI,IAAgC;CAE3D,SAAS,EAAW,GAA6B,oBAAO,IAAI,IAAY,GAAuB;EAC7F,IAAI,CAAC,GAAS;EACd,IAAI,EAAe,IAAI,CAAO,GAAG,OAAO,EAAe,IAAI,CAAO;EAClE,IAAI,EAAK,IAAI,CAAO,GAAG;EACvB,EAAK,IAAI,CAAO;EAChB,IAAM,IAAQ,EAAW,IAAI,CAAO,GAC9B,IACJ,EAAuB,GAAO,gBAAgB,YAAY,KAC1D,EAAqB,GAAS,GAAO,IAAI,GAAO,IAAI,KACpD,EAAW,GAAO,WAAW,KAAA,GAAW,CAAI;EAE9C,OADA,EAAe,IAAI,GAAS,CAAK,GAC1B;CACT;CAEA,IAAM,IAAyB,CAAC;CAChC,EAAkB,EAAS,cAAc,MAAM,YAAY,CAAC,GAAG,CAAU;CACzE,IAAM,IAAqB,MAAM,KAC/B,EAAW,iBAA8B,0CAA0C,CACrF,GACM,IAAmC,CAAC;CAE1C,KAAK,IAAI,IAAQ,GAAG,IAAQ,KAAK,IAAI,EAAW,QAAQ,EAAmB,MAAM,GAAG,KAAS,GAAG;EAC9F,IAAM,IAAY,EAAW,IACvB,IAAS,EAAmB,IAC5B,IAAQ,EAAuB,EAAU,YAAY,KAAK,EAAW,EAAU,SAAS,GACxF,IAAQ,EAAW,EAAO,WAAW;EAC3C,IAAI,MAAU,KAAA,KAAa,CAAC,GAAO;EACnC,IAAM,IAAK,gBAAgB,EAAQ;EAEnC,AADA,EAAO,QAAQ,gBAAgB,GAC/B,EAAQ,KAAK;GAAE;GAAI;GAAO;GAAO;EAAO,CAAC;CAC3C;CAIA,OAAO;EAAE;EAAS,OAFJ,EAAwB,CAEpB;CAAM;AAC1B"}
|
package/dist/lib/dxf.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dxf.js","names":[],"sources":["../../src/lib/dxf.ts"],"sourcesContent":["/**\n * Hand-rolled, zero-dependency DXF (AutoCAD Drawing Exchange Format) parser for the file viewer — turns an\n * ASCII DXF into a flat list of drawable primitives the viewer draws as an SVG (in the codebase's hand-rolled\n * house style, like lib/geo.ts / lib/diagram.ts — no CAD runtime, no CDN).\n *\n * DXF is a stream of (group-code, value) line PAIRS grouped into SECTIONS. We read:\n * - TABLES → the LAYER table (name → ACI colour + on/off), so entities colour \"ByLayer\".\n * - BLOCKS → reusable geometry, keyed by name, expanded wherever an INSERT references them (with the\n * insert's translation / scale / rotation; nested INSERTs are expanded recursively, depth-capped).\n * - ENTITIES → the drawing itself.\n * Supported entities: LINE, LWPOLYLINE (incl. bulge arcs), POLYLINE/VERTEX, CIRCLE, ARC, ELLIPSE (tessellated),\n * POINT, TEXT / MTEXT, SOLID / 3DFACE / TRACE (filled), SPLINE (fit/control points as a polyline), INSERT.\n * Anything else (HATCH / DIMENSION / WIPEOUT / …) is skipped — the drawing still renders, minus that entity.\n *\n * Coordinates are kept in DXF world space (Y up); the viewer flips Y for SVG. Binary DXF is not supported.\n */\n\nexport interface DxfLayer {\n name: string\n color: string\n on: boolean\n}\n\nexport type DxfPrim =\n | { kind: 'line'; layer: string; color: string; x1: number; y1: number; x2: number; y2: number }\n | { kind: 'polyline'; layer: string; color: string; pts: [number, number][]; closed: boolean }\n | { kind: 'circle'; layer: string; color: string; cx: number; cy: number; r: number }\n | {\n kind: 'arc'\n layer: string\n color: string\n cx: number\n cy: number\n r: number\n a0: number\n a1: number\n }\n | { kind: 'polygon'; layer: string; color: string; pts: [number, number][] }\n | {\n kind: 'text'\n layer: string\n color: string\n x: number\n y: number\n h: number\n text: string\n rot: number\n align: 'l' | 'c' | 'r'\n }\n | { kind: 'point'; layer: string; color: string; x: number; y: number }\n\nexport interface DxfBounds {\n minX: number\n minY: number\n maxX: number\n maxY: number\n}\n\nexport interface ParsedDxf {\n prims: DxfPrim[]\n layers: DxfLayer[]\n bounds: DxfBounds | null\n counts: Record<string, number>\n}\n\n// AutoCAD Color Index → a legible hex on a light background (1-9 named colours + a grey ramp fallback).\nconst ACI: Record<number, string> = {\n 1: '#e03131',\n 2: '#f08c00',\n 3: '#2f9e44',\n 4: '#0c8599',\n 5: '#1971c2',\n 6: '#ae3ec9',\n 7: '#1a1a1a',\n 8: '#868e96',\n 9: '#adb5bd',\n}\nfunction aciColor(i: number): string {\n if (ACI[i]) return ACI[i]\n if (i >= 250 && i <= 255) {\n const g = Math.round(51 + ((i - 250) / 5) * 200)\n return `rgb(${g},${g},${g})`\n }\n return '#1a1a1a'\n}\n\n// ── tokenize ────────────────────────────────────────────────────────────────────────────────────\ninterface Pair {\n code: number\n value: string\n}\n\nfunction tokenize(text: string): Pair[] {\n const lines = text.split(/\\r\\n|\\r|\\n/)\n const pairs: Pair[] = []\n // Pairs are (code line, value line). Tolerate a trailing dangling code line.\n for (let i = 0; i + 1 < lines.length; i += 2) {\n const code = Number.parseInt(lines[i].trim(), 10)\n if (Number.isNaN(code)) {\n // Resync: skip one line (some exporters emit stray blank lines). Step back to keep parity.\n i -= 1\n continue\n }\n pairs.push({ code, value: lines[i + 1] })\n }\n return pairs\n}\n\ninterface Ent {\n type: string\n pairs: Pair[]\n}\n\n/** Split a [from,to) pair range into entities delimited by group-code-0 markers. */\nfunction splitEntities(pairs: Pair[], from: number, to: number): Ent[] {\n const ents: Ent[] = []\n let cur: Ent | null = null\n for (let i = from; i < to; i++) {\n const p = pairs[i]\n if (p.code === 0) {\n if (cur) ents.push(cur)\n cur = { type: p.value.trim(), pairs: [] }\n } else if (cur) {\n cur.pairs.push(p)\n }\n }\n if (cur) ents.push(cur)\n return ents\n}\n\nfunction first(ent: Ent, code: number): string | undefined {\n for (const p of ent.pairs) if (p.code === code) return p.value\n return undefined\n}\nfunction fnum(ent: Ent, code: number, def = 0): number {\n const v = first(ent, code)\n if (v === undefined) return def\n const n = Number.parseFloat(v.trim())\n return Number.isFinite(n) ? n : def\n}\nfunction inum(ent: Ent, code: number, def = 0): number {\n const v = first(ent, code)\n if (v === undefined) return def\n const n = Number.parseInt(v.trim(), 10)\n return Number.isFinite(n) ? n : def\n}\n\n// ── section ranges ────────────────────────────────────────────────────────────────────────────\nfunction sectionRanges(pairs: Pair[]): Map<string, [number, number]> {\n const out = new Map<string, [number, number]>()\n for (let i = 0; i + 1 < pairs.length; i++) {\n if (pairs[i].code === 0 && pairs[i].value.trim() === 'SECTION' && pairs[i + 1].code === 2) {\n const name = pairs[i + 1].value.trim()\n let j = i + 2\n while (j < pairs.length && !(pairs[j].code === 0 && pairs[j].value.trim() === 'ENDSEC')) j++\n out.set(name, [i + 2, j])\n }\n }\n return out\n}\n\n// ── layers ──────────────────────────────────────────────────────────────────────────────────\nfunction parseLayers(pairs: Pair[], range: [number, number] | undefined): Map<string, DxfLayer> {\n const layers = new Map<string, DxfLayer>()\n if (!range) return layers\n for (const ent of splitEntities(pairs, range[0], range[1])) {\n if (ent.type !== 'LAYER') continue\n const name = (first(ent, 2) ?? '').trim()\n if (!name) continue\n const ci = inum(ent, 62, 7)\n layers.set(name, { name, color: aciColor(Math.abs(ci)), on: ci >= 0 })\n }\n return layers\n}\n\n// ── affine transform (for INSERT expansion) ──────────────────────────────────────────────────\ninterface Mat {\n a: number\n b: number\n c: number\n d: number\n e: number\n f: number\n} // x' = a*x + c*y + e ; y' = b*x + d*y + f\nconst IDENT: Mat = { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }\nfunction apply(m: Mat, x: number, y: number): [number, number] {\n return [m.a * x + m.c * y + m.e, m.b * x + m.d * y + m.f]\n}\nfunction mul(m: Mat, n: Mat): Mat {\n // m ∘ n (apply n first, then m)\n return {\n a: m.a * n.a + m.c * n.b,\n b: m.b * n.a + m.d * n.b,\n c: m.a * n.c + m.c * n.d,\n d: m.b * n.c + m.d * n.d,\n e: m.a * n.e + m.c * n.f + m.e,\n f: m.b * n.e + m.d * n.f + m.f,\n }\n}\nfunction insertMat(ins: Ent): Mat {\n const px = fnum(ins, 10)\n const py = fnum(ins, 20)\n const sx = fnum(ins, 41, 1)\n const sy = fnum(ins, 42, 1)\n const rot = (fnum(ins, 50, 0) * Math.PI) / 180\n const cos = Math.cos(rot)\n const sin = Math.sin(rot)\n // translate(px,py) · rotate(rot) · scale(sx,sy)\n return {\n a: cos * sx,\n b: sin * sx,\n c: -sin * sy,\n d: cos * sy,\n e: px,\n f: py,\n }\n}\n// Average scale of a matrix (for radii under scaling — approximate for non-uniform scale).\nfunction scaleOf(m: Mat): number {\n const s1 = Math.hypot(m.a, m.b)\n const s2 = Math.hypot(m.c, m.d)\n return (s1 + s2) / 2 || 1\n}\nfunction rotOf(m: Mat): number {\n return (Math.atan2(m.b, m.a) * 180) / Math.PI\n}\n\n// ── bulge tessellation (LWPOLYLINE / POLYLINE arc segments) ──────────────────────────────────\nfunction bulgeArc(p1: [number, number], p2: [number, number], bulge: number): [number, number][] {\n if (!bulge) return [p2]\n const theta = 4 * Math.atan(bulge) // included angle\n const chord = Math.hypot(p2[0] - p1[0], p2[1] - p1[1])\n if (chord < 1e-9) return [p2]\n const radius = chord / 2 / Math.sin(Math.abs(theta) / 2)\n const mid = [(p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2]\n // perpendicular offset from chord midpoint to arc centre\n const apothem = radius * Math.cos(Math.abs(theta) / 2)\n const dir = [(p2[0] - p1[0]) / chord, (p2[1] - p1[1]) / chord]\n const nrm = bulge > 0 ? [dir[1], -dir[0]] : [-dir[1], dir[0]]\n const cx = mid[0] + nrm[0] * apothem\n const cy = mid[1] + nrm[1] * apothem\n const a0 = Math.atan2(p1[1] - cy, p1[0] - cx)\n const steps = Math.max(2, Math.min(64, Math.ceil((Math.abs(theta) / (Math.PI * 2)) * 48)))\n const out: [number, number][] = []\n for (let i = 1; i <= steps; i++) {\n const a = a0 + (theta * i) / steps\n out.push([cx + radius * Math.cos(a), cy + radius * Math.sin(a)])\n }\n return out\n}\n\n// ── MTEXT inline formatting strip ─────────────────────────────────────────────────────────────\nfunction stripMtext(s: string): string {\n return s\n .replace(/\\\\P/g, '\\n') // paragraph\n .replace(/\\\\[A-Za-z]+;?/g, '') // \\fArial; \\H2.5; \\C1; …\n .replace(/[{}]/g, '')\n .replace(/\\\\([{}\\\\])/g, '$1')\n .trim()\n}\n\n// ── entity → primitives ──────────────────────────────────────────────────────────────────────\nexport function parseDxf(text: string): ParsedDxf {\n const pairs = tokenize(text)\n const sections = sectionRanges(pairs)\n const layerMap = parseLayers(pairs, sections.get('TABLES'))\n\n // Blocks: name → its entity list (between BLOCK and ENDBLK), plus base point.\n const blocks = new Map<string, { base: [number, number]; ents: Ent[] }>()\n const blocksRange = sections.get('BLOCKS')\n if (blocksRange) {\n const ents = splitEntities(pairs, blocksRange[0], blocksRange[1])\n let cur: { name: string; base: [number, number]; ents: Ent[] } | null = null\n for (const ent of ents) {\n if (ent.type === 'BLOCK') {\n cur = { name: (first(ent, 2) ?? '').trim(), base: [fnum(ent, 10), fnum(ent, 20)], ents: [] }\n } else if (ent.type === 'ENDBLK') {\n if (cur && cur.name) blocks.set(cur.name, { base: cur.base, ents: cur.ents })\n cur = null\n } else if (cur) {\n cur.ents.push(ent)\n }\n }\n }\n\n const prims: DxfPrim[] = []\n const counts: Record<string, number> = {}\n\n const layerColor = (name: string): string => layerMap.get(name)?.color ?? '#1a1a1a'\n // Resolve an entity's colour: explicit ACI (62), else ByLayer, else the INSERT-inherited colour.\n const colorOf = (ent: Ent, layer: string, inherited: string): string => {\n const ci = inum(ent, 62, 256)\n if (ci === 0 || ci === 256) return ci === 0 ? inherited : layerColor(layer)\n return aciColor(Math.abs(ci))\n }\n\n function emit(ents: Ent[], m: Mat, inheritedColor: string, depth: number): void {\n for (let i = 0; i < ents.length; i++) {\n const ent = ents[i]\n const type = ent.type\n const layer = (first(ent, 8) ?? '0').trim()\n const color = colorOf(ent, layer, inheritedColor)\n const P = (x: number, y: number): [number, number] => apply(m, x, y)\n\n switch (type) {\n case 'LINE': {\n const [x1, y1] = P(fnum(ent, 10), fnum(ent, 20))\n const [x2, y2] = P(fnum(ent, 11), fnum(ent, 21))\n prims.push({ kind: 'line', layer, color, x1, y1, x2, y2 })\n break\n }\n case 'LWPOLYLINE': {\n const raw = lwVertices(ent)\n const pts = expandBulges(raw)\n const closed = (inum(ent, 70, 0) & 1) === 1\n prims.push({\n kind: 'polyline',\n layer,\n color,\n pts: pts.map(([x, y]) => P(x, y)),\n closed,\n })\n break\n }\n case 'POLYLINE': {\n // old-style: following VERTEX entities until SEQEND\n const verts: { pt: [number, number]; bulge: number }[] = []\n let j = i + 1\n while (j < ents.length && ents[j].type === 'VERTEX') {\n verts.push({ pt: [fnum(ents[j], 10), fnum(ents[j], 20)], bulge: fnum(ents[j], 42, 0) })\n j++\n }\n if (j < ents.length && ents[j].type === 'SEQEND') j++\n i = j - 1\n const raw = verts.map((v) => ({ x: v.pt[0], y: v.pt[1], bulge: v.bulge }))\n const pts = expandBulges(raw)\n const closed = (inum(ent, 70, 0) & 1) === 1\n if (pts.length)\n prims.push({\n kind: 'polyline',\n layer,\n color,\n pts: pts.map(([x, y]) => P(x, y)),\n closed,\n })\n break\n }\n case 'CIRCLE': {\n const [cx, cy] = P(fnum(ent, 10), fnum(ent, 20))\n prims.push({ kind: 'circle', layer, color, cx, cy, r: fnum(ent, 40) * scaleOf(m) })\n break\n }\n case 'ARC': {\n const [cx, cy] = P(fnum(ent, 10), fnum(ent, 20))\n const rot = rotOf(m)\n prims.push({\n kind: 'arc',\n layer,\n color,\n cx,\n cy,\n r: fnum(ent, 40) * scaleOf(m),\n a0: fnum(ent, 50) + rot,\n a1: fnum(ent, 51) + rot,\n })\n break\n }\n case 'ELLIPSE': {\n prims.push({ kind: 'polyline', layer, color, pts: ellipsePts(ent, P), closed: false })\n break\n }\n case 'POINT': {\n const [x, y] = P(fnum(ent, 10), fnum(ent, 20))\n prims.push({ kind: 'point', layer, color, x, y })\n break\n }\n case 'TEXT': {\n const s = (first(ent, 1) ?? '').trim()\n if (s) {\n const [x, y] = P(fnum(ent, 10), fnum(ent, 20))\n const ha = inum(ent, 72, 0)\n prims.push({\n kind: 'text',\n layer,\n color,\n x,\n y,\n h: fnum(ent, 40, 2.5) * scaleOf(m),\n text: s,\n rot: fnum(ent, 50, 0) + rotOf(m),\n align: ha === 1 ? 'c' : ha === 2 ? 'r' : 'l',\n })\n }\n break\n }\n case 'MTEXT': {\n const s = stripMtext(\n ent.pairs\n .filter((p) => p.code === 1 || p.code === 3)\n .map((p) => p.value)\n .join(''),\n )\n if (s) {\n const [x, y] = P(fnum(ent, 10), fnum(ent, 20))\n const at = inum(ent, 71, 1) // attachment: 1-3 top, 4-6 mid, 7-9 bottom; col%3 → l/c/r\n prims.push({\n kind: 'text',\n layer,\n color,\n x,\n y,\n h: fnum(ent, 40, 2.5) * scaleOf(m),\n text: s,\n rot: fnum(ent, 50, 0) + rotOf(m),\n align: at % 3 === 2 ? 'c' : at % 3 === 0 ? 'r' : 'l',\n })\n }\n break\n }\n case 'SOLID':\n case 'TRACE':\n case '3DFACE': {\n // corner order for SOLID/TRACE is 0,1,3,2 (bow-tie in file order); 3DFACE is 0,1,2,3\n const c0 = P(fnum(ent, 10), fnum(ent, 20))\n const c1 = P(fnum(ent, 11), fnum(ent, 21))\n const c2 = P(fnum(ent, 12), fnum(ent, 22))\n const c3 = P(fnum(ent, 13), fnum(ent, 23))\n const pts = type === '3DFACE' ? [c0, c1, c2, c3] : [c0, c1, c3, c2]\n prims.push({ kind: 'polygon', layer, color, pts })\n break\n }\n case 'SPLINE': {\n const pts = splinePts(ent, P)\n if (pts.length >= 2) prims.push({ kind: 'polyline', layer, color, pts, closed: false })\n break\n }\n case 'INSERT': {\n if (depth > 12) break\n const name = (first(ent, 2) ?? '').trim()\n const blk = blocks.get(name)\n if (blk) {\n const base: Mat = { ...IDENT, e: -blk.base[0], f: -blk.base[1] }\n const local = mul(insertMat(ent), base)\n emit(blk.ents, mul(m, local), color, depth + 1)\n }\n break\n }\n default:\n break\n }\n if (type && type !== 'VERTEX' && type !== 'SEQEND') counts[type] = (counts[type] ?? 0) + 1\n }\n }\n\n const entRange = sections.get('ENTITIES')\n if (entRange) emit(splitEntities(pairs, entRange[0], entRange[1]), IDENT, '#1a1a1a', 0)\n\n // Bounds over every primitive point.\n let minX = Infinity\n let minY = Infinity\n let maxX = -Infinity\n let maxY = -Infinity\n const grow = (x: number, y: number): void => {\n if (x < minX) minX = x\n if (x > maxX) maxX = x\n if (y < minY) minY = y\n if (y > maxY) maxY = y\n }\n for (const p of prims) {\n if (p.kind === 'line') {\n grow(p.x1, p.y1)\n grow(p.x2, p.y2)\n } else if (p.kind === 'polyline' || p.kind === 'polygon') {\n for (const [x, y] of p.pts) grow(x, y)\n } else if (p.kind === 'circle' || p.kind === 'arc') {\n grow(p.cx - p.r, p.cy - p.r)\n grow(p.cx + p.r, p.cy + p.r)\n } else {\n grow(p.x, p.y)\n }\n }\n const bounds = Number.isFinite(minX) ? { minX, minY, maxX, maxY } : null\n\n // Only surface layers that actually carry geometry (plus keep their table colour/on state).\n const used = new Set(prims.map((p) => p.layer))\n const layers: DxfLayer[] = []\n for (const name of used) {\n const l = layerMap.get(name)\n layers.push(l ?? { name, color: '#1a1a1a', on: true })\n }\n layers.sort((a, b) => a.name.localeCompare(b.name))\n\n return { prims, layers, bounds, counts }\n}\n\n// LWPOLYLINE vertices in order. Each vertex is `10 (x), 20 (y)` with an optional trailing `42 (bulge)`; a new\n// `10` starts the next vertex (flushing the current one).\nfunction lwVertices(ent: Ent): { x: number; y: number; bulge: number }[] {\n const out: { x: number; y: number; bulge: number }[] = []\n let cur: { x: number; y: number; bulge: number } | null = null\n for (const p of ent.pairs) {\n if (p.code === 10) {\n if (cur) out.push(cur)\n cur = { x: Number.parseFloat(p.value), y: 0, bulge: 0 }\n } else if (p.code === 20 && cur) {\n cur.y = Number.parseFloat(p.value)\n } else if (p.code === 42 && cur) {\n cur.bulge = Number.parseFloat(p.value)\n }\n }\n if (cur) out.push(cur)\n return out\n}\n\n// Turn a vertex list with per-vertex bulges into a flat point list (bulge arcs tessellated).\nfunction expandBulges(verts: { x: number; y: number; bulge: number }[]): [number, number][] {\n if (!verts.length) return []\n const pts: [number, number][] = [[verts[0].x, verts[0].y]]\n for (let i = 0; i + 1 < verts.length; i++) {\n const a: [number, number] = [verts[i].x, verts[i].y]\n const b: [number, number] = [verts[i + 1].x, verts[i + 1].y]\n for (const p of bulgeArc(a, b, verts[i].bulge)) pts.push(p)\n }\n return pts\n}\n\nfunction ellipsePts(ent: Ent, P: (x: number, y: number) => [number, number]): [number, number][] {\n const cx = fnum(ent, 10)\n const cy = fnum(ent, 20)\n const mx = fnum(ent, 11) // major axis endpoint (relative to centre)\n const my = fnum(ent, 21)\n const ratio = fnum(ent, 40, 1)\n const start = fnum(ent, 41, 0)\n const end = fnum(ent, 42, Math.PI * 2)\n const major = Math.hypot(mx, my)\n const minor = major * ratio\n const rot = Math.atan2(my, mx)\n const cos = Math.cos(rot)\n const sin = Math.sin(rot)\n const span = end - start <= 0 ? end - start + Math.PI * 2 : end - start\n const steps = Math.max(8, Math.min(96, Math.ceil((span / (Math.PI * 2)) * 72)))\n const out: [number, number][] = []\n for (let i = 0; i <= steps; i++) {\n const a = start + (span * i) / steps\n const ex = Math.cos(a) * major\n const ey = Math.sin(a) * minor\n out.push(P(cx + ex * cos - ey * sin, cy + ex * sin + ey * cos))\n }\n return out\n}\n\nfunction splinePts(ent: Ent, P: (x: number, y: number) => [number, number]): [number, number][] {\n // Best effort: prefer fit points (11/21); else control points (10/20). No NURBS evaluation.\n const fit: [number, number][] = []\n const ctrl: [number, number][] = []\n let x: number | null = null\n for (const p of ent.pairs) {\n if (p.code === 11) x = Number.parseFloat(p.value)\n else if (p.code === 21 && x !== null) {\n fit.push(P(x, Number.parseFloat(p.value)))\n x = null\n } else if (p.code === 10) x = Number.parseFloat(p.value)\n else if (p.code === 20 && x !== null) {\n ctrl.push(P(x, Number.parseFloat(p.value)))\n x = null\n }\n }\n return fit.length ? fit : ctrl\n}\n"],"mappings":"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"}
|
package/dist/lib/eml.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eml.js","names":[],"sources":["../../src/lib/eml.ts"],"sourcesContent":["/**\n * Hand-rolled EML email parser — the email-specific flattening on top of the shared MIME core (lib/mime.ts).\n *\n * An `.eml` file is a MIME message; lib/mime.ts parses the container into an {@link Entity} tree and decodes\n * each leaf. Here we flatten that tree into a render-ready shape for EmlViewer.vue: the header fields, the\n * best body (HTML preferred, plain-text fallback), and the attachment + inline-image leaves. Email inline\n * images are referenced from the HTML body by `cid:` (Content-ID), so we also index them by content-id.\n */\n\nimport {\n bytesToBinary,\n collectLeaves,\n decodeCharset,\n decodeWords,\n getHeader,\n parseEntity,\n parseHeaders,\n splitHeadersBody,\n type Entity,\n} from '@/lib/mime'\n\n/** A non-body leaf: an attachment or an inline (cid-referenced) resource. */\nexport interface EmlAttachment {\n filename: string\n contentType: string\n size: number\n bytes: Uint8Array\n /** disposition:inline, or an image carrying a Content-ID (referenced from the HTML body via `cid:`). */\n isInline: boolean\n /** Content-ID without the angle brackets, '' if none — the key the HTML body's `cid:` refs resolve to. */\n contentId: string\n}\n\n/** Flattened, render-ready view of a parsed email. */\nexport interface ParsedEmail {\n subject: string\n from: string\n to: string\n cc: string\n bcc: string\n replyTo: string\n /** Raw `Date:` header value (the viewer formats it for display). */\n date: string\n /** Decoded text/html body, or null when the message has no HTML alternative. */\n htmlBody: string | null\n /** Decoded text/plain body, or null when the message has no plain alternative. */\n textBody: string | null\n /** Attachments + inline resources (inline ones are also indexed in {@link inlineById}). */\n attachments: EmlAttachment[]\n /** contentId → resource, for resolving `cid:` references in the HTML body to data URLs. */\n inlineById: Map<string, EmlAttachment>\n}\n\n/** A type/subtype with no filename → a synthetic one, so every attachment has a downloadable name. */\nfunction defaultName(type: string, i: number): string {\n const ext = type.split('/')[1]?.replace(/[^a-z0-9.+-]/gi, '') || 'bin'\n return `attachment-${i + 1}.${ext}`\n}\n\n/** Parse raw `.eml` bytes into a flattened {@link ParsedEmail}. */\nexport function parseEml(bytes: Uint8Array): ParsedEmail {\n const bin = bytesToBinary(bytes)\n const root = parseEntity(bin)\n const headers = parseHeaders(splitHeadersBody(bin)[0])\n\n const leaves: Entity[] = []\n collectLeaves(root, leaves)\n\n let htmlBody: string | null = null\n let textBody: string | null = null\n const attachments: EmlAttachment[] = []\n const inlineById = new Map<string, EmlAttachment>()\n\n for (const lf of leaves) {\n const bodyBytes = lf.bodyBytes ?? new Uint8Array()\n const isAttachment = lf.disposition === 'attachment' || !!lf.filename\n // The first non-attachment text/html and text/plain leaves are the body alternatives.\n if (!isAttachment && lf.contentType === 'text/html' && htmlBody === null) {\n htmlBody = decodeCharset(bodyBytes, lf.charset)\n continue\n }\n if (!isAttachment && lf.contentType === 'text/plain' && textBody === null) {\n textBody = decodeCharset(bodyBytes, lf.charset)\n continue\n }\n // Everything else is an attachment or an inline resource.\n const att: EmlAttachment = {\n filename: lf.filename || defaultName(lf.contentType, attachments.length),\n contentType: lf.contentType,\n size: bodyBytes.length,\n bytes: bodyBytes,\n isInline:\n lf.disposition === 'inline' || (!!lf.contentId && lf.contentType.startsWith('image/')),\n contentId: lf.contentId,\n }\n attachments.push(att)\n if (lf.contentId) inlineById.set(lf.contentId, att)\n }\n\n return {\n subject: decodeWords(getHeader(headers, 'subject')),\n from: decodeWords(getHeader(headers, 'from')),\n to: decodeWords(getHeader(headers, 'to')),\n cc: decodeWords(getHeader(headers, 'cc')),\n bcc: decodeWords(getHeader(headers, 'bcc')),\n replyTo: decodeWords(getHeader(headers, 'reply-to')),\n date: getHeader(headers, 'date'),\n htmlBody,\n textBody,\n attachments,\n inlineById,\n }\n}\n"],"mappings":";;AAsDA,SAAS,EAAY,GAAc,GAAmB;CACpD,IAAM,IAAM,EAAK,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,QAAQ,kBAAkB,EAAE,KAAK;CACjE,OAAO,cAAc,IAAI,EAAE,GAAG;AAChC;AAGA,SAAgB,EAAS,GAAgC;CACvD,IAAM,IAAM,EAAc,CAAK,GACzB,IAAO,EAAY,CAAG,GACtB,IAAU,EAAa,EAAiB,CAAG,CAAC,CAAC,EAAE,GAE/C,IAAmB,CAAC;CAC1B,EAAc,GAAM,CAAM;CAE1B,IAAI,IAA0B,MAC1B,IAA0B,MACxB,IAA+B,CAAC,GAChC,oBAAa,IAAI,IAA2B;CAElD,KAAK,IAAM,KAAM,GAAQ;EACvB,IAAM,IAAY,EAAG,aAAa,IAAI,WAAW,GAC3C,IAAe,EAAG,gBAAgB,gBAAgB,CAAC,CAAC,EAAG;EAE7D,IAAI,CAAC,KAAgB,EAAG,gBAAgB,eAAe,MAAa,MAAM;GACxE,IAAW,EAAc,GAAW,EAAG,OAAO;GAC9C;EACF;EACA,IAAI,CAAC,KAAgB,EAAG,gBAAgB,gBAAgB,MAAa,MAAM;GACzE,IAAW,EAAc,GAAW,EAAG,OAAO;GAC9C;EACF;EAEA,IAAM,IAAqB;GACzB,UAAU,EAAG,YAAY,EAAY,EAAG,aAAa,EAAY,MAAM;GACvE,aAAa,EAAG;GAChB,MAAM,EAAU;GAChB,OAAO;GACP,UACE,EAAG,gBAAgB,YAAa,CAAC,CAAC,EAAG,aAAa,EAAG,YAAY,WAAW,QAAQ;GACtF,WAAW,EAAG;EAChB;EAEA,AADA,EAAY,KAAK,CAAG,GAChB,EAAG,aAAW,EAAW,IAAI,EAAG,WAAW,CAAG;CACpD;CAEA,OAAO;EACL,SAAS,EAAY,EAAU,GAAS,SAAS,CAAC;EAClD,MAAM,EAAY,EAAU,GAAS,MAAM,CAAC;EAC5C,IAAI,EAAY,EAAU,GAAS,IAAI,CAAC;EACxC,IAAI,EAAY,EAAU,GAAS,IAAI,CAAC;EACxC,KAAK,EAAY,EAAU,GAAS,KAAK,CAAC;EAC1C,SAAS,EAAY,EAAU,GAAS,UAAU,CAAC;EACnD,MAAM,EAAU,GAAS,MAAM;EAC/B;EACA;EACA;EACA;CACF;AACF"}
|
package/dist/lib/encoding.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encoding.js","names":[],"sources":["../../src/lib/encoding.ts"],"sourcesContent":["/**\n * Decode an archive entry's filename bytes, sniffing the encoding.\n *\n * Archive formats store names in whatever byte encoding the producer's OS used: ZIP without its UTF-8 flag and\n * legacy tar both carry raw bytes, and on Chinese Windows that's usually **GBK** — which turns into mojibake if\n * force-decoded as UTF-8. So we probe: try strict UTF-8 first (the modern default, and a superset of ASCII); if\n * the bytes aren't valid UTF-8, try GBK; if neither decodes cleanly, fall back to a lossless `\\uXXXX` escape of\n * the raw bytes so the name is at least unambiguous instead of garbled.\n *\n * \"Strict\" = a `fatal` TextDecoder that throws on an invalid sequence, which is exactly our \"is it this\n * encoding?\" probe. Non-streaming `decode()` calls are independent, so the decoders are safely reused.\n */\n\nconst utf8Strict = new TextDecoder('utf-8', { fatal: true })\n\nlet gbkStrict: TextDecoder | null = null\nlet gbkInit = false\nfunction getGbk(): TextDecoder | null {\n if (!gbkInit) {\n gbkInit = true\n try {\n // 'gbk' maps to the gb18030 decoder per the Encoding spec — present in every browser; a minimal Node\n // ICU may lack it, in which case we just skip the GBK probe.\n gbkStrict = new TextDecoder('gbk', { fatal: true })\n } catch {\n gbkStrict = null\n }\n }\n return gbkStrict\n}\n\n/** Escape each raw byte as `\\u00XX` (printable ASCII kept verbatim) — a deterministic, reversible last resort. */\nfunction escapeBytes(bytes: Uint8Array): string {\n let out = ''\n for (const b of bytes) {\n if (b >= 0x20 && b < 0x7f) out += String.fromCharCode(b)\n else out += '\\\\u' + b.toString(16).padStart(4, '0')\n }\n return out\n}\n\n/**\n * Decode a TEXT file's bytes, sniffing the encoding: UTF-8 → GBK → lenient UTF-8.\n *\n * Same probe as {@link decodeFileNameBytes}, but for file *content* (e.g. a `.lrc` lyrics file, which on\n * Chinese Windows is almost always GBK and turns to mojibake if force-decoded as UTF-8). The last resort\n * differs: instead of a `\\uXXXX` escape (right for a filename) we fall back to a non-fatal UTF-8 decode,\n * which yields the readable text with `�` for any stray invalid byte — the sane default for prose.\n */\nexport function decodeTextSniff(bytes: Uint8Array): string {\n if (bytes.length === 0) return ''\n try {\n return utf8Strict.decode(bytes)\n } catch {\n /* not valid UTF-8 → try GBK */\n }\n const gbk = getGbk()\n if (gbk) {\n try {\n return gbk.decode(bytes)\n } catch {\n /* not clean GBK either → lenient UTF-8 below */\n }\n }\n return new TextDecoder('utf-8').decode(bytes)\n}\n\n/**\n * Decode filename bytes as UTF-8 → GBK → `\\uXXXX` escape (in that order of preference).\n *\n * `normalizeSeparators` rewrites backslashes to '/' on a *successfully decoded* name (ZIP from Windows'\n * Compress-Archive emits '\\'), but never on the escaped fallback — whose own `\\u…` backslashes must survive.\n */\nexport function decodeFileNameBytes(bytes: Uint8Array, normalizeSeparators = false): string {\n if (bytes.length === 0) return ''\n try {\n const s = utf8Strict.decode(bytes)\n return normalizeSeparators ? s.replace(/\\\\/g, '/') : s\n } catch {\n /* not valid UTF-8 → try GBK */\n }\n const gbk = getGbk()\n if (gbk) {\n try {\n const s = gbk.decode(bytes)\n return normalizeSeparators ? s.replace(/\\\\/g, '/') : s\n } catch {\n /* not valid GBK either → escape */\n }\n }\n return escapeBytes(bytes)\n}\n"],"mappings":";AAaA,IAAM,IAAa,IAAI,YAAY,SAAS,EAAE,OAAO,GAAK,CAAC,GAEvD,IAAgC,MAChC,IAAU;AACd,SAAS,IAA6B;CACpC,IAAI,CAAC,GAAS;EACZ,IAAU;EACV,IAAI;GAGF,IAAY,IAAI,YAAY,OAAO,EAAE,OAAO,GAAK,CAAC;EACpD,QAAQ;GACN,IAAY;EACd;CACF;CACA,OAAO;AACT;AAGA,SAAS,EAAY,GAA2B;CAC9C,IAAI,IAAM;CACV,KAAK,IAAM,KAAK,GACd,AAAI,KAAK,MAAQ,IAAI,MAAM,KAAO,OAAO,aAAa,CAAC,IAClD,KAAO,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;CAEpD,OAAO;AACT;AAUA,SAAgB,EAAgB,GAA2B;CACzD,IAAI,EAAM,WAAW,GAAG,OAAO;CAC/B,IAAI;EACF,OAAO,EAAW,OAAO,CAAK;CAChC,QAAQ,CAER;CACA,IAAM,IAAM,EAAO;CACnB,IAAI,GACF,IAAI;EACF,OAAO,EAAI,OAAO,CAAK;CACzB,QAAQ,CAER;CAEF,OAAO,IAAI,YAAY,OAAO,CAAC,CAAC,OAAO,CAAK;AAC9C;AAQA,SAAgB,EAAoB,GAAmB,IAAsB,IAAe;CAC1F,IAAI,EAAM,WAAW,GAAG,OAAO;CAC/B,IAAI;EACF,IAAM,IAAI,EAAW,OAAO,CAAK;EACjC,OAAO,IAAsB,EAAE,QAAQ,OAAO,GAAG,IAAI;CACvD,QAAQ,CAER;CACA,IAAM,IAAM,EAAO;CACnB,IAAI,GACF,IAAI;EACF,IAAM,IAAI,EAAI,OAAO,CAAK;EAC1B,OAAO,IAAsB,EAAE,QAAQ,OAAO,GAAG,IAAI;CACvD,QAAQ,CAER;CAEF,OAAO,EAAY,CAAK;AAC1B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codeDataParser.js","names":[],"sources":["../../../../src/lib/epl/code/codeDataParser.ts"],"sourcesContent":["/**\n * Code-data decompiler — port of `CodeDataParser.cs` (read path).\n *\n * Turns a method's `expressionData` (a bytecode-like stream) back into a {@link StatementBlock} tree. The C#\n * also rebuilds the line/block offset tables as a side effect; those writers are pure outputs that never\n * influence the reads, so this port drops them entirely and only produces the statement tree.\n *\n * Statement opcodes: 0x50 否则 / 0x51 如果结束 / 0x52 如果真结束 / 0x53 判断Case结束 / 0x54 判断结束 /\n * 0x55 循环体结束 / 0x6A 常规Call / 0x6B 如果 / 0x6C 如果真 / 0x6D 判断开始 / 0x6E 判断Case / 0x6F 默认 /\n * 0x70 循环首 / 0x71 循环尾. Expression opcodes are handled in {@link parseExpression}.\n */\nimport { BinaryReader } from '../io/binaryReader'\nimport { gbk } from '../io/encoding'\nimport { decomposeLibDataTypeId, ID_NAV, isLibDataType } from '../model/eplSystemId'\nimport type { CallExpr, CaseInfo, Expr, LoopCommon, StatementBlock, Stmt } from './ast'\n\nconst KNOWN_TYPE_ID = new Set([\n 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71,\n])\n\n// Transient singletons that mark the end of a param list / array literal during parsing (never rendered).\nconst PARAM_LIST_END: Expr = { kind: 'paramListEnd' }\nconst ARRAY_LITERAL_END: Expr = { kind: 'arrayLiteralEnd' }\nconst DEFAULT_VALUE: Expr = { kind: 'default' }\n\ninterface ParsedCall {\n exp: CallExpr\n unexaminedCode: string | null\n comment: string | null\n mask: boolean\n}\n\n/** Parse a method's expression-data bytes into a statement block. `dec` = the method's codepage decoder. */\nexport function parseStatementBlock(\n expressionData: Uint8Array,\n dec: TextDecoder = gbk(),\n): StatementBlock {\n const r = new BinaryReader(expressionData, dec)\n return parseBlock(r)\n}\n\nfunction parseBlock(r: BinaryReader): StatementBlock {\n const statements: Stmt[] = []\n for (;;) {\n let type: number\n // Skip bytes that aren't a known statement opcode (the gaps between statements).\n for (;;) {\n if (r.pos >= r.length) return { statements }\n type = r.readByte()\n if (KNOWN_TYPE_ID.has(type)) break\n }\n const startOffset = r.pos - 1\n\n // Block terminators: rewind one byte and hand control back to the caller.\n if (\n type === 0x50 ||\n type === 0x51 ||\n type === 0x52 ||\n type === 0x53 ||\n type === 0x54 ||\n type === 0x6f ||\n type === 0x71\n ) {\n r.pos = startOffset\n return { statements }\n }\n if (type === 0x55) continue // loop-body end marker — nothing to emit\n\n if (type === 0x6d) {\n // .判断开始 — a switch. The 0x6E (first case) follows immediately.\n if (r.readByte() !== 0x6e) throw new Error('CodeData: expected 0x6E after 0x6D')\n const cases: CaseInfo[] = []\n let switchType: number\n do {\n const c = parseCall(r)\n const condition = c.exp.paramList?.[0] ?? null\n const block = parseBlock(r)\n cases.push({\n condition,\n unexaminedCode: c.unexaminedCode,\n block,\n comment: c.comment,\n mask: c.mask,\n })\n if (r.readByte() !== 0x53) throw new Error('CodeData: expected 0x53 (case end)')\n switchType = r.readByte()\n } while (switchType === 0x6e)\n if (switchType !== 0x6f) throw new Error('CodeData: expected 0x6F (default)')\n const defaultBlock = parseBlock(r)\n if (r.readByte() !== 0x54) throw new Error('CodeData: expected 0x54 (switch end)')\n r.readByte() // 0x74\n statements.push({ kind: 'switch', cases, defaultBlock })\n continue\n }\n\n // 0x70 loop / 0x6C if-true / 0x6B if-else / 0x6A expression — all start with a call expression.\n const { exp, unexaminedCode, comment, mask } = parseCall(r)\n switch (type) {\n case 0x70: {\n const block = parseBlock(r)\n if (r.readByte() !== 0x71) throw new Error('CodeData: expected 0x71 (loop end)')\n const end = parseCall(r)\n if (exp.libraryId !== 0) throw new Error('CodeData: bad loop library id')\n const common: LoopCommon = {\n block,\n unexaminedCode,\n commentOnStart: comment,\n commentOnEnd: end.comment,\n maskOnStart: mask,\n maskOnEnd: end.mask,\n }\n switch (exp.methodId) {\n case 3:\n statements.push({ kind: 'while', condition: exp.paramList?.[0] ?? null, ...common })\n break\n case 5:\n statements.push({\n kind: 'doWhile',\n condition: end.exp.paramList?.[0] ?? null,\n ...common,\n unexaminedCode: end.unexaminedCode,\n })\n break\n case 7:\n statements.push({\n kind: 'counter',\n count: exp.paramList?.[0] ?? null,\n var: exp.paramList?.[1] ?? null,\n ...common,\n })\n break\n case 9:\n statements.push({\n kind: 'for',\n start: exp.paramList?.[0] ?? null,\n end: exp.paramList?.[1] ?? null,\n step: exp.paramList?.[2] ?? null,\n var: exp.paramList?.[3] ?? null,\n ...common,\n })\n break\n default:\n throw new Error(`CodeData: unknown loop methodId ${exp.methodId}`)\n }\n break\n }\n case 0x6c: {\n const block = parseBlock(r)\n if (r.readByte() !== 0x52) throw new Error('CodeData: expected 0x52')\n r.readByte() // 0x73\n statements.push({\n kind: 'if',\n condition: exp.paramList?.[0] ?? null,\n unexaminedCode,\n block,\n comment,\n mask,\n })\n break\n }\n case 0x6b: {\n const blockOnTrue = parseBlock(r)\n if (r.readByte() !== 0x50) throw new Error('CodeData: expected 0x50')\n const blockOnFalse = parseBlock(r)\n if (r.readByte() !== 0x51) throw new Error('CodeData: expected 0x51')\n r.readByte() // 0x72\n statements.push({\n kind: 'ifElse',\n condition: exp.paramList?.[0] ?? null,\n unexaminedCode,\n blockOnTrue,\n blockOnFalse,\n comment,\n mask,\n })\n break\n }\n case 0x6a: {\n if (unexaminedCode != null) {\n statements.push({ kind: 'unexamined', code: unexaminedCode, mask })\n } else if (exp.libraryId === -1) {\n statements.push({ kind: 'expression', expr: null, mask: false, comment })\n } else {\n statements.push({ kind: 'expression', expr: exp, mask, comment })\n }\n break\n }\n }\n }\n}\n\nfunction parseCall(r: BinaryReader): ParsedCall {\n const methodId = r.readInt32()\n const libraryId = r.readInt16()\n const flag = r.readInt16()\n let unexaminedCode = r.readBStr()\n let comment = r.readBStr()\n const mask = (flag & 0x20) !== 0\n const invokeSpecial = (flag & 0x10) !== 0\n if (unexaminedCode != null) {\n const z = unexaminedCode.indexOf('\\0')\n if (z !== -1) unexaminedCode = unexaminedCode.slice(0, z)\n if (unexaminedCode === '') unexaminedCode = null\n }\n if (comment != null) {\n const z = comment.indexOf('\\0')\n if (z !== -1) comment = comment.slice(0, z)\n if (comment === '') comment = null\n }\n const exp: CallExpr = {\n kind: 'call',\n libraryId,\n methodId,\n invokeSpecial,\n target: null,\n paramList: null,\n }\n if (r.pos !== r.length) {\n const b = r.readByte()\n if (b === 0x36) {\n exp.paramList = parseParamList(r)\n } else if (b === 0x38) {\n // ThisCall\n r.pos -= 1\n exp.target = parseExpression(r)\n exp.paramList = parseParamList(r)\n } else {\n r.pos -= 1\n throw new Error('CodeData: bad call terminator')\n }\n }\n return { exp, unexaminedCode, comment, mask }\n}\n\nfunction parseParamList(r: BinaryReader): Expr[] {\n const params: Expr[] = []\n for (;;) {\n const e = parseExpression(r)\n if (e === PARAM_LIST_END) break\n params.push(e)\n }\n return params\n}\n\nfunction parseExpression(r: BinaryReader, parseMember = true): Expr {\n let result!: Expr\n for (;;) {\n const type = r.readByte()\n if (type === 0x01) {\n result = PARAM_LIST_END\n break\n } else if (type === 0x16) {\n result = DEFAULT_VALUE\n break\n } else if (type === 0x17) {\n result = { kind: 'number', value: r.readFloat64() }\n break\n } else if (type === 0x18) {\n result = { kind: 'bool', value: r.readInt16() !== 0 }\n break\n } else if (type === 0x19) {\n result = { kind: 'datetime', oa: r.readFloat64() }\n break\n } else if (type === 0x1a) {\n result = { kind: 'string', value: r.readBStr() ?? '' }\n break\n } else if (type === 0x1b) {\n result = { kind: 'constant', libraryId: -2, constantId: r.readInt32() }\n break\n } else if (type === 0x1c) {\n result = { kind: 'constant', libraryId: r.readInt16() - 1, constantId: r.readInt16() - 1 }\n break\n } else if (type === 0x1d) {\n continue // 0x1D prefixes a 0x38 var access — skip and re-read\n } else if (type === 0x1e) {\n result = { kind: 'methodPtr', methodId: r.readInt32() }\n break\n } else if (type === 0x21) {\n result = parseCall(r).exp\n break\n } else if (type === 0x23) {\n const structId = r.readInt16() - 1\n const libraryId = r.readInt16() - 1\n const memberId = r.readInt32() - 1\n result = { kind: 'emnu', libraryId, structId, memberId }\n break\n } else if (type === 0x37) {\n continue\n } else if (type === 0x1f) {\n const items: Expr[] = []\n let e: Expr\n while ((e = parseExpression(r)) !== ARRAY_LITERAL_END) items.push(e)\n result = { kind: 'array', items }\n break\n } else if (type === 0x20) {\n result = ARRAY_LITERAL_END\n break\n } else if (type === 0x38) {\n const variable = r.readInt32()\n if (variable === ID_NAV) {\n r.readByte() // 0x3A\n return parseExpression(r, true)\n }\n result = { kind: 'variable', id: variable }\n parseMember = true\n break\n } else if (type === 0x3b) {\n result = { kind: 'number', value: r.readInt32() }\n break\n } else {\n throw new Error(`CodeData: unknown expression type 0x${type.toString(16)}`)\n }\n }\n\n if (\n parseMember &&\n (result.kind === 'variable' ||\n result.kind === 'call' ||\n result.kind === 'accessMember' ||\n result.kind === 'accessArray')\n ) {\n while (r.pos !== r.length) {\n const b = r.readByte()\n if (b === 0x39) {\n const memberId = r.readInt32()\n const structId = r.readInt32()\n if (isLibDataType(structId)) {\n const { lib, type } = decomposeLibDataTypeId(structId)\n result = {\n kind: 'accessMember',\n target: result,\n libraryId: lib,\n structId: type,\n memberId: memberId - 1,\n }\n } else {\n result = { kind: 'accessMember', target: result, libraryId: -2, structId, memberId }\n }\n } else if (b === 0x3a) {\n result = { kind: 'accessArray', target: result, index: parseExpression(r, false) }\n } else if (b === 0x37) {\n break\n } else {\n r.pos -= 1\n break\n }\n }\n }\n return result\n}\n"],"mappings":";;;;AAgBA,IAAM,IAAgB,IAAI,IAAI;CAC5B;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;CAAM;AAChF,CAAC,GAGK,IAAuB,EAAE,MAAM,eAAe,GAC9C,IAA0B,EAAE,MAAM,kBAAkB,GACpD,IAAsB,EAAE,MAAM,UAAU;AAU9C,SAAgB,EACd,GACA,IAAmB,EAAI,GACP;CAEhB,OAAO,EAAW,IADJ,EAAa,GAAgB,CACzB,CAAC;AACrB;AAEA,SAAS,EAAW,GAAiC;CACnD,IAAM,IAAqB,CAAC;CAC5B,SAAS;EACP,IAAI;EAEJ,SAAS;GACP,IAAI,EAAE,OAAO,EAAE,QAAQ,OAAO,EAAE,cAAW;GAE3C,IADA,IAAO,EAAE,SAAS,GACd,EAAc,IAAI,CAAI,GAAG;EAC/B;EACA,IAAM,IAAc,EAAE,MAAM;EAG5B,IACE,MAAS,MACT,MAAS,MACT,MAAS,MACT,MAAS,MACT,MAAS,MACT,MAAS,OACT,MAAS,KAGT,OADA,EAAE,MAAM,GACD,EAAE,cAAW;EAEtB,IAAI,MAAS,IAAM;EAEnB,IAAI,MAAS,KAAM;GAEjB,IAAI,EAAE,SAAS,MAAM,KAAM,MAAU,MAAM,oCAAoC;GAC/E,IAAM,IAAoB,CAAC,GACvB;GACJ,GAAG;IACD,IAAM,IAAI,EAAU,CAAC,GACf,IAAY,EAAE,IAAI,YAAY,MAAM,MACpC,IAAQ,EAAW,CAAC;IAQ1B,IAPA,EAAM,KAAK;KACT;KACA,gBAAgB,EAAE;KAClB;KACA,SAAS,EAAE;KACX,MAAM,EAAE;IACV,CAAC,GACG,EAAE,SAAS,MAAM,IAAM,MAAU,MAAM,oCAAoC;IAC/E,IAAa,EAAE,SAAS;GAC1B,SAAS,MAAe;GACxB,IAAI,MAAe,KAAM,MAAU,MAAM,mCAAmC;GAC5E,IAAM,IAAe,EAAW,CAAC;GACjC,IAAI,EAAE,SAAS,MAAM,IAAM,MAAU,MAAM,sCAAsC;GAEjF,AADA,EAAE,SAAS,GACX,EAAW,KAAK;IAAE,MAAM;IAAU;IAAO;GAAa,CAAC;GACvD;EACF;EAGA,IAAM,EAAE,QAAK,mBAAgB,YAAS,YAAS,EAAU,CAAC;EAC1D,QAAQ,GAAR;GACE,KAAK,KAAM;IACT,IAAM,IAAQ,EAAW,CAAC;IAC1B,IAAI,EAAE,SAAS,MAAM,KAAM,MAAU,MAAM,oCAAoC;IAC/E,IAAM,IAAM,EAAU,CAAC;IACvB,IAAI,EAAI,cAAc,GAAG,MAAU,MAAM,+BAA+B;IACxE,IAAM,IAAqB;KACzB;KACA;KACA,gBAAgB;KAChB,cAAc,EAAI;KAClB,aAAa;KACb,WAAW,EAAI;IACjB;IACA,QAAQ,EAAI,UAAZ;KACE,KAAK;MACH,EAAW,KAAK;OAAE,MAAM;OAAS,WAAW,EAAI,YAAY,MAAM;OAAM,GAAG;MAAO,CAAC;MACnF;KACF,KAAK;MACH,EAAW,KAAK;OACd,MAAM;OACN,WAAW,EAAI,IAAI,YAAY,MAAM;OACrC,GAAG;OACH,gBAAgB,EAAI;MACtB,CAAC;MACD;KACF,KAAK;MACH,EAAW,KAAK;OACd,MAAM;OACN,OAAO,EAAI,YAAY,MAAM;OAC7B,KAAK,EAAI,YAAY,MAAM;OAC3B,GAAG;MACL,CAAC;MACD;KACF,KAAK;MACH,EAAW,KAAK;OACd,MAAM;OACN,OAAO,EAAI,YAAY,MAAM;OAC7B,KAAK,EAAI,YAAY,MAAM;OAC3B,MAAM,EAAI,YAAY,MAAM;OAC5B,KAAK,EAAI,YAAY,MAAM;OAC3B,GAAG;MACL,CAAC;MACD;KACF,SACE,MAAU,MAAM,mCAAmC,EAAI,UAAU;IACrE;IACA;GACF;GACA,KAAK,KAAM;IACT,IAAM,IAAQ,EAAW,CAAC;IAC1B,IAAI,EAAE,SAAS,MAAM,IAAM,MAAU,MAAM,yBAAyB;IAEpE,AADA,EAAE,SAAS,GACX,EAAW,KAAK;KACd,MAAM;KACN,WAAW,EAAI,YAAY,MAAM;KACjC;KACA;KACA;KACA;IACF,CAAC;IACD;GACF;GACA,KAAK,KAAM;IACT,IAAM,IAAc,EAAW,CAAC;IAChC,IAAI,EAAE,SAAS,MAAM,IAAM,MAAU,MAAM,yBAAyB;IACpE,IAAM,IAAe,EAAW,CAAC;IACjC,IAAI,EAAE,SAAS,MAAM,IAAM,MAAU,MAAM,yBAAyB;IAEpE,AADA,EAAE,SAAS,GACX,EAAW,KAAK;KACd,MAAM;KACN,WAAW,EAAI,YAAY,MAAM;KACjC;KACA;KACA;KACA;KACA;IACF,CAAC;IACD;GACF;GACA,KAAK;IACH,AAAI,KAAkB,OAEX,EAAI,cAAc,KAC3B,EAAW,KAAK;KAAE,MAAM;KAAc,MAAM;KAAM,MAAM;KAAO;IAAQ,CAAC,IAExE,EAAW,KAAK;KAAE,MAAM;KAAc,MAAM;KAAK;KAAM;IAAQ,CAAC,IAJhE,EAAW,KAAK;KAAE,MAAM;KAAc,MAAM;KAAgB;IAAK,CAAC;IAMpE;EAEJ;CACF;AACF;AAEA,SAAS,EAAU,GAA6B;CAC9C,IAAM,IAAW,EAAE,UAAU,GACvB,IAAY,EAAE,UAAU,GACxB,IAAO,EAAE,UAAU,GACrB,IAAiB,EAAE,SAAS,GAC5B,IAAU,EAAE,SAAS,GACnB,KAAQ,IAAO,OAAU,GACzB,KAAiB,IAAO,OAAU;CACxC,IAAI,KAAkB,MAAM;EAC1B,IAAM,IAAI,EAAe,QAAQ,IAAI;EAErC,AADI,MAAM,OAAI,IAAiB,EAAe,MAAM,GAAG,CAAC,IACpD,MAAmB,OAAI,IAAiB;CAC9C;CACA,IAAI,KAAW,MAAM;EACnB,IAAM,IAAI,EAAQ,QAAQ,IAAI;EAE9B,AADI,MAAM,OAAI,IAAU,EAAQ,MAAM,GAAG,CAAC,IACtC,MAAY,OAAI,IAAU;CAChC;CACA,IAAM,IAAgB;EACpB,MAAM;EACN;EACA;EACA;EACA,QAAQ;EACR,WAAW;CACb;CACA,IAAI,EAAE,QAAQ,EAAE,QAAQ;EACtB,IAAM,IAAI,EAAE,SAAS;EACrB,IAAI,MAAM,IACR,EAAI,YAAY,EAAe,CAAC;OAC3B,IAAI,MAAM,IAIf,AAFA,IAAE,KACF,EAAI,SAAS,EAAgB,CAAC,GAC9B,EAAI,YAAY,EAAe,CAAC;OAGhC,MADA,IAAE,KACQ,MAAM,+BAA+B;CAEnD;CACA,OAAO;EAAE;EAAK;EAAgB;EAAS;CAAK;AAC9C;AAEA,SAAS,EAAe,GAAyB;CAC/C,IAAM,IAAiB,CAAC;CACxB,SAAS;EACP,IAAM,IAAI,EAAgB,CAAC;EAC3B,IAAI,MAAM,GAAgB;EAC1B,EAAO,KAAK,CAAC;CACf;CACA,OAAO;AACT;AAEA,SAAS,EAAgB,GAAiB,IAAc,IAAY;CAClE,IAAI;CACJ,SAAS;EACP,IAAM,IAAO,EAAE,SAAS;EACxB,IAAI,MAAS,GAAM;GACjB,IAAS;GACT;EACF,OAAO,IAAI,MAAS,IAAM;GACxB,IAAS;GACT;EACF,OAAO,IAAI,MAAS,IAAM;GACxB,IAAS;IAAE,MAAM;IAAU,OAAO,EAAE,YAAY;GAAE;GAClD;EACF,OAAO,IAAI,MAAS,IAAM;GACxB,IAAS;IAAE,MAAM;IAAQ,OAAO,EAAE,UAAU,MAAM;GAAE;GACpD;EACF,OAAO,IAAI,MAAS,IAAM;GACxB,IAAS;IAAE,MAAM;IAAY,IAAI,EAAE,YAAY;GAAE;GACjD;EACF,OAAO,IAAI,MAAS,IAAM;GACxB,IAAS;IAAE,MAAM;IAAU,OAAO,EAAE,SAAS,KAAK;GAAG;GACrD;EACF,OAAO,IAAI,MAAS,IAAM;GACxB,IAAS;IAAE,MAAM;IAAY,WAAW;IAAI,YAAY,EAAE,UAAU;GAAE;GACtE;EACF,OAAO,IAAI,MAAS,IAAM;GACxB,IAAS;IAAE,MAAM;IAAY,WAAW,EAAE,UAAU,IAAI;IAAG,YAAY,EAAE,UAAU,IAAI;GAAE;GACzF;EACF,OAAO,IAAI,MAAS,IAClB;OACK,IAAI,MAAS,IAAM;GACxB,IAAS;IAAE,MAAM;IAAa,UAAU,EAAE,UAAU;GAAE;GACtD;EACF,OAAO,IAAI,MAAS,IAAM;GACxB,IAAS,EAAU,CAAC,CAAC,CAAC;GACtB;EACF,OAAO,IAAI,MAAS,IAAM;GACxB,IAAM,IAAW,EAAE,UAAU,IAAI;GAGjC,IAAS;IAAE,MAAM;IAAQ,WAFP,EAAE,UAAU,IAAI;IAEE;IAAU,UAD7B,EAAE,UAAU,IAAI;GACsB;GACvD;EACF,OAAO,IAAI,MAAS,IAClB;OACK,IAAI,MAAS,IAAM;GACxB,IAAM,IAAgB,CAAC,GACnB;GACJ,QAAQ,IAAI,EAAgB,CAAC,OAAO,IAAmB,EAAM,KAAK,CAAC;GACnE,IAAS;IAAE,MAAM;IAAS;GAAM;GAChC;EACF,OAAO,IAAI,MAAS,IAAM;GACxB,IAAS;GACT;EACF,OAAO,IAAI,MAAS,IAAM;GACxB,IAAM,IAAW,EAAE,UAAU;GAC7B,IAAI,MAAA,UAEF,OADA,EAAE,SAAS,GACJ,EAAgB,GAAG,EAAI;GAGhC,AADA,IAAS;IAAE,MAAM;IAAY,IAAI;GAAS,GAC1C,IAAc;GACd;EACF,OAAO,IAAI,MAAS,IAAM;GACxB,IAAS;IAAE,MAAM;IAAU,OAAO,EAAE,UAAU;GAAE;GAChD;EACF,OACE,MAAU,MAAM,uCAAuC,EAAK,SAAS,EAAE,GAAG;CAE9E;CAEA,IACE,MACC,EAAO,SAAS,cACf,EAAO,SAAS,UAChB,EAAO,SAAS,kBAChB,EAAO,SAAS,gBAElB,OAAO,EAAE,QAAQ,EAAE,SAAQ;EACzB,IAAM,IAAI,EAAE,SAAS;EACrB,IAAI,MAAM,IAAM;GACd,IAAM,IAAW,EAAE,UAAU,GACvB,IAAW,EAAE,UAAU;GAC7B,IAAI,EAAc,CAAQ,GAAG;IAC3B,IAAM,EAAE,QAAK,YAAS,EAAuB,CAAQ;IACrD,IAAS;KACP,MAAM;KACN,QAAQ;KACR,WAAW;KACX,UAAU;KACV,UAAU,IAAW;IACvB;GACF,OACE,IAAS;IAAE,MAAM;IAAgB,QAAQ;IAAQ,WAAW;IAAI;IAAU;GAAS;EAEvF,OAAO,IAAI,MAAM,IACf,IAAS;GAAE,MAAM;GAAe,QAAQ;GAAQ,OAAO,EAAgB,GAAG,EAAK;EAAE;OAC5E,IAAI,MAAM,IACf;OACK;GACL,IAAE;GACF;EACF;CACF;CAEF,OAAO;AACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codeLines.js","names":[],"sources":["../../../../src/lib/epl/code/codeLines.ts"],"sourcesContent":["/**\n * IDE-style code-line emitter — produces the line list the 易语言 viewer renders (one {@link CodeLine} per\n * displayed row, each carrying the dashed block-guide segments that draw its nesting brackets).\n *\n * The guides are NOT a pixel copy of the 易语言 IDE (that proved fiddly and unreadable — floating arrows,\n * disconnected half-lines, two-column false-wires). Instead each control-flow block is framed by ONE clean,\n * continuous bracket drawn in its own guide column:\n * ┌ header (如果真 / 如果 / 判断 / 循环首 …)\n * │ body line (indented inside the bracket)\n * ├ a branch divider within the same block (否则 / 判断 case / 默认)\n * └ the block's end\n * Nested blocks add deeper columns, so the bracket depth alone reads as the nesting level. This keeps the\n * hierarchy obvious and the lines unbroken, which is the whole point.\n *\n * Text emission mirrors {@link stmtToText} (same keywords, mask `' ` prefix, ` ' ` comment suffix,\n * operator-aware expressions), with two display-only differences:\n * - a fall-through block's terminator (如果真结束 / 如果结束 / 判断结束) shows NO text and takes NO line number\n * (the {@link CodeLine.noNo} flag) — the └ corner already marks the end, so the keyword is just noise. Loops\n * keep their 循环尾 text (it carries the loop-back condition for do-while) on the └ row, numbered as usual.\n * - the leading `.` on a control-flow keyword stays in the text but the renderer hides it (see EplCodeBlock).\n * A masked (commented-out) block keeps every keyword as a comment (numbered) and draws no guide line.\n */\nimport { exprToText } from './textCode'\nimport { parseStatementBlock } from './codeDataParser'\nimport type { IdToNameMap } from '../names/idToNameMap'\nimport type { CaseInfo, Expr, StatementBlock, Stmt } from './ast'\n\n/**\n * One guide column's visual on a given line, forming a continuous bracket down a block's own column:\n * - `top` ┌ — the block's header (the vertical drops in here, with a tick toward the keyword).\n * - `bottom` └ — the block's end (the vertical arrives from above and turns toward the row).\n * - `tee` ├ — a branch divider inside the block (否则 / 判断 case / 默认): the vertical passes through.\n * - `mid` │ — a plain pass-through (a body line, or an enclosing block's column running past this row).\n * - `blank` — the column is occupied (so the body stays indented) but no line is drawn: a commented-out\n * (masked) control-flow block keeps its nesting but shows no guide.\n */\nexport type GuideSeg = 'top' | 'bottom' | 'mid' | 'tee' | 'blank'\n\n/** One rendered source line: the text + one guide segment per enclosing/own block column (left to right). */\nexport interface CodeLine {\n text: string\n segs: GuideSeg[]\n /** A hidden control-flow terminator row (carries only the └ guide): rendered, but takes no line number. */\n noNo?: boolean\n}\n\n/**\n * A block's guide: column = nesting depth, spanning [start, end] line indices (start = header ┌, end = the\n * terminator └), with branch tees ├ inside (否则 / 判断 case / 默认). `masked` marks a commented-out block —\n * its column is blanked (kept for indentation) but draws no line.\n */\ninterface Span {\n col: number\n start: number\n end: number\n tees: number[]\n masked?: boolean\n}\n\ninterface EmitCtx {\n texts: string[]\n spans: Span[]\n /** Line indices that should NOT take a line number (hidden control-flow terminators). */\n noNos: Set<number>\n}\n\nfunction maskPrefix(m: boolean): string {\n return m ? \"' \" : ''\n}\nfunction cond(e: Expr | null, nm: IdToNameMap): string {\n return e ? exprToText(e, nm, 0) : ''\n}\n\nfunction push(ctx: EmitCtx, text: string): number {\n const i = ctx.texts.length\n ctx.texts.push(text)\n return i\n}\n\n/**\n * A fall-through block's terminator line. When live, it's hidden — no text (the └ corner draws the end) AND no\n * line number (the 易语言 IDE does not number the 结束 marker). When the block is masked, the keyword is kept\n * as a comment (the block draws no guide, so the terminator stays readable + numbered).\n */\nfunction terminator(ctx: EmitCtx, masked: boolean, keyword: string): number {\n const i = push(ctx, masked ? \"' \" + keyword : '')\n if (!masked) ctx.noNos.add(i)\n return i\n}\n\nfunction emitBlock(block: StatementBlock, nm: IdToNameMap, depth: number, ctx: EmitCtx): void {\n for (const s of block.statements) emitStmt(s, nm, depth, ctx)\n}\n\nfunction emitStmt(stmt: Stmt, nm: IdToNameMap, depth: number, ctx: EmitCtx): void {\n switch (stmt.kind) {\n case 'expression': {\n let t = maskPrefix(stmt.mask)\n if (stmt.expr) t += exprToText(stmt.expr, nm, 0)\n if (stmt.comment != null) t += (stmt.expr == null ? \"' \" : \" ' \") + stmt.comment\n push(ctx, t)\n break\n }\n case 'unexamined':\n push(ctx, maskPrefix(stmt.mask) + stmt.code)\n break\n case 'if': {\n let h = maskPrefix(stmt.mask)\n h +=\n stmt.unexaminedCode == null\n ? '.如果真 (' + cond(stmt.condition, nm) + ')'\n : '.' + stmt.unexaminedCode\n if (stmt.comment != null) h += \" ' \" + stmt.comment\n const start = push(ctx, h)\n emitBlock(stmt.block, nm, depth + 1, ctx)\n const end = terminator(ctx, stmt.mask, '.如果真结束')\n ctx.spans.push({ col: depth, start, end, tees: [], masked: stmt.mask })\n break\n }\n case 'ifElse': {\n let h = maskPrefix(stmt.mask)\n h +=\n stmt.unexaminedCode == null\n ? '.如果 (' + cond(stmt.condition, nm) + ')'\n : '.' + stmt.unexaminedCode\n if (stmt.comment != null) h += \" ' \" + stmt.comment\n const start = push(ctx, h)\n emitBlock(stmt.blockOnTrue, nm, depth + 1, ctx)\n // 否则 divides the block into its true/false halves — shown as a ├ branch row (numbered, like the IDE).\n const elseRow = push(ctx, maskPrefix(stmt.mask) + '.否则')\n emitBlock(stmt.blockOnFalse, nm, depth + 1, ctx)\n const end = terminator(ctx, stmt.mask, '.如果结束')\n ctx.spans.push({ col: depth, start, end, tees: [elseRow], masked: stmt.mask })\n break\n }\n case 'while': {\n let h = maskPrefix(stmt.maskOnStart)\n h +=\n stmt.unexaminedCode == null\n ? '.判断循环首 (' + cond(stmt.condition, nm) + ')'\n : '.' + stmt.unexaminedCode\n if (stmt.commentOnStart != null) h += \" ' \" + stmt.commentOnStart\n const start = push(ctx, h)\n emitBlock(stmt.block, nm, depth + 1, ctx)\n let f = maskPrefix(stmt.maskOnEnd) + '.判断循环尾 ()'\n if (stmt.commentOnEnd != null) f += \" ' \" + stmt.commentOnEnd\n const end = push(ctx, f)\n ctx.spans.push({ col: depth, start, end, tees: [], masked: stmt.maskOnStart })\n break\n }\n case 'doWhile': {\n let h = maskPrefix(stmt.maskOnStart) + '.循环判断首 ()'\n if (stmt.commentOnStart != null) h += \" ' \" + stmt.commentOnStart\n const start = push(ctx, h)\n emitBlock(stmt.block, nm, depth + 1, ctx)\n let f = maskPrefix(stmt.maskOnEnd)\n f +=\n stmt.unexaminedCode == null\n ? '.循环判断尾 (' + cond(stmt.condition, nm) + ')'\n : '.' + stmt.unexaminedCode\n if (stmt.commentOnEnd != null) f += \" ' \" + stmt.commentOnEnd\n const end = push(ctx, f)\n ctx.spans.push({ col: depth, start, end, tees: [], masked: stmt.maskOnStart })\n break\n }\n case 'for': {\n let h = maskPrefix(stmt.maskOnStart)\n if (stmt.unexaminedCode == null) {\n h +=\n '.变量循环首 (' +\n cond(stmt.start, nm) +\n ', ' +\n cond(stmt.end, nm) +\n ', ' +\n cond(stmt.step, nm) +\n ', ' +\n cond(stmt.var, nm) +\n ')'\n } else {\n h += '.' + stmt.unexaminedCode\n }\n if (stmt.commentOnStart != null) h += \" ' \" + stmt.commentOnStart\n const start = push(ctx, h)\n emitBlock(stmt.block, nm, depth + 1, ctx)\n let f = maskPrefix(stmt.maskOnEnd) + '.变量循环尾 ()'\n if (stmt.commentOnEnd != null) f += \" ' \" + stmt.commentOnEnd\n const end = push(ctx, f)\n ctx.spans.push({ col: depth, start, end, tees: [], masked: stmt.maskOnStart })\n break\n }\n case 'counter': {\n let h = maskPrefix(stmt.maskOnStart)\n if (stmt.unexaminedCode == null) {\n h += '.计次循环首 (' + cond(stmt.count, nm) + ', ' + cond(stmt.var, nm) + ')'\n } else {\n h += '.' + stmt.unexaminedCode\n }\n if (stmt.commentOnStart != null) h += \" ' \" + stmt.commentOnStart\n const start = push(ctx, h)\n emitBlock(stmt.block, nm, depth + 1, ctx)\n let f = maskPrefix(stmt.maskOnEnd) + '.计次循环尾 ()'\n if (stmt.commentOnEnd != null) f += \" ' \" + stmt.commentOnEnd\n const end = push(ctx, f)\n ctx.spans.push({ col: depth, start, end, tees: [], masked: stmt.maskOnStart })\n break\n }\n case 'switch':\n emitSwitch(stmt, nm, depth, ctx)\n break\n }\n}\n\nfunction emitSwitch(\n stmt: Extract<Stmt, { kind: 'switch' }>,\n nm: IdToNameMap,\n depth: number,\n ctx: EmitCtx,\n): void {\n if (stmt.cases.length === 0) throw new Error('CodeData: switch must have a case')\n const c0 = stmt.cases[0]\n // The IDE shows the switch head as 判断 (not 判断开始) — the ┌ corner already marks it as the block start.\n let h = maskPrefix(c0.mask)\n if (c0.unexaminedCode == null) {\n h += '.判断 (' + cond(c0.condition, nm) + ')'\n } else {\n h += '.' + c0.unexaminedCode.trimStart()\n }\n if (c0.comment != null) h += \" ' \" + c0.comment\n const start = push(ctx, h)\n emitBlock(c0.block, nm, depth + 1, ctx)\n // Each subsequent 判断 case and the 默认 (default) are branch dividers ├ within the one switch bracket.\n const tees: number[] = []\n for (let i = 1; i < stmt.cases.length; i++) {\n const ci: CaseInfo = stmt.cases[i]\n let cs = maskPrefix(ci.mask)\n cs +=\n ci.unexaminedCode == null ? '.判断 (' + cond(ci.condition, nm) + ')' : '.' + ci.unexaminedCode\n if (ci.comment != null) cs += \" ' \" + ci.comment\n tees.push(push(ctx, cs))\n emitBlock(ci.block, nm, depth + 1, ctx)\n }\n if (stmt.defaultBlock.statements.length > 0) {\n tees.push(push(ctx, maskPrefix(c0.mask) + '.默认'))\n emitBlock(stmt.defaultBlock, nm, depth + 1, ctx)\n }\n const end = terminator(ctx, c0.mask, '.判断结束')\n ctx.spans.push({ col: depth, start, end, tees, masked: c0.mask })\n}\n\n/**\n * Paint each line's guide columns from the collected block spans. A span at column `d` owns one continuous\n * bracket: ┌ at its header (`start`), └ at its end (`end`), ├ at each branch divider, and │ on every other\n * line it spans (its own body lines + enclosing rows it passes through). A masked (commented-out) block draws\n * no line but still occupies its column so its body stays indented.\n */\nfunction paintGuides(ctx: EmitCtx): GuideSeg[][] {\n const rows: GuideSeg[][] = ctx.texts.map(() => [])\n const set = (line: number, col: number, seg: GuideSeg): void => {\n rows[line][col] = seg\n }\n\n for (const sp of ctx.spans) {\n if (sp.masked) {\n for (let i = sp.start; i <= sp.end; i++)\n if (rows[i][sp.col] === undefined) set(i, sp.col, 'blank')\n continue\n }\n set(sp.start, sp.col, 'top')\n set(sp.end, sp.col, 'bottom')\n for (const t of sp.tees) set(t, sp.col, 'tee')\n for (let i = sp.start + 1; i < sp.end; i++)\n if (rows[i][sp.col] === undefined) set(i, sp.col, 'mid')\n }\n\n // Densify each row to columns 0..max (the active spans on any line are contiguous from 0, so gaps shouldn't\n // arise — fill defensively with a plain vertical just in case).\n return rows.map((r) => {\n const out: GuideSeg[] = []\n for (let k = 0; k < r.length; k++) out.push(r[k] ?? 'mid')\n return out\n })\n}\n\n/** Decompile a method's expression data into IDE-style display lines (text + guide segments). */\nexport function methodCodeLines(\n expressionData: Uint8Array,\n nm: IdToNameMap,\n dec: TextDecoder,\n): CodeLine[] {\n const block = parseStatementBlock(expressionData, dec)\n const ctx: EmitCtx = { texts: [], spans: [], noNos: new Set() }\n emitBlock(block, nm, 0, ctx)\n const segs = paintGuides(ctx)\n return ctx.texts.map((text, i) => ({ text, segs: segs[i], noNo: ctx.noNos.has(i) }))\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textCode.js","names":[],"sources":["../../../../src/lib/epl/code/textCode.ts"],"sourcesContent":["/**\n * Text-code rendering — port of every `ToTextCode` across `Expressions/`, `Statements/`, the variable\n * subtypes, ClassInfo / MethodInfo / StructInfo / DllDeclareInfo, and CodeSection.\n *\n * Produces the readable 易语言 source: definitions (`.程序集` / `.子程序` / `.局部变量` …), control flow\n * (`.如果`/`.判断循环首`/`.判断开始` …) and expressions (operators with precedence-aware brackets, calls,\n * member/array access, literals). Newlines are `\\n` (the C# uses Environment.NewLine; callers comparing to a\n * Windows oracle should normalize). Method bodies are decompiled on demand via {@link parseStatementBlock}.\n */\nimport { gbk } from '../io/encoding'\nimport { IdToNameMap } from '../names/idToNameMap'\nimport { getType, TYPE_FORM, TYPE_FORM_SELF, TYPE_CLASS } from '../model/eplSystemId'\nimport type { CallExpr, Expr, StatementBlock, Stmt } from './ast'\nimport { parseStatementBlock } from './codeDataParser'\nimport type { EplVariable } from './variable'\nimport type { ClassInfo, DllDeclareInfo, MethodInfo, StructInfo } from './structures'\nimport type { CodeSection } from '../sections/code'\n\nconst IND = ' '\nfunction ind(n: number): string {\n return IND.repeat(n)\n}\n\n/** `.类型 a, b, c` — trailing empty items are trimmed; all-empty ⇒ just `.类型` (C# WriteDefinitionCode). */\nfunction def(indent: number, type: string, ...items: string[]): string {\n const head = ind(indent) + '.' + type\n let count = items.length\n while (count > 0 && !items[count - 1]) count--\n if (count === 0) return head\n return head + ' ' + items.slice(0, count).join(', ')\n}\n\n// ── operators ─────────────────────────────────────────────────────────────────────────────────────\ntype OpType = 'unary' | 'binary' | 'multi'\ninterface OpInfo {\n op: string\n /** Lower binds tighter. */\n precedence: number\n type: OpType\n}\nconst OPERATOR_MAP = new Map<number, OpInfo>([\n [15, { op: '*', precedence: 2, type: 'multi' }],\n [16, { op: '/', precedence: 2, type: 'multi' }],\n [17, { op: '\\\\', precedence: 3, type: 'multi' }],\n [18, { op: '%', precedence: 4, type: 'multi' }],\n [19, { op: '+', precedence: 5, type: 'multi' }],\n [20, { op: '-', precedence: 5, type: 'multi' }],\n [21, { op: '-', precedence: 1, type: 'unary' }],\n [38, { op: '==', precedence: 6, type: 'binary' }],\n [39, { op: '!=', precedence: 6, type: 'binary' }],\n [40, { op: '<', precedence: 6, type: 'binary' }],\n [41, { op: '>', precedence: 6, type: 'binary' }],\n [42, { op: '<=', precedence: 6, type: 'binary' }],\n [43, { op: '>=', precedence: 6, type: 'binary' }],\n [44, { op: '?=', precedence: 6, type: 'binary' }],\n [45, { op: '&&', precedence: 7, type: 'multi' }],\n [46, { op: '||', precedence: 8, type: 'multi' }],\n [52, { op: '=', precedence: 9, type: 'binary' }],\n])\n\nfunction invokeWithPrec(e: Expr, nameMap: IdToNameMap, indent: number, expected: number): string {\n if (e.kind === 'call') return callToText(e, nameMap, indent, expected)\n return exprToText(e, nameMap, indent)\n}\n\nfunction operatorToText(\n c: CallExpr,\n nameMap: IdToNameMap,\n indent: number,\n expectedLowestPrecedence: number,\n op: OpInfo,\n): string {\n const addBrackets = op.precedence > expectedLowestPrecedence\n let s = addBrackets ? '(' : ''\n const params = c.paramList ?? []\n if (op.type === 'unary') {\n s += op.op\n if (params.length > 0) s += invokeWithPrec(params[0], nameMap, indent, op.precedence)\n } else {\n // binary / multi: 0 params ⇒ \" op \", 1 ⇒ \"a op \", ≥2 ⇒ \"a op b op c\"\n if (params.length === 0) {\n s += ' ' + op.op + ' '\n } else {\n s += invokeWithPrec(params[0], nameMap, indent, op.precedence)\n s += ' ' + op.op + ' '\n for (let i = 1; i < params.length; i++) {\n if (i > 1) s += ' ' + op.op + ' '\n s += invokeWithPrec(params[i], nameMap, indent, op.precedence - 1)\n }\n }\n }\n if (addBrackets) s += ')'\n return s\n}\n\n// ── expressions ─────────────────────────────────────────────────────────────────────────────────────\nfunction paramListToText(params: Expr[] | null, nameMap: IdToNameMap, indent: number): string {\n return '(' + (params ?? []).map((p) => exprToText(p, nameMap, indent)).join(', ') + ')'\n}\n\nfunction callToText(\n c: CallExpr,\n nameMap: IdToNameMap,\n indent: number,\n expectedLowestPrecedence: number,\n): string {\n const op = c.target == null && c.libraryId === 0 ? OPERATOR_MAP.get(c.methodId) : undefined\n if (op) return operatorToText(c, nameMap, indent, expectedLowestPrecedence, op)\n let s = ''\n if (c.target != null) s += exprToText(c.target, nameMap, indent) + '.'\n if (c.invokeSpecial && c.libraryId === -2) {\n const classId = nameMap.methodIdToClassId.get(c.methodId)\n if (classId !== undefined && getType(classId) === TYPE_CLASS) {\n s += nameMap.getUserDefinedName(classId) + '.'\n }\n }\n s +=\n c.libraryId === -2 || c.libraryId === -3\n ? nameMap.getUserDefinedName(c.methodId)\n : nameMap.getLibCmdName(c.libraryId, c.methodId)\n s += ' '\n s += paramListToText(c.paramList, nameMap, indent)\n return s\n}\n\n/** Render a single arbitrary expression. */\nexport function exprToText(e: Expr, nameMap: IdToNameMap, indent: number): string {\n switch (e.kind) {\n case 'number':\n return formatNumber(e.value)\n case 'string':\n return '“' + e.value + '”' // “ ”\n case 'bool':\n return e.value ? '真' : '假'\n case 'datetime':\n return '[' + formatOADate(e.oa) + ']'\n case 'constant':\n return (\n '#' +\n (e.libraryId === -2\n ? nameMap.getUserDefinedName(e.constantId)\n : nameMap.getLibConstantName(e.libraryId, e.constantId))\n )\n case 'emnu':\n return (\n '#' +\n nameMap.getLibTypeName(e.libraryId, e.structId) +\n '.' +\n nameMap.getLibTypeMemberName(e.libraryId, e.structId, e.memberId)\n )\n case 'variable':\n return nameMap.getUserDefinedName(e.id)\n case 'methodPtr':\n return '&' + nameMap.getUserDefinedName(e.methodId)\n case 'default':\n return ''\n case 'array':\n return '{' + e.items.map((x) => exprToText(x, nameMap, indent)).join(', ') + '}'\n case 'accessMember':\n return accessMemberToText(e, nameMap, indent)\n case 'accessArray':\n return (\n exprToText(e.target, nameMap, indent) + '[' + exprToText(e.index, nameMap, indent) + ']'\n )\n case 'call':\n return callToText(e, nameMap, indent, Number.MAX_SAFE_INTEGER)\n default:\n return '' // transient markers — never rendered\n }\n}\n\nfunction accessMemberToText(\n e: Extract<Expr, { kind: 'accessMember' }>,\n nameMap: IdToNameMap,\n indent: number,\n): string {\n let s = ''\n // In a window class, accessing the window's own system property: omit the \"target.\" prefix.\n if (!(e.target.kind === 'variable' && getType(e.target.id) === TYPE_FORM_SELF)) {\n s += exprToText(e.target, nameMap, indent) + '.'\n }\n if (e.libraryId === -2) {\n if (getType(e.structId) === TYPE_FORM && (e.memberId & 0xff000000) === 0) {\n s += nameMap.getLibTypeMemberName(0, 0, e.memberId - 1)\n } else {\n s += nameMap.getUserDefinedName(e.memberId)\n }\n } else {\n s += nameMap.getLibTypeMemberName(e.libraryId, e.structId, e.memberId)\n }\n return s\n}\n\nexport function formatNumber(v: number): string {\n return String(v)\n}\n\nfunction pad(n: number, w = 2): string {\n return String(n).padStart(w, '0')\n}\nexport function formatOADate(oa: number): string {\n // OLE Automation date → calendar fields, computed in UTC so the components match C# DateTime (no TZ shift).\n const ms = Math.round(oa * 86400000) + Date.UTC(1899, 11, 30)\n const d = new Date(ms)\n const date = `${pad(d.getUTCFullYear(), 4)}年${pad(d.getUTCMonth() + 1)}月${pad(d.getUTCDate())}日`\n const h = d.getUTCHours()\n const m = d.getUTCMinutes()\n const s = d.getUTCSeconds()\n if (h === 0 && m === 0 && s === 0) return date\n return `${date}${pad(h)}时${pad(m)}分${pad(s)}秒`\n}\n\n// ── statements ─────────────────────────────────────────────────────────────────────────────────────\nfunction maskPrefix(m: boolean): string {\n return m ? \"' \" : ''\n}\nfunction cond(e: Expr | null, nameMap: IdToNameMap, indent: number): string {\n return e ? exprToText(e, nameMap, indent) : ''\n}\n\nexport function blockToText(block: StatementBlock, nameMap: IdToNameMap, indent: number): string {\n return block.statements.map((s) => stmtToText(s, nameMap, indent)).join('\\n')\n}\n\nexport function stmtToText(stmt: Stmt, nameMap: IdToNameMap, indent: number): string {\n switch (stmt.kind) {\n case 'expression': {\n let s = ind(indent) + maskPrefix(stmt.mask)\n if (stmt.expr) s += exprToText(stmt.expr, nameMap, indent)\n if (stmt.comment != null) s += (stmt.expr == null ? \"' \" : \" ' \") + stmt.comment\n return s\n }\n case 'unexamined':\n return ind(indent) + maskPrefix(stmt.mask) + stmt.code\n case 'if': {\n let s = ind(indent) + maskPrefix(stmt.mask)\n s +=\n stmt.unexaminedCode == null\n ? '.如果真 (' + cond(stmt.condition, nameMap, indent) + ')'\n : '.' + stmt.unexaminedCode\n if (stmt.comment != null) s += \" ' \" + stmt.comment\n s += '\\n' + blockToText(stmt.block, nameMap, indent + 1) + '\\n'\n s += ind(indent) + maskPrefix(stmt.mask) + '.如果真结束'\n return s\n }\n case 'ifElse': {\n let s = ind(indent) + maskPrefix(stmt.mask)\n s +=\n stmt.unexaminedCode == null\n ? '.如果 (' + cond(stmt.condition, nameMap, indent) + ')'\n : '.' + stmt.unexaminedCode\n if (stmt.comment != null) s += \" ' \" + stmt.comment\n s += '\\n' + blockToText(stmt.blockOnTrue, nameMap, indent + 1) + '\\n'\n s += ind(indent) + maskPrefix(stmt.mask) + '.否则\\n'\n s += blockToText(stmt.blockOnFalse, nameMap, indent + 1) + '\\n'\n s += ind(indent) + maskPrefix(stmt.mask) + '.如果结束'\n return s\n }\n case 'while': {\n let s = ind(indent) + maskPrefix(stmt.maskOnStart)\n s +=\n stmt.unexaminedCode == null\n ? '.判断循环首 (' + cond(stmt.condition, nameMap, indent) + ')'\n : '.' + stmt.unexaminedCode\n if (stmt.commentOnStart != null) s += \" ' \" + stmt.commentOnStart\n s += '\\n' + blockToText(stmt.block, nameMap, indent + 1) + '\\n'\n s += ind(indent) + maskPrefix(stmt.maskOnEnd) + '.判断循环尾 ()'\n if (stmt.commentOnEnd != null) s += \" ' \" + stmt.commentOnEnd\n return s\n }\n case 'doWhile': {\n let s = ind(indent) + maskPrefix(stmt.maskOnStart) + '.循环判断首 ()'\n if (stmt.commentOnStart != null) s += \" ' \" + stmt.commentOnStart\n s += '\\n' + blockToText(stmt.block, nameMap, indent + 1) + '\\n'\n s += ind(indent) + maskPrefix(stmt.maskOnEnd)\n s +=\n stmt.unexaminedCode == null\n ? '.循环判断尾 (' + cond(stmt.condition, nameMap, indent) + ')'\n : '.' + stmt.unexaminedCode\n if (stmt.commentOnEnd != null) s += \" ' \" + stmt.commentOnEnd\n return s\n }\n case 'for': {\n let s = ind(indent) + maskPrefix(stmt.maskOnStart)\n if (stmt.unexaminedCode == null) {\n s +=\n '.变量循环首 (' +\n cond(stmt.start, nameMap, indent) +\n ', ' +\n cond(stmt.end, nameMap, indent) +\n ', ' +\n cond(stmt.step, nameMap, indent) +\n ', ' +\n cond(stmt.var, nameMap, indent) +\n ')'\n } else {\n s += '.' + stmt.unexaminedCode\n }\n if (stmt.commentOnStart != null) s += \" ' \" + stmt.commentOnStart\n s += '\\n' + blockToText(stmt.block, nameMap, indent + 1) + '\\n'\n s += ind(indent) + maskPrefix(stmt.maskOnEnd) + '.变量循环尾 ()'\n if (stmt.commentOnEnd != null) s += \" ' \" + stmt.commentOnEnd\n return s\n }\n case 'counter': {\n let s = ind(indent) + maskPrefix(stmt.maskOnStart)\n if (stmt.unexaminedCode == null) {\n s +=\n '.计次循环首 (' +\n cond(stmt.count, nameMap, indent) +\n ', ' +\n cond(stmt.var, nameMap, indent) +\n ')'\n } else {\n s += '.' + stmt.unexaminedCode\n }\n if (stmt.commentOnStart != null) s += \" ' \" + stmt.commentOnStart\n s += '\\n' + blockToText(stmt.block, nameMap, indent + 1) + '\\n'\n s += ind(indent) + maskPrefix(stmt.maskOnEnd) + '.计次循环尾 ()'\n if (stmt.commentOnEnd != null) s += \" ' \" + stmt.commentOnEnd\n return s\n }\n case 'switch':\n return switchToText(stmt, nameMap, indent)\n }\n}\n\nfunction switchToText(\n stmt: Extract<Stmt, { kind: 'switch' }>,\n nameMap: IdToNameMap,\n indent: number,\n): string {\n if (stmt.cases.length === 0) throw new Error('CodeData: switch must have a case')\n const c0 = stmt.cases[0]\n let s = ind(indent) + maskPrefix(c0.mask)\n if (c0.unexaminedCode == null) {\n s += '.判断开始 (' + cond(c0.condition, nameMap, indent) + ')'\n } else {\n s += '.判断开始' + c0.unexaminedCode.trimStart().slice('判断'.length)\n }\n if (c0.comment != null) s += \" ' \" + c0.comment\n s += '\\n' + blockToText(c0.block, nameMap, indent + 1)\n for (let i = 1; i < stmt.cases.length; i++) {\n const ci = stmt.cases[i]\n s += '\\n' + ind(indent) + maskPrefix(ci.mask)\n s +=\n ci.unexaminedCode == null\n ? '.判断 (' + cond(ci.condition, nameMap, indent) + ')'\n : '.' + ci.unexaminedCode\n if (ci.comment != null) s += \" ' \" + ci.comment\n s += '\\n' + blockToText(ci.block, nameMap, indent + 1)\n }\n s += '\\n' + ind(indent) + maskPrefix(c0.mask) + '.默认\\n'\n s += blockToText(stmt.defaultBlock, nameMap, indent + 1)\n s += '\\n' + ind(indent) + maskPrefix(c0.mask) + '.判断结束'\n return s\n}\n\n// ── definitions ─────────────────────────────────────────────────────────────────────────────────────\nfunction uboundStr(uBound: number[]): string {\n if (uBound.length === 0) return ''\n if (uBound.length === 1 && uBound[0] === 0) return '\"0\"'\n return '\"' + uBound.map((x) => (x === 0 ? '' : String(x))).join(',') + '\"'\n}\n\nexport function variableToText(v: EplVariable, nameMap: IdToNameMap, indent: number): string {\n const name = nameMap.getUserDefinedName(v.id)\n const dt = nameMap.getDataTypeName(v.dataType)\n switch (v.kind) {\n case 'global':\n return def(\n indent,\n '全局变量',\n name,\n dt,\n v.flags & 0x100 ? '公开' : '',\n uboundStr(v.uBound),\n v.comment,\n )\n case 'local':\n return def(\n indent,\n '局部变量',\n name,\n dt,\n v.flags & 0x1 ? '静态' : '',\n uboundStr(v.uBound),\n v.comment,\n )\n case 'classMember':\n return def(indent, '程序集变量', name, dt, '', uboundStr(v.uBound), v.comment)\n case 'structMember':\n return def(\n indent,\n '成员',\n name,\n dt,\n v.flags & 0x2 ? '传址' : '',\n uboundStr(v.uBound),\n v.comment,\n )\n case 'methodParameter': {\n const f = [\n v.flags & 0x2 ? '参考' : '',\n v.flags & 0x4 ? '可空' : '',\n v.flags & 0x8 ? '数组' : '',\n ]\n .filter(Boolean)\n .join(' ')\n return def(indent, '参数', name, dt, f, v.comment)\n }\n case 'dllParameter': {\n const f = [v.flags & 0x2 ? '传址' : '', v.flags & 0x8 ? '数组' : ''].filter(Boolean).join(' ')\n return def(indent, '参数', name, dt, f, v.comment)\n }\n }\n}\n\nexport function methodToText(\n m: MethodInfo,\n nameMap: IdToNameMap,\n indent: number,\n dec: TextDecoder,\n writeCode = true,\n): string {\n let s = def(\n indent,\n '子程序',\n nameMap.getUserDefinedName(m.id),\n nameMap.getDataTypeName(m.returnDataType),\n m.flags & 0x8 ? '公开' : '',\n m.comment,\n )\n if (m.parameters.length) {\n s += '\\n' + m.parameters.map((p) => variableToText(p, nameMap, indent)).join('\\n')\n }\n if (!writeCode) return s\n if (m.variables.length) {\n s += '\\n' + m.variables.map((v) => variableToText(v, nameMap, indent)).join('\\n')\n }\n s += '\\n\\n'\n s += blockToText(parseStatementBlock(m.codeData.expressionData, dec), nameMap, indent)\n return s\n}\n\nexport function structToText(st: StructInfo, nameMap: IdToNameMap, indent: number): string {\n let s = def(\n indent,\n '数据类型',\n nameMap.getUserDefinedName(st.id),\n st.flags & 0x1 ? '公开' : '',\n st.comment,\n )\n s += '\\n' + st.members.map((mem) => variableToText(mem, nameMap, indent + 1)).join('\\n')\n return s\n}\n\nexport function dllDeclareToText(d: DllDeclareInfo, nameMap: IdToNameMap, indent: number): string {\n let s = def(\n indent,\n 'DLL命令',\n nameMap.getUserDefinedName(d.id),\n nameMap.getDataTypeName(d.returnDataType),\n d.libraryName,\n d.entryPoint,\n d.flags & 0x2 ? '公开' : '',\n d.comment,\n )\n s += '\\n' + d.parameters.map((p) => variableToText(p, nameMap, indent + 1)).join('\\n')\n return s\n}\n\nexport function classToText(\n c: ClassInfo,\n nameMap: IdToNameMap,\n indent: number,\n codeSection: CodeSection | null,\n dec: TextDecoder,\n writeCode = true,\n): string {\n const baseName =\n c.baseClass === 0 || c.baseClass === -1 ? '' : nameMap.getUserDefinedName(c.baseClass)\n let s = def(indent, '程序集', nameMap.getUserDefinedName(c.id), baseName, '', c.comment)\n s += '\\n' + c.variables.map((v) => variableToText(v, nameMap, indent)).join('\\n')\n if (codeSection != null) {\n s += '\\n\\n'\n const ids = new Set(c.methods)\n const methods = codeSection.methods.filter((m) => ids.has(m.id))\n s += methods.map((m) => methodToText(m, nameMap, indent, dec, writeCode)).join('\\n\\n')\n }\n return s\n}\n\n/** Render the whole code section (global vars + classes [+ their methods] + DLL declares + structs). */\nexport function codeSectionToText(\n cs: CodeSection,\n nameMap: IdToNameMap,\n dec: TextDecoder,\n indent = 0,\n writeMethod = true,\n writeCode = true,\n): string {\n const parts: string[] = []\n if (cs.globalVariables.length) {\n parts.push(cs.globalVariables.map((v) => variableToText(v, nameMap, indent)).join('\\n') + '\\n')\n }\n parts.push(\n cs.classes\n .map((c) => classToText(c, nameMap, indent, writeMethod ? cs : null, dec, writeCode))\n .join('\\n'),\n )\n if (cs.dllDeclares.length) {\n parts.push('\\n' + cs.dllDeclares.map((d) => dllDeclareToText(d, nameMap, indent)).join('\\n'))\n }\n if (cs.structs.length) {\n parts.push('\\n' + cs.structs.map((st) => structToText(st, nameMap, indent)).join('\\n'))\n }\n return parts.join('')\n}\n\nexport { gbk }\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eplDocument.js","names":[],"sources":["../../../src/lib/epl/eplDocument.ts"],"sourcesContent":["/**\n * Top-level loader — port of `EplDocument.Load`.\n *\n * Drives {@link ProjectFileReader} section-by-section: a registered {@link SectionType} parses known keys,\n * everything else becomes a {@link GeneralSection}. When the system-info section is seen, its codepage takes\n * over for the rest of the document. The end-of-file section is consumed but not added to the list.\n *\n * `EplSection` is the discriminated union the UI consumes (narrow on `kind`). The registry grows as more\n * section milestones land.\n */\nimport { decoderForLanguage, gbk } from './io/encoding'\nimport type { GeneralSection, ParserContext, SectionType } from './model/section'\nimport { EplEncryptedError, NotEplFileError, ProjectFileReader } from './projectFileReader'\nimport { CodeSectionKey, type CodeSection } from './sections/code'\nimport { ESystemInfoKey, type ESystemInfoSection } from './sections/esystemInfo'\nimport { ProjectConfigKey, type ProjectConfigSection } from './sections/projectConfig'\nimport { LosableSectionKey, type LosableSection } from './sections/losable'\nimport { ResourceSectionKey, type ResourceSection } from './sections/resource'\n\nexport type EplSection =\n | ESystemInfoSection\n | ProjectConfigSection\n | CodeSection\n | ResourceSection\n | LosableSection\n | GeneralSection\n\nconst KEY_END = 0x07007319\n\n/** key → parser registry (mirrors C# PredefinedSections.Keys). Grows as more section milestones land. */\nconst PREDEFINED = new Map<number, SectionType>([\n [ESystemInfoKey.key, ESystemInfoKey],\n [ProjectConfigKey.key, ProjectConfigKey],\n [CodeSectionKey.key, CodeSectionKey],\n [ResourceSectionKey.key, ResourceSectionKey],\n [LosableSectionKey.key, LosableSectionKey],\n])\n\nexport interface EplDocument {\n /** All parsed sections in file order (end-of-file section excluded). */\n sections: EplSection[]\n /** Convenience handles for the common sections (filled after parsing). */\n systemInfo?: ESystemInfoSection\n projectConfig?: ProjectConfigSection\n code?: CodeSection\n resource?: ResourceSection\n losable?: LosableSection\n}\n\n/** Parse a `.e`/`.ec` byte buffer into its sections. Throws {@link EplEncryptedError} / {@link NotEplFileError}. */\nexport function loadEpl(bytes: Uint8Array): EplDocument {\n const reader = new ProjectFileReader(bytes)\n let dec = gbk()\n const sections: EplSection[] = []\n while (!reader.isFinish) {\n const raw = reader.readSection()\n if (raw.key === KEY_END) break // end-of-file section: consumed, not listed\n const type = PREDEFINED.get(raw.key)\n let section: EplSection\n if (type) {\n const ctx: ParserContext = { dec, cryptEC: reader.cryptEC }\n section = type.parse(raw.data, ctx) as EplSection\n } else {\n section = {\n kind: 'general',\n sectionKey: raw.key,\n sectionName: raw.name,\n isOptional: raw.isOptional,\n data: raw.data,\n }\n }\n // The system-info section dictates the codepage for everything that follows it.\n if (section.kind === 'systemInfo') dec = decoderForLanguage(section.language)\n sections.push(section)\n }\n\n const doc: EplDocument = { sections }\n for (const s of sections) {\n if (s.kind === 'systemInfo') doc.systemInfo = s\n else if (s.kind === 'projectConfig') doc.projectConfig = s\n else if (s.kind === 'code') doc.code = s\n else if (s.kind === 'resource') doc.resource = s\n else if (s.kind === 'losable') doc.losable = s\n }\n return doc\n}\n\nexport { EplEncryptedError, NotEplFileError }\n"],"mappings":";;AA2BA,IAAM,IAAU,WAGV,IAAa,IAAI,IAAyB;CAC9C,CAAC,eAAe,KAAK,cAAc;CACnC,CAAC,iBAAiB,KAAK,gBAAgB;CACvC,CAAC,eAAe,KAAK,cAAc;CACnC,CAAC,mBAAmB,KAAK,kBAAkB;CAC3C,CAAC,kBAAkB,KAAK,iBAAiB;AAC3C,CAAC;AAcD,SAAgB,EAAQ,GAAgC;CACtD,IAAM,IAAS,IAAI,EAAkB,CAAK,GACtC,IAAM,IAAI,GACR,IAAyB,CAAC;CAChC,OAAO,CAAC,EAAO,WAAU;EACvB,IAAM,IAAM,EAAO,YAAY;EAC/B,IAAI,EAAI,QAAQ,GAAS;EACzB,IAAM,IAAO,EAAW,IAAI,EAAI,GAAG,GAC/B;EACJ,IAAI,GAAM;GACR,IAAM,IAAqB;IAAE;IAAK,SAAS,EAAO;GAAQ;GAC1D,IAAU,EAAK,MAAM,EAAI,MAAM,CAAG;EACpC,OACE,IAAU;GACR,MAAM;GACN,YAAY,EAAI;GAChB,aAAa,EAAI;GACjB,YAAY,EAAI;GAChB,MAAM,EAAI;EACZ;EAIF,AADI,EAAQ,SAAS,iBAAc,IAAM,mBAAmB,EAAQ,QAAQ,IAC5E,EAAS,KAAK,CAAO;CACvB;CAEA,IAAM,IAAmB,EAAE,YAAS;CACpC,KAAK,IAAM,KAAK,GACd,AAAI,EAAE,SAAS,eAAc,EAAI,aAAa,IACrC,EAAE,SAAS,kBAAiB,EAAI,gBAAgB,IAChD,EAAE,SAAS,SAAQ,EAAI,OAAO,IAC9B,EAAE,SAAS,aAAY,EAAI,WAAW,IACtC,EAAE,SAAS,cAAW,EAAI,UAAU;CAE/C,OAAO;AACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imageList.js","names":[],"sources":["../../../src/lib/epl/imageList.ts"],"sourcesContent":["/**\n * 易语言 图片组 (picture group) decoder. A 图片组 image constant stores a Windows common-control ImageList\n * stream (the same bytes `ImageList_Write` emits) wrapped in an 8-byte 易语言 header `\"MITL\" + 00 00 00 FF`:\n *\n * off 0 \"MITL\" + 4 bytes (易语言 wrapper)\n * off 8 ILHEAD (28 bytes): u16 magic 'IL'(0x4C49) · u16 version · u16 cCurImage · u16 cMaxImage ·\n * u16 cGrow · u16 cx · u16 cy · u32 bkColor · u16 flags · i16 overlays[4]\n * off 36 color bitmap (BMP) — all frames packed left→right, top→bottom in a `cols = bmpW/cx` grid\n * … mask bitmap (BMP, 1bpp) — unused here; the 32bpp color bitmap carries its own alpha\n *\n * We parse the header, decode the uncompressed color BMP to top-down RGBA, then slice it into the `cCurImage`\n * frames of `cx × cy`. Pure logic (no DOM) so it stays testable; the component turns each frame's RGBA into a\n * canvas. Returns null for anything that isn't a well-formed MITL ImageList so the caller falls back to treating\n * the bytes as a single image.\n */\n\nexport interface EplImageFrame {\n width: number\n height: number\n /** RGBA, top-down, length = width*height*4. */\n rgba: Uint8ClampedArray\n}\n\nexport interface EplImageList {\n cx: number\n cy: number\n frames: EplImageFrame[]\n}\n\nfunction u16(b: Uint8Array, o: number): number {\n return b[o] | (b[o + 1] << 8)\n}\nfunction i32(b: Uint8Array, o: number): number {\n return b[o] | (b[o + 1] << 8) | (b[o + 2] << 16) | (b[o + 3] << 24)\n}\n\n/** Decode an uncompressed (BI_RGB) 24/32-bpp BMP at offset `o` to top-down RGBA, or null if unsupported. */\nfunction decodeBmp(\n b: Uint8Array,\n o: number,\n): { width: number; height: number; rgba: Uint8ClampedArray } | null {\n if (b[o] !== 0x42 || b[o + 1] !== 0x4d) return null // \"BM\"\n const offBits = i32(b, o + 10) >>> 0\n const w = i32(b, o + 18)\n let h = i32(b, o + 22)\n const bpp = u16(b, o + 28)\n const comp = i32(b, o + 30) >>> 0\n if (comp !== 0 || (bpp !== 32 && bpp !== 24)) return null\n const topDown = h < 0\n h = Math.abs(h)\n if (w <= 0 || h <= 0 || w > 8192 || h > 8192) return null\n const bytesPP = bpp >> 3\n const stride = (((w * bpp + 31) / 32) | 0) * 4 // rows padded to a 4-byte boundary\n const px = o + offBits\n if (px + stride * h > b.length) return null\n const rgba = new Uint8ClampedArray(w * h * 4)\n let anyAlpha = false\n for (let y = 0; y < h; y++) {\n const srcRow = topDown ? y : h - 1 - y\n let sp = px + srcRow * stride\n let dp = y * w * 4\n for (let x = 0; x < w; x++) {\n rgba[dp] = b[sp + 2] // R ← BMP is BGR(A)\n rgba[dp + 1] = b[sp + 1] // G\n rgba[dp + 2] = b[sp] // B\n const a = bpp === 32 ? b[sp + 3] : 255\n if (a !== 0) anyAlpha = true\n rgba[dp + 3] = a\n sp += bytesPP\n dp += 4\n }\n }\n // A 32bpp BITMAPINFOHEADER's 4th byte is officially \"reserved\"; if it's zero everywhere it isn't an alpha\n // channel — render opaque rather than fully transparent.\n if (!anyAlpha) for (let i = 3; i < rgba.length; i += 4) rgba[i] = 255\n return { width: w, height: h, rgba }\n}\n\n/** Parse a 图片组 (MITL ImageList) into its frames, or null if the bytes aren't one. */\nexport function parseEplImageList(bytes: Uint8Array): EplImageList | null {\n if (bytes.length < 64) return null\n // 易语言 wrapper magic \"MITL\"\n if (bytes[0] !== 0x4d || bytes[1] !== 0x49 || bytes[2] !== 0x54 || bytes[3] !== 0x4c) return null\n // locate the ImageList 'IL' (0x4C49) magic — normally at offset 8\n let h = -1\n for (let i = 4; i < 24; i++) {\n if (bytes[i] === 0x49 && bytes[i + 1] === 0x4c) {\n h = i\n break\n }\n }\n if (h < 0) return null\n const cCur = u16(bytes, h + 4)\n const cx = u16(bytes, h + 10)\n const cy = u16(bytes, h + 12)\n if (cCur <= 0 || cCur > 4096 || cx <= 0 || cy <= 0 || cx > 1024 || cy > 1024) return null\n const color = decodeBmp(bytes, h + 28)\n if (!color) return null\n const cols = Math.max(1, Math.floor(color.width / cx))\n const frames: EplImageFrame[] = []\n for (let i = 0; i < cCur; i++) {\n const x0 = (i % cols) * cx\n const y0 = Math.floor(i / cols) * cy\n if (x0 + cx > color.width || y0 + cy > color.height) break\n const rgba = new Uint8ClampedArray(cx * cy * 4)\n for (let y = 0; y < cy; y++) {\n let sp = ((y0 + y) * color.width + x0) * 4\n let dp = y * cx * 4\n for (let x = 0; x < cx * 4; x++) rgba[dp++] = color.rgba[sp++]\n }\n frames.push({ width: cx, height: cy, rgba })\n }\n return frames.length > 0 ? { cx, cy, frames } : null\n}\n"],"mappings":"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"}
|
package/dist/lib/epl/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/lib/epl/index.ts"],"sourcesContent":["/**\n * Public entry for the e-language (易语言) `.e`/`.ec` reader — the only module the UI imports.\n *\n * {@link decompileEpl} turns the raw bytes into a preview: project metadata + a structured {@link EplProgram}\n * (classes / methods / variables / structs / DLL declares / constants / forms / libraries, with method bodies\n * reconstructed by the code-data decompiler) that the IDE-style viewer renders directly. Throws\n * {@link EplEncryptedError} for encrypted files and {@link NotEplFileError} when the bytes aren't a project\n * file — the viewer turns those into friendly messages.\n */\nimport { loadEpl, type EplDocument, EplEncryptedError, NotEplFileError } from './eplDocument'\nimport { buildProgram, type EplProgram } from './program'\n\nexport interface EplLibrary {\n name: string\n fileName: string\n version: string\n}\n\nexport interface EplProjectInfo {\n name: string\n description: string\n author: string\n version: string\n copyright: string\n email: string\n homepage: string\n}\n\nexport interface EplMeta {\n /** 1=源码(.e), 3=模块(.ec). */\n fileType: 'source' | 'module' | 'unknown'\n /** The e-language IDE version that wrote the file, e.g. `5.71`. */\n systemVersion: string\n project: EplProjectInfo | null\n libraries: EplLibrary[]\n counts: {\n classes: number\n methods: number\n globalVariables: number\n structs: number\n dllDeclares: number\n }\n}\n\nexport interface EplPreview {\n meta: EplMeta\n /** Structured program model (classes / tables) rendered by the IDE-style viewer. */\n program: EplProgram\n}\n\nfunction ver(parts: number[]): string {\n return parts.join('.')\n}\n\nfunction buildMeta(doc: EplDocument): EplMeta {\n const si = doc.systemInfo\n const pc = doc.projectConfig\n const cs = doc.code\n const fileType = si?.fileType === 1 ? 'source' : si?.fileType === 3 ? 'module' : 'unknown'\n return {\n fileType,\n systemVersion: si ? ver(si.esystemVersion) : '',\n project: pc\n ? {\n name: pc.name,\n description: pc.description,\n author: pc.author,\n version: ver(pc.version),\n copyright: pc.copyright,\n email: pc.email,\n homepage: pc.homepage,\n }\n : null,\n libraries: (cs?.libraries ?? []).map((l) => ({\n name: l.name,\n fileName: l.fileName,\n version: ver(l.version),\n })),\n counts: {\n classes: cs?.classes.length ?? 0,\n methods: cs?.methods.length ?? 0,\n globalVariables: cs?.globalVariables.length ?? 0,\n structs: cs?.structs.length ?? 0,\n dllDeclares: cs?.dllDeclares.length ?? 0,\n },\n }\n}\n\n/** Parse + decompile a `.e`/`.ec` byte buffer into a preview (metadata + structured program model). */\nexport function decompileEpl(bytes: Uint8Array): EplPreview {\n const doc = loadEpl(bytes)\n return { meta: buildMeta(doc), program: buildProgram(doc) }\n}\n\nexport { loadEpl, EplEncryptedError, NotEplFileError }\nexport type { EplDocument, EplProgram }\nexport type {\n EplClassView,\n EplClassKind,\n EplMethodView,\n EplVarRow,\n EplStructView,\n EplDllView,\n EplConstantRow,\n EplResourceRow,\n EplFormView,\n EplLibraryView,\n} from './program'\nexport type { CodeLine, GuideSeg } from './code/codeLines'\nexport { parseEplImageList } from './imageList'\nexport type { EplImageList, EplImageFrame } from './imageList'\n"],"mappings":";;;;;AAkDA,SAAS,EAAI,GAAyB;CACpC,OAAO,EAAM,KAAK,GAAG;AACvB;AAEA,SAAS,EAAU,GAA2B;CAC5C,IAAM,IAAK,EAAI,YACT,IAAK,EAAI,eACT,IAAK,EAAI;CAEf,OAAO;EACL,UAFe,GAAI,aAAa,IAAI,WAAW,GAAI,aAAa,IAAI,WAAW;EAG/E,eAAe,IAAK,EAAI,EAAG,cAAc,IAAI;EAC7C,SAAS,IACL;GACE,MAAM,EAAG;GACT,aAAa,EAAG;GAChB,QAAQ,EAAG;GACX,SAAS,EAAI,EAAG,OAAO;GACvB,WAAW,EAAG;GACd,OAAO,EAAG;GACV,UAAU,EAAG;EACf,IACA;EACJ,YAAY,GAAI,aAAa,CAAC,EAAA,CAAG,KAAK,OAAO;GAC3C,MAAM,EAAE;GACR,UAAU,EAAE;GACZ,SAAS,EAAI,EAAE,OAAO;EACxB,EAAE;EACF,QAAQ;GACN,SAAS,GAAI,QAAQ,UAAU;GAC/B,SAAS,GAAI,QAAQ,UAAU;GAC/B,iBAAiB,GAAI,gBAAgB,UAAU;GAC/C,SAAS,GAAI,QAAQ,UAAU;GAC/B,aAAa,GAAI,YAAY,UAAU;EACzC;CACF;AACF;AAGA,SAAgB,EAAa,GAA+B;CAC1D,IAAM,IAAM,EAAQ,CAAK;CACzB,OAAO;EAAE,MAAM,EAAU,CAAG;EAAG,SAAS,EAAa,CAAG;CAAE;AAC5D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binaryReader.js","names":[],"sources":["../../../../src/lib/epl/io/binaryReader.ts"],"sourcesContent":["/**\n * Little-endian binary reader with a movable cursor — the READ-side port of EProjectFile's\n * `System.IO.BinaryReader` + `Internal/ExtensionMethod.cs`. Every multi-byte integer in a `.e`/`.ec` file is\n * little-endian; the cursor (`pos`) is mutable because the block readers seek by offset (C# `BaseStream.Position`).\n *\n * String reads take an explicit {@link TextDecoder} (defaulting to the reader's active `dec`), mirroring how the\n * C# helpers pass an `Encoding` — the section/document layer swaps in the codepage chosen by the system-info\n * section. We only decode (read), never encode: this is a read-only previewer.\n */\nimport { gbk } from './encoding'\n\nexport class BinaryReader {\n readonly bytes: Uint8Array\n private readonly view: DataView\n /** Cursor in bytes. Mutable: {@link readBlocksWithIdAndOffest} seeks via per-block offsets. */\n pos: number\n /** Active decoder for string reads — defaults to GBK; the document swaps it per the system-info section. */\n dec: TextDecoder\n\n constructor(bytes: Uint8Array, dec: TextDecoder = gbk(), pos = 0) {\n this.bytes = bytes\n this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)\n this.pos = pos\n this.dec = dec\n }\n\n get length(): number {\n return this.bytes.length\n }\n get eof(): boolean {\n return this.pos >= this.bytes.length\n }\n\n // ── primitives (little-endian) ──────────────────────────────────────────────────────────────────\n readInt32(): number {\n const v = this.view.getInt32(this.pos, true)\n this.pos += 4\n return v\n }\n readUInt32(): number {\n const v = this.view.getUint32(this.pos, true)\n this.pos += 4\n return v\n }\n readInt16(): number {\n const v = this.view.getInt16(this.pos, true)\n this.pos += 2\n return v\n }\n readUInt16(): number {\n const v = this.view.getUint16(this.pos, true)\n this.pos += 2\n return v\n }\n /** 64-bit little-endian IEEE-754 double (used by code-data number / date-time literals). */\n readFloat64(): number {\n const v = this.view.getFloat64(this.pos, true)\n this.pos += 8\n return v\n }\n readByte(): number {\n return this.bytes[this.pos++]\n }\n /** Read `n` bytes as a fresh copy (C# ReadBytes returns a new array — callers may keep it). */\n readBytes(n: number): Uint8Array {\n const v = this.bytes.slice(this.pos, this.pos + n)\n this.pos += n\n return v\n }\n skip(n: number): void {\n this.pos += n\n }\n\n // ── strings ─────────────────────────────────────────────────────────────────────────────────────\n /** Read `length` bytes, truncate at the first NUL, then decode (C# ReadStringWithFixedLength). */\n readStringWithFixedLength(length: number, dec: TextDecoder = this.dec): string {\n const start = this.pos\n this.pos += length\n let end = start\n const stop = start + length\n while (end < stop && this.bytes[end] !== 0) end++\n return dec.decode(this.bytes.subarray(start, end))\n }\n /** int32 length prefix, then a fixed-length string of that many bytes. */\n readStringWithLengthPrefix(dec: TextDecoder = this.dec): string {\n return this.readStringWithFixedLength(this.readInt32(), dec)\n }\n /**\n * BStr: int32 length prefix INCLUDING the trailing NUL. length 0 ⇒ null; otherwise decode (length-1) bytes\n * and consume the NUL. (C# ReadBStr.)\n */\n readBStr(dec: TextDecoder = this.dec): string | null {\n const length = this.readInt32()\n if (length === 0) return null\n const s = dec.decode(this.readBytes(length - 1))\n this.readByte()\n return s\n }\n /** NUL-terminated string (does not depend on a length prefix). */\n readCStyleString(dec: TextDecoder = this.dec): string {\n const start = this.pos\n while (this.pos < this.bytes.length && this.bytes[this.pos] !== 0) this.pos++\n const s = dec.decode(this.bytes.subarray(start, this.pos))\n if (this.pos < this.bytes.length) this.pos++ // skip the NUL\n return s\n }\n\n // ── length-prefixed bytes / count prefixes ──────────────────────────────────────────────────────\n readBytesWithLengthPrefix(): Uint8Array {\n return this.readBytes(this.readInt32())\n }\n /** MFC CString-style count: a uint16, or, when 0xFFFF, a following int32 (C# ReadMfcStyleCountPrefix). */\n readMfcStyleCountPrefix(): number {\n const c16 = this.readUInt16()\n if (c16 !== 0xffff) return c16\n return this.readInt32()\n }\n readStringsWithMfcStyleCountPrefix(dec: TextDecoder = this.dec): string[] {\n const count = this.readMfcStyleCountPrefix()\n const result: string[] = []\n for (let i = 0; i < count; i++) result.push(this.readStringWithLengthPrefix(dec))\n return result\n }\n\n // ── int arrays ──────────────────────────────────────────────────────────────────────────────────\n readInt32sWithFixedLength(count: number): number[] {\n const result: number[] = []\n for (let i = 0; i < count; i++) result.push(this.readInt32())\n return result\n }\n readInt32sWithLengthPrefix(): number[] {\n return this.readInt32sWithFixedLength(this.readInt32())\n }\n /** int32 BYTE-size prefix (divided by 4 to get the element count). */\n readInt32sWithByteSizePrefix(): number[] {\n return this.readInt32sWithFixedLength(Math.trunc(this.readInt32() / 4))\n }\n readInt16sWithFixedLength(count: number): number[] {\n const result: number[] = []\n for (let i = 0; i < count; i++) result.push(this.readInt16())\n return result\n }\n readInt16sWithLengthPrefix(): number[] {\n return this.readInt16sWithFixedLength(this.readInt32())\n }\n /** int32 BYTE-size prefix (divided by 2 to get the element count). */\n readInt16sWithByteSizePrefix(): number[] {\n return this.readInt16sWithFixedLength(Math.trunc(this.readInt32() / 2))\n }\n\n // ── block tables ────────────────────────────────────────────────────────────────────────────────\n /**\n * Block table addressed by per-element OFFSET (C# ReadBlocksWithIdAndOffest):\n * int32 count, int32 size, int32[count] ids, int32[count] offsets, then each block at\n * (start + offset) prefixed by its own int32 length. The cursor lands at start+size afterwards.\n */\n readBlocksWithIdAndOffest<T>(readFn: (r: BinaryReader, id: number, length: number) => T): T[] {\n const count = this.readInt32()\n const size = this.readInt32()\n const endPos = this.pos + size\n const ids = this.readInt32sWithFixedLength(count)\n const offsets = this.readInt32sWithFixedLength(count)\n const startPos = this.pos\n const result: T[] = []\n for (let i = 0; i < count; i++) {\n this.pos = startPos + offsets[i]\n const length = this.readInt32()\n result.push(readFn(this, ids[i], length))\n }\n this.pos = endPos\n return result\n }\n /**\n * Block table addressed by in-line MEMORY ADDRESS (C# ReadBlocksWithIdAndMemoryAddress):\n * int32 headerSize, count = headerSize/8, int32[count] ids, int32[count] memoryAddresses, then each\n * block read sequentially. Unlike the offset variant there is no per-block length / seeking.\n */\n readBlocksWithIdAndMemoryAddress<T>(\n readFn: (r: BinaryReader, id: number, memoryAddress: number) => T,\n ): T[] {\n const headerSize = this.readInt32()\n const count = Math.trunc(headerSize / 8)\n const ids = this.readInt32sWithFixedLength(count)\n const memoryAddresses = this.readInt32sWithFixedLength(count)\n const result: T[] = []\n for (let i = 0; i < count; i++) result.push(readFn(this, ids[i], memoryAddresses[i]))\n return result\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encoding.js","names":[],"sources":["../../../../src/lib/epl/io/encoding.ts"],"sourcesContent":["/**\n * Text decoders for the e-language (易语言) project parser.\n *\n * The whole `.e`/`.ec` format defaults to GBK (codepage 936). The system-info section may select another\n * codepage via its `Language` field (see {@link decoderForLanguage}); section parsing then uses that decoder\n * (falling back to GBK). We only ever DECODE (read) — never encode — so the native `TextDecoder` covers it.\n */\n\nconst cache = new Map<string, TextDecoder>()\n\n/** A cached non-fatal {@link TextDecoder} for a WHATWG label, falling back to GBK for unknown labels. */\nexport function decoderFor(label: string): TextDecoder {\n let d = cache.get(label)\n if (!d) {\n try {\n d = new TextDecoder(label)\n } catch {\n d = new TextDecoder('gbk')\n }\n cache.set(label, d)\n }\n return d\n}\n\n/** The default GBK decoder (codepage 936) used for段名 and most string fields. */\nexport function gbk(): TextDecoder {\n return decoderFor('gbk')\n}\n\n/**\n * Decoder for ESystemInfoSection.Language: 1=中文(GBK), 2=英语(ASCII), 3=中文(BIG5), 4=日文(SJIS).\n * ASCII is served by windows-1252 (a superset that decodes 0x00–0x7F identically); anything else → GBK.\n */\nexport function decoderForLanguage(lang: number): TextDecoder {\n switch (lang) {\n case 2:\n return decoderFor('windows-1252')\n case 3:\n return decoderFor('big5')\n case 4:\n return decoderFor('shift_jis')\n default:\n return decoderFor('gbk')\n }\n}\n"],"mappings":";AAQA,IAAM,oBAAQ,IAAI,IAAyB;AAG3C,SAAgB,EAAW,GAA4B;CACrD,IAAI,IAAI,EAAM,IAAI,CAAK;CACvB,IAAI,CAAC,GAAG;EACN,IAAI;GACF,IAAI,IAAI,YAAY,CAAK;EAC3B,QAAQ;GACN,IAAI,IAAI,YAAY,KAAK;EAC3B;EACA,EAAM,IAAI,GAAO,CAAC;CACpB;CACA,OAAO;AACT;AAGA,SAAgB,IAAmB;CACjC,OAAO,EAAW,KAAK;AACzB;AAMA,SAAgB,EAAmB,GAA2B;CAC5D,QAAQ,GAAR;EACE,KAAK,GACH,OAAO,EAAW,cAAc;EAClC,KAAK,GACH,OAAO,EAAW,MAAM;EAC1B,KAAK,GACH,OAAO,EAAW,WAAW;EAC/B,SACE,OAAO,EAAW,KAAK;CAC3B;AACF"}
|
|
@@ -14,3 +14,5 @@ function F(e) {
|
|
|
14
14
|
}
|
|
15
15
|
//#endregion
|
|
16
16
|
export { b as DATATYPE_ANY, A as DATATYPE_BIN, D as DATATYPE_BOOL, x as DATATYPE_BYTE, O as DATATYPE_DATETIME, E as DATATYPE_DOUBLE, T as DATATYPE_FLOAT, C as DATATYPE_INT, M as DATATYPE_LAMBDA, w as DATATYPE_LONG, j as DATATYPE_METHODPTR, S as DATATYPE_SHORT, k as DATATYPE_STRING, y as ID_NAV, e as MASK_NUM, m as TYPE_CLASS, a as TYPE_CLASS_MEMBER, o as TYPE_CONSTANT, i as TYPE_DLL, p as TYPE_DLL_PARAMETER, h as TYPE_FORM, s as TYPE_FORM_CLASS, _ as TYPE_FORM_CONTROL, v as TYPE_FORM_MENU, g as TYPE_FORM_SELF, n as TYPE_GLOBAL, l as TYPE_IMAGE_RESOURCE, c as TYPE_LOCAL, t as TYPE_METHOD, d as TYPE_SOUND_RESOURCE, r as TYPE_STATIC_CLASS, f as TYPE_STRUCT, u as TYPE_STRUCT_MEMBER, F as decomposeLibDataTypeId, N as getType, P as isLibDataType };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=eplSystemId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eplSystemId.js","names":[],"sources":["../../../../src/lib/epl/model/eplSystemId.ts"],"sourcesContent":["/**\n * E-language system Id constants + helpers — port of `EplSystemId.cs`.\n *\n * Every reference in a `.e` project (variable, method, constant, class, form control, …) is a 32-bit Id:\n * high 8 bits = TYPE (see `TYPE_*`), low 24 bits = number. Built-in data types use a separate `DATATYPE_*`\n * space. All bit ops below run on int32 in JS exactly as C# `int` does, so the masks/shifts port verbatim.\n *\n * Note: `readInt32()` yields SIGNED int32, so the `DATATYPE_*` constants (high bit set) are written as\n * `0x8000xxxx | 0` to land on the same negative int32 the file holds — letting `===` comparisons work.\n */\n\nexport const MASK_TYPE = 0xff000000 | 0 // -16777216 as int32\nexport const MASK_NUM = 0x00ffffff\n\nexport const TYPE_METHOD = 0x04000000\nexport const TYPE_GLOBAL = 0x05000000\n/** 普通程序集(非窗口程序集、类模块) */\nexport const TYPE_STATIC_CLASS = 0x09000000\nexport const TYPE_DLL = 0x0a000000\n/** 程序集变量 或 类模块成员 */\nexport const TYPE_CLASS_MEMBER = 0x15000000\nexport const TYPE_CONSTANT = 0x18000000\n/** 窗口程序集 */\nexport const TYPE_FORM_CLASS = 0x19000000\n/** 局部变量 或 子程序参数(非 DLL 命令) */\nexport const TYPE_LOCAL = 0x25000000\nexport const TYPE_IMAGE_RESOURCE = 0x28000000\nexport const TYPE_STRUCT_MEMBER = 0x35000000\nexport const TYPE_SOUND_RESOURCE = 0x38000000\nexport const TYPE_STRUCT = 0x41000000\nexport const TYPE_DLL_PARAMETER = 0x45000000\nexport const TYPE_CLASS = 0x49000000\nexport const TYPE_FORM = 0x52000000\nexport const TYPE_FORM_SELF = 0x06000000\nexport const TYPE_FORM_CONTROL = 0x16000000\nexport const TYPE_FORM_MENU = 0x26000000\n\n/** Not-a-Variable sentinel (used by AccessArray/AccessMember expressions). */\nexport const ID_NAV = 0x0500fffe\n\n/** 无类型(如某子程序无返回值) */\nexport const DATATYPE_VOID = 0x00000000\n/** 通用型 */\nexport const DATATYPE_ANY = 0x80000000 | 0\nexport const DATATYPE_BYTE = 0x80000101 | 0\nexport const DATATYPE_SHORT = 0x80000201 | 0\nexport const DATATYPE_INT = 0x80000301 | 0\nexport const DATATYPE_LONG = 0x80000401 | 0\nexport const DATATYPE_FLOAT = 0x80000501 | 0\nexport const DATATYPE_DOUBLE = 0x80000601 | 0\nexport const DATATYPE_BOOL = 0x80000002 | 0\nexport const DATATYPE_DATETIME = 0x80000003 | 0\nexport const DATATYPE_STRING = 0x80000004 | 0\nexport const DATATYPE_BIN = 0x80000005 | 0\nexport const DATATYPE_METHODPTR = 0x80000006 | 0\n/** 条件语句型(SDT_STATMENT),通常只用于支持库命令的参数 */\nexport const DATATYPE_LAMBDA = 0x80000008 | 0\n\n/** Type part of a user-defined Id (`id & 0xFF000000`). */\nexport function getType(id: number): number {\n return id & 0xff000000\n}\n\n/** Is this a library-defined data type Id (high byte 0, and not Void)? */\nexport function isLibDataType(id: number): boolean {\n return (id & 0xff000000) === 0 && id !== DATATYPE_VOID\n}\n\n/** Compose a library data-type Id from 0-based lib + type indices. */\nexport function makeLibDataTypeId(lib: number, type: number): number {\n return ((lib + 1) << 16) | (type + 1)\n}\n\n/** Decompose a library data-type Id back to 0-based lib + type indices (C# uses sign-extended shorts). */\nexport function decomposeLibDataTypeId(id: number): { lib: number; type: number } {\n const lib = (((id >> 16) << 16) >> 16) - 1 // (short)(id >> 16) - 1\n const type = ((id << 16) >> 16) - 1 // (short)id - 1\n return { lib, type }\n}\n"],"mappings":";AAYA,IAAa,IAAW,UAEX,IAAc,UACd,IAAc,UAEd,IAAoB,WACpB,IAAW,WAEX,IAAoB,WACpB,IAAgB,WAEhB,IAAkB,WAElB,IAAa,WACb,IAAsB,WACtB,IAAqB,WACrB,IAAsB,WACtB,IAAc,YACd,IAAqB,YACrB,IAAa,YACb,IAAY,YACZ,IAAiB,WACjB,IAAoB,WACpB,IAAiB,WAGjB,IAAS,UAKT,IAAe,aACf,IAAgB,aAChB,IAAiB,aACjB,IAAe,aACf,IAAgB,aAChB,IAAiB,aACjB,IAAkB,aAClB,IAAgB,aAChB,IAAoB,aACpB,IAAkB,aAClB,IAAe,aACf,IAAqB,aAErB,IAAkB;AAG/B,SAAgB,EAAQ,GAAoB;CAC1C,OAAO,IAAK;AACd;AAGA,SAAgB,EAAc,GAAqB;CACjD,QAAQ,IAAK,eAAgB,KAAK,MAAA;AACpC;AAQA,SAAgB,EAAuB,GAA2C;CAGhF,OAAO;EAAE,MAFM,KAAM,MAAO,MAAO,MAAM;EAE3B,OADC,KAAM,MAAO,MAAM;CACf;AACrB"}
|