# aardio 范例: WebView2 - ACE(Markdown)编辑器

``````aardio
import string.markdown;
import web.form.simpleMarkdown;
import fonts.fontAwesome;
import win.ui;
/*DSG{{*/
var mainForm = win.form(text="WebView2 - ACE(Markdown)编辑器";right=966;bottom=622)
mainForm.add(
btnClear={cls="plus";text="清空";left=122;top=586;right=202;bottom=616;align="left";color=0x3C3C3C;db=1;dl=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF26B';notify=1;textPadding={left=25};z=3};
btnPaster={cls="plus";text="粘贴";left=190;top=586;right=270;bottom=616;align="left";color=0x3C3C3C;db=1;dl=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF26B';notify=1;textPadding={left=25};z=4};
btnPreview={cls="plus";text="预览";left=841;top=586;right=921;bottom=616;align="left";color=0x3C3C3C;db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF26B';notify=1;textPadding={left=25};z=2};
btnPrint={cls="plus";text="打印";left=752;top=586;right=832;bottom=616;align="left";color=0x3C3C3C;db=1;dr=1;font=LOGFONT(h=-13);iconStyle={align="left";font=LOGFONT(h=-13;name='FontAwesome');padding={left=8}};iconText='\uF26B';notify=1;textPadding={left=25};z=5};
custom={cls="custom";text="自定义控件";left=0;top=0;right=967;bottom=581;db=1;dl=1;dr=1;dt=1;z=1}
)
/*}}*/

import web.view;
var wb = web.view(mainForm.custom);

var isMarkdown = function(text){
    if(!#text) return false;
    
    var patterns = {
        "^\s*\#+\s+"; // 标题
        "^\s*[\*\-\+]\s+"; // 无序列表
        "^\s*\d+\.\s+"; // 有序列表
        "\<```\>|\<`\>"; // 代码块或行内代码
        "\[.+\]\(.+\)"; // 链接
        "\*\*.+\*\*"; // 加粗
        "^\s*\>\s+"; // 引用
        "^\s*\|.+\|"; // 表格
    };
    
    for(i=1;#patterns;1){
        if(string.match(text,patterns[i])) return true;
    }
    
    return false;
}

import win.clip;
var clipText = win.clip.read();

if( isMarkdown(clipText) ){
   wb.markdown = clipText
}
else{
   wb.markdown = "> 在剪贴板未检测到 Markdown 格式文本，请手动输入" 	
}

wb.html = /********
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Ace Editor 示例</title>
<style type="text/css" media="screen">
    #editor {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
    }
</style>
<!-- 引入 Ace Editor -->
<script src="https://registry.npmmirror.com/ace-builds/1.43.6/files/src-min-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
<!-- 引入主题文件 -->
<script src="https://registry.npmmirror.com/ace-builds/1.43.6/files/src-min-noconflict/theme-github.js"></script>
<!-- 引入语言模式文件 -->
<script src="https://registry.npmmirror.com/ace-builds/1.43.6/files/src-min-noconflict/mode-markdown.js"></script>
<script>

//在网页加载后执行代码
document.addEventListener('DOMContentLoaded', function() {
    window.editor = ace.edit("editor");
    editor.setTheme("ace/theme/chrome");
    editor.session.setMode("ace/mode/markdown");
 	editor.setTheme("ace/theme/github");
})
</script>
</head>
<body id="editor">
<? =owner.markdown ?>
</body>

</html>
********/

import string.html;
var markdownToHTML = function(md){

	md = ..string.replaceUnmatched(md 
		,"<$[""'`$]?![^$]+$>|<$$[^$]+$$>|<%<\\\(><\\\)>>|<%<\\\[><\\]>>"
		,function(text){
			var t,e = ..string.match(text,"^(<<\\\[>|<\\\(>|<$$>|$>)(.+)<<\\\]>|<\\\)>|<$$>|$>$");
			if(#e){
				var displayMode = t=='[' || t =='$$'; 
				if(displayMode) return `<div class="katex">` + ..string.html.escape(e) + `</div>` 
				return `<span class="katex">` + ..string.html.escape(e) + `</span>`
			}
		
		}
		,"!\N([ \t]*)(```+)<\a\w+>?(.+?)!\N\s*\2![^`\S]"
		,"%``"
		,"\<script!\W[^\>]*\>.*?\<\/script\>"
		,`(["'])\s*javascript\:.+?\1`
	);

	var html = /***
	<!DOCTYPE html>
	<html>
	<head>
	<meta charset="utf-8">
	<meta http-equiv='content-language' content='zh-cn'>
	<link rel="stylesheet" href="https://registry.npmmirror.com/github-markdown-css/5.9.0/files/github-markdown.css">
	<link rel="stylesheet" href="https://lib.baomitu.com/KaTeX/latest/katex.min.css">
	<script src="https://lib.baomitu.com/KaTeX/latest/katex.min.js"></script>
	
	<link rel="stylesheet" href="https://lib.baomitu.com/prism/1.30.0/themes/prism.min.css">
	<script src="https://lib.baomitu.com/prism/1.30.0/prism.js"></script>
	<script src="https://lib.baomitu.com/prism/1.30.0/components/prism-javascript.min.js"></script>
	<script src="https://lib.baomitu.com/prism/1.30.0/components/prism-python.min.js"></script>
	<script src="https://lib.baomitu.com/prism/1.30.0/components/prism-go.min.js"></script>
	<script src="https://lib.baomitu.com/prism/1.30.0/components/prism-c.min.js"></script>
	<script src="https://lib.baomitu.com/prism/1.30.0/components/prism-markup.min.js"></script>
	<script>
	
	document.addEventListener('DOMContentLoaded', function () {
    Prism.languages.aardio = Prism.languages.extend('clike', {
        'class-name': [
            Prism.languages.clike['class-name']
        ],
        'keyword': {
            pattern: /(^|[^.])\b(?:begin|end|if|lambda|λ|else|elseif|class|function|return|while|do|namespace|select|case|catch|try|for|in|this|global|self|owner|var|def|null|and|not|or|break|continue|import|with|ctor|eval|type|assert|assert2|assertf|error|rget|callex|errput|loadcode|dumpcode|collectgarbage|call|invoke|tostring|topointer|tonumber|sleep|execute|setlocale|setprivilege|loadcodex|reduce|switch)\b/,
            lookbehind: true
        },
        'string': {
            pattern: /"(?:[^"]|"")*"|`(?:[^`]|``)*`|'(?:[^'\\]|\\.)*'/,
            greedy: true
        },
        'boolean': /\b(?:true|false)\b/,
        'function': /\b[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*\()/,
        'number': /\b0x[a-fA-F_\d]+(?:\.[a-fA-F_\d]*)?(?:p[+-]?\d[_\d]*)?\b|\b\d+#[\d_]+\b|\b\d[_\d]*(?:\.\B|(?:\.[_\d]*)?(?:e[+-]?\d[_\d]*)?\b)|\B\.\d[_\d]*(?:e[+-]?\d[_\d]*)?\b/i,
    
        'operator': /\.\.|--|\+\+|\*\*=?|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|[~:?]/,
        'comment': [
            {
                pattern: /(^|[^\\])\/(\*+)(?:[\s\S]*?[^*])?\2\//,
                lookbehind: true
            },
            {
                pattern: /(^|[^\\:])\/\/.*/,
                lookbehind: true
            }
        ]
    });

    Prism.languages.aardio['class-name'][0].pattern = /(\b(?:class)\s+)[\w.\\]+/;
    Prism.languages.insertBefore('aardio', 'keyword', {
        'constant': /\b_[a-zA-Z]\w*\b/,
    }); 
 
    Prism.highlightAll();  
    
    document.querySelectorAll('div.katex').forEach(el => {
  		 
  		try {
    		katex.render(el.textContent, el, {
      		displayMode: true,
      		throwOnError: false
    		});
  		} catch (err) {
    		console.error('KaTeX rendering error:', err);
  		}
	});
	
	document.querySelectorAll('span.katex').forEach(el => { 
  		try {
    		katex.render(el.textContent, el, {
      		displayMode: false,
      		throwOnError: false
    		});
  		} catch (err) {
    		console.error('KaTeX rendering error:', err);
  		}
	});
    })
	</script>
	</head>
	<body class="markdown-body" style="margin:20px" id="markdown-body">
	***/
	
	
	var markdownRenderer = string.markdown();
	return html ++ markdownRenderer.render(md);
}

mainForm.btnPreview.oncommand = function(id,event){

	var markdown = wb.xcall("editor.getValue")
	
	import web.view;
	var frmPreview = win.form(text="WebView2 - Markdown 预览，支持数学公式、高亮代码块，右键菜单可打印";parent=mainForm;right=966;bottom=622)
	var wb = web.view(frmPreview);
	
	wb.html = markdownToHTML(markdown);
	frmPreview.show();
}

mainForm.btnPrint.oncommand = function(id,event){
	mainForm.btnPrint.disabledText = ['\uF254','\uF251','\uF252','\uF253','\uF250']
	
	//WebView2 加载用户配置
	var userDataDir = ..io.appData("aardio/example/web-view-ace-markdown-print-direct/");
	var preferences,path = web.view.loadPreferences(userDataDir);
	var appState = table.get("printing.print_preview_sticky_settings.appState",preferences)
	
	//获取与修改打印设置
	var printAppState = JSON.tryParse(appState) //不存在会返回 NULL 值
	printAppState = table.assignDeep(printAppState , {
		"version":2,
		"duplexType":"LONG_EDGE",
		"mediaSize":{"height_microns":297000,"width_microns":210000},
		"marginsType":0,
		"customMargins":{"marginTop":72,"marginRight":72,"marginBottom":72,"marginLeft":72},
		"isColorEnabled":true,
		"isHeaderFooterEnabled":false,
		"isLandscapeEnabled":false,
		"isCollateEnabled":true,
		"isCssBackgroundEnabled":false,
		"scaling":"100"
	});
	
	//保存打印机设置
	printAppState = JSON.stringify(printAppState)
	table.set("printing.print_preview_sticky_settings.appState",printAppState,preferences)
	JSON.save(path,preferences,false);//保存，不需要格式化

	var markdown = wb.xcall("editor.getValue")
	
	import web.view;
	var wb = web.view(,{
		startArguments =  ["--kiosk-printing","--use-system-default-printer"];
		userDataDir = userDataDir;
	})
	
	wb.html = markdownToHTML(markdown);
	wb.wait();
	wb.xcall("print");	
	
	thread.delay(5000);
	wb.closeAndWait();
	mainForm.btnPrint.disabledText = null;
}

mainForm.btnClear.oncommand = function(id,event){
	wb.invoke("editor.setValue","")	
}

import win.clip;
mainForm.btnPaster.oncommand = function(id,event){
	var text = win.clip.read();
	
	if(#text) {
		wb.invoke("editor.insert",text)	
	}	
}

var style = {
	button = {
		color={
			active=0xFF00FF00;
			default=0xFF3C3C3C;
			disabled=0xFF6D6D6D;
			hover=0xFFFF0000		
		}
	}
}

mainForm.btnPaster.skin(style.button)
mainForm.btnPrint.skin(style.button)
mainForm.btnPreview.skin(style.button)
mainForm.btnClear.skin(style.button)

mainForm.show();
win.loopMessage();
``````