The cause of

Recently, I have been copying an excellent writing software, which supports the parsing of Markdown. Naturally, it is an essential function. While thinking about how to implement the parser, I happened to read the technical article about “code editor” produced by Zhong Ying. Found that you could use webViews to wrap Web projects such as MarkDown-it and Highlightjs.org to implement a CommonMark parser with good results at a low cost.

The implementation process

1. Create an HTML file

Create the local file index.html and enter the code. The JS files and resources used in the page need to be created and generated later. The unique

in the tag will be used for subsequent insertion of code rendered through Markdown-it, and the style layout of the page is implemented and optimized through CSS files.
<html>
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="Width = device - width, initial - scale = 1.0, the maximum - scale = 1.0, user - scalable = no" />
        <link rel="stylesheet" href="./main.css" />
        <script src="./main.js"></script>
    </head>
    <body>
        <div class="container" id="contents"></div>
    </body>
</html>
Copy the code

2. Create the JS file

Create a local file named index.js and write the JAVASCRIPT code that needs to be injected into the web page.

import hljs from 'highlight.js'
import MarkdownIt from 'markdown-it'
import emoji from 'markdown-it-emoji'
import '. /.. /css/bootstrap.css'
import '. /.. /css/gist.css'
import '. /.. /css/github.css'
import '. /.. /css/index.css'

window.showMarkdown = (percentEncodedMarkdown, enableImage = true) = > {

  if(! percentEncodedMarkdown) {return
  }

  const markdownText = decodeURIComponent(percentEncodedMarkdown)

  let markdown = new MarkdownIt({
    html: true.breaks: true.linkify: true.highlight: function(code){
        returnhljs.highlightAuto(code).value; }})if(! enableImage) { markdown = markdown.disable('image')
  }
  markdown.use(emoji)
  markdown.use(require('markdown-it-latex2img'))
  let html = markdown.render(markdownText)

  document.getElementById('contents').innerHTML = html
  let tables = document.querySelectorAll('table')
  tables.forEach((table) = > {
    table.classList.add('table')})let codes = document.querySelectorAll('pre code')
  codes.forEach((code) = > {
    hljs.highlightBlock(code)
  })

}
Copy the code

The main JS libraries used are:

Markdown-it: 100% CommonMark syntax parsing highlight.js: code highlighting Markdown-it-emoji: Emoji syntax support Markdown-it-latex2IMG: server-side MathJax parser

The basic logic of the code is mainly as follows: 1. Because the markdown format of the content is passed before the encoding operation, so here need to call decodeURIComponent() function to decode the content first. 2. Use Markdown-it to render the content with the required plug-ins. For more information about the use of Markdown-it and plug-in support, please refer to the official API documentation: Markdown-it Chinese Documentation. 3. Insert rendered code into the corresponding position of the index.html page, and support table (inline function) and code highlighting.

3. Control encapsulation

Business logic: create WKWebView and inject JS code (showMarkdown() function call)

  @objc public func load(markdown: String? .enableImage: Bool = true) {
    guard let markdown = markdown else { return }

    if let url = htmlURL {
      let templateRequest = URLRequest(url: url)

      let escapedMarkdown = self.escape(markdown: markdown) ?? ""
      let imageOption = enableImage ? "true" : "false"
      let script = "window.showMarkdown('\(escapedMarkdown)', \(imageOption));"
      let userScript = WKUserScript(source: script, injectionTime: .atDocumentEnd, forMainFrameOnly: true)

      let controller = WKUserContentController()
      controller.addUserScript(userScript)

      let configuration = WKWebViewConfiguration()
      configuration.userContentController = controller

      let wv = WKWebView(frame: self.bounds, configuration: configuration)
      wv.scrollView.isScrollEnabled = self.isScrollEnabled
      wv.translatesAutoresizingMaskIntoConstraints = false
      wv.navigationDelegate = self
      addSubview(wv)
    
      // Code ellipsis: layout constraints, spatial styles
      // ...

      wv.load(templateRequest)
    } else {
      // TODO: raise error}}private func escape(markdown: String) -> String? {
    return markdown.addingPercentEncoding(withAllowedCharacters: CharacterSet.alphanumerics)
  }
Copy the code

4. Pack resource files

At this point, the work at the code level is done. Finally, we need to use the Webpack tool to package the JS application into a handy static resource, output a file named main.js, and place it in the same directory as the index.html file. Here is a configuration file for reference: webpack.config.js.

Write in the last

In order to support LaTeX parsing, I slightly rewrote the showMarkdown function to include the markdown-it-latex2img library, which can display mathematical formulas as pictures. In the process, also learned the use of Webpack tools and JS static resource packaging. Webview project encapsulation can provide more rich functions for native, can provide more ideas and extensions for complex business implementation, I hope this article can help people in need.

References:

Taio Development Notes Webpack tutorial Markdown – IT API documentation Keita Ouchi /MarkdownView