import { window , commands , Uri , ViewColumn , version , workspace , TextDocument } from "vscode" ; import * as path from "path" ; import { TextEditorHelper } from "./textEditorHelper" ; import { PreviewDocumentContentProvider } from "../previewDocumentContentProvider" ; export enum SourceType { BODY , BR , COMMENT , DIVISION , DOCTYPE , HEAD , HR , HTML , IMAGE , LINK , SCRIPT , STYLE , CUSTOM_MERMAID_SAMPLE , CUSTOM_STYLE_SAMPLE , CUSTOM_NEWLINE } export class HtmlPreview { private static COMMAND_TOGGLE_PREVIEW = "vscode.previewHtml" ; private static HTTP_S_REGREX_PREFFIX = /http[s]{0,1}:\/\// ; public static sendPreviewCommand ( previewUri , displayColumn ) { if ( TextEditorHelper . versionCompare ( version , "1.23.0" ) < 0 ) { return HtmlPreview . sendPreviewCommand_1_23_0_BELOW ( previewUri , displayColumn ) ; } return HtmlPreview . sendPreviewCommand_1_23_0 ( previewUri , displayColumn ) ; } public static sendPreviewCommand_1_23_0_BELOW ( previewUri , displayColumn ) { return commands . executeCommand ( this . COMMAND_TOGGLE_PREVIEW , previewUri , displayColumn ) . then ( ( ) => { return ; } , ( reason ) => { console . warn ( reason ) ; window . showErrorMessage ( reason ) ; } ) ; } public static sendPreviewCommand_1_23_0 ( previewUri , displayColumn ) { if ( ! window [ 'createWebviewPanel' ] ) { return Promise . resolve ( ) ; } return workspace . openTextDocument ( previewUri ) . then ( ( doc ) => { const panel = window [ 'createWebviewPanel' ] ( previewUri . toString ( ) , PreviewDocumentContentProvider . getPreviewTitle ( doc . fileName ) , displayColumn , { enableScripts : true } ) ; panel . webview . html = doc . getText ( ) ; } , ( reason ) => { console . warn ( reason ) ; window . showErrorMessage ( reason ) ; } ) ; } public static createFullHtmlSnippetFrom ( headPayLoad ? , bodyPayLoad ? ) { return this . createRemoteSource ( SourceType . DOCTYPE , this . createRemoteSource ( SourceType . HTML , this . createRemoteSource ( SourceType . HEAD , headPayLoad ) + this . createRemoteSource ( SourceType . CUSTOM_NEWLINE , undefined ) + this . createRemoteSource ( SourceType . BODY , bodyPayLoad ) ) ) ; } public static errorSnippet ( error ) { return this . createFullHtmlSnippetFrom ( undefined , this . createRemoteSource ( SourceType . DIVISION , error ) ) ; } private static isWithPayLoad ( payLoad ? ) { if ( ! ! payLoad && payLoad . length > 0 ) { return true ; } return false ; } public static createRemoteSourceAtNewline ( type , payLoad ? ) { return HtmlPreview . createRemoteSource ( SourceType . CUSTOM_NEWLINE , HtmlPreview . createRemoteSource ( type , payLoad ) ) ; } public static createRemoteSource ( type , payLoad ? ) { if ( ! payLoad ) { return "" ; } switch ( type ) { case SourceType . BODY : return this . createRemoteSourceOfBODY ( payLoad ) ; case SourceType . BR : return this . createRemoteSourceOfBR ( payLoad ) ; case SourceType . COMMENT : return this . createRemoteSourceOfCOMMENT ( payLoad ) ; case SourceType . DIVISION : return this . createRemoteSourceOfDIVISION ( payLoad ) ; case SourceType . DOCTYPE : return this . createRemoteSourceOfDOCTYPE ( payLoad ) ; case SourceType . HEAD : return this . createRemoteSourceOfHEAD ( payLoad ) ; case SourceType . HR : return this . createRemoteSourceOfHR ( payLoad ) ; case SourceType . HTML : return this . createRemoteSourceOfHTML ( payLoad ) ; case SourceType . IMAGE : return this . createRemoteSourceOfIMAGE ( payLoad ) ; case SourceType . LINK : return this . createRemoteSourceOfLINK ( payLoad ) ; case SourceType . SCRIPT : return this . createRemoteSourceOfSCRIPT ( payLoad ) ; case SourceType . STYLE : return this . createRemoteSourceOfSTYLE ( payLoad ) ; case SourceType . CUSTOM_NEWLINE : return this . createRemoteSourceOfCUSTOM_NEWLINE ( payLoad ) ; case SourceType . CUSTOM_MERMAID_SAMPLE : return this . createRemoteSourceOfCUSTOM_MERMAID_SAMPLE ( payLoad ) ; case SourceType . CUSTOM_STYLE_SAMPLE : return this . createRemoteSourceOfCUSTOM_STYLE_SAMPLE ( payLoad ) ; } } public static createLocalSource ( type , fileName ) { const fileUrl = require ( 'file-url' ) ; const source_path = fileUrl ( path . join ( __dirname , ".." , ".." , ".." , "static" , fileName ) ) ; return this . createRemoteSource ( type , source_path ) ; } public static fixNoneNetLinks ( document , _documentPath ? ) { if ( ! document ) { return document ; } return document . replace ( new RegExp ( "((?:src|href)=['\"])(.*?)(['\"])" , "gmi" ) , ( _subString , p1 , p2 , p3 ) : string => { p2 = HtmlPreview . getExtensionPath ( p2 ) ; return [ p1 . trim ( ) , p2 . trim ( ) , p3 . trim ( ) ] . join ( "" ) ; } ) ; } public static fixImageSrcLinks ( imageSnippet ) { return HtmlPreview . fixNoneNetLinks ( imageSnippet , "" ) ; } public static async fixImageRedirectUrl ( srcUrl ) { if ( ! srcUrl ) { return "" ; } const result = HtmlPreview . HTTP_S_REGREX_PREFFIX . exec ( srcUrl ) ; if ( result == null ) { return "" ; } try { return await HtmlPreview . getRedirectUrl ( srcUrl ) ; } catch ( error ) { return srcUrl ; } } private static getRedirectUrl ( firstUrl ) { return new Promise ( function ( _resolve , _reject ) { const options = { method : 'GET' , url : firstUrl , headers : { 'cache-control' : 'no-cache' } } ; const request = require ( "request" ) ; request . get ( options , ( err , resp , _body ) => { if ( err ) { return ; } firstUrl = resp . request . uri . href ; } ) ; return firstUrl ; } ) ; } private static createRemoteSourceOfBODY ( payLoad ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `` ; } return `<body>
                    ${ payLoad }
                </body>` ; } private static createRemoteSourceOfBR ( payLoad ? ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `<br>` ; } return `<br>
                ${ payLoad }` ; } private static createRemoteSourceOfCOMMENT ( payLoad ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `` ; } return `<!-- ${ payLoad } -->` ; } private static createRemoteSourceOfDIVISION ( payLoad ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `` ; } return `<div>${ payLoad }</div>` ; } private static createRemoteSourceOfDOCTYPE ( payLoad ? ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `<!DOCTYPE html>` ; } return `<!DOCTYPE html>
                ${ payLoad }` ; } private static createRemoteSourceOfHEAD ( payLoad ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `` ; } return `<head>
                    ${ payLoad }
                </head>` ; } private static createRemoteSourceOfHR ( payLoad ? ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `<hr>` ; } return `<hr>
                ${ payLoad }` ; } private static createRemoteSourceOfHTML ( payLoad ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `` ; } return `<html>
                    ${ payLoad }
                </html>` ; } private static createRemoteSourceOfIMAGE ( payLoad ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `` ; } return `<img src="${ payLoad }"/>` ; } private static createRemoteSourceOfLINK ( payLoad ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `` ; } return `<link href="${ payLoad }" rel="stylesheet" />` ; } private static createRemoteSourceOfSCRIPT ( payLoad ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `` ; } return `<script src="${ payLoad }"></script>` ; } private static createRemoteSourceOfSTYLE ( payLoad ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `` ; } return `<style type="text/css">
                    ${ payLoad }
                </style>` ; } private static createRemoteSourceOfCUSTOM_NEWLINE ( payLoad ? ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `\n` ; } return `\n${ payLoad }` ; } private static createRemoteSourceOfCUSTOM_MERMAID_SAMPLE ( payLoad ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `` ; } const head = `<script src="${ this . getExtensionPath ( ) }/node_modules/mermaid/dist/mermaid.min.js" type="text/javascript">
                        mermaid.initialize({startOnLoad: true, theme: 'forest'});
                    </script>` ; const body = `
                    <hr>
                    <div class="mermaid">
                        ${ payLoad }
                    </div>` ; return HtmlPreview . createFullHtmlSnippetFrom ( head , body ) ; } private static getExtensionPath ( ... paths ) { if ( TextEditorHelper . versionCompare ( version , "1.23.0" ) < 0 ) { return HtmlPreview . getExtensionPath_1_23_0_BELOW ( ... paths ) ; } return HtmlPreview . getExtensionPath_1_23_0 ( ... paths ) ; } private static getExtensionPath_1_23_0_BELOW ( ... paths ) { if ( ! ! paths && paths . length > 0 ) { const p = Uri . parse ( paths [ 0 ] ) ; if ( p . scheme != "file" || paths [ 0 ] . startsWith ( "/" ) ) { return paths . join ( "/" ) ; } const fs = paths [ 0 ] . replace ( /^file:\/\// , "" ) ; if ( fs . startsWith ( "/" ) ) { return paths . join ( "/" ) ; } if ( paths [ 0 ] . startsWith ( "file://" ) ) { paths [ 0 ] = "file://" + path . normalize ( path . join ( __dirname , ".." , ".." , fs ) ) ; return paths . join ( "/" ) ; } } return path . normalize ( path . join ( __dirname , ".." , ".." , ... paths ) ) ; } private static getExtensionPath_1_23_0 ( ... paths ) { const onDiskPath_1_23_0_BELOW = HtmlPreview . getExtensionPath_1_23_0_BELOW ( ... paths ) ; const onDiskPath = Uri . parse ( onDiskPath_1_23_0_BELOW ) ; if ( ! onDiskPath [ 'with' ] ) { return onDiskPath_1_23_0_BELOW ; } if ( onDiskPath . scheme !== "file" ) { return onDiskPath_1_23_0_BELOW ; } return onDiskPath . with ( { scheme : 'vscode-resource' } ) . toString ( ) ; } private static createRemoteSourceOfCUSTOM_STYLE_SAMPLE ( payLoad ) { if ( ! this . isWithPayLoad ( payLoad ) ) { return `` ; } const head = HtmlPreview . createRemoteSource ( SourceType . STYLE , `#css_property {
                ${ payLoad }
            }` ) ; const body = `<div>Preview of the CSS properties
                        {
                            ${ payLoad }
                        }
                    </div>
                    <hr>
                    <div id="css_property">Hello World</div>` ; return HtmlPreview . createFullHtmlSnippetFrom ( head , body ) ; } }