WMarkDown/Go/WMarkDown.go

1690 lines
48 KiB
Go

package Modules
import (
"bytes"
"encoding/base64"
"encoding/json"
"maps"
"os"
"path/filepath"
"regexp"
"runtime/debug"
"slices"
"strconv"
"strings"
)
type ItemMark struct {
from string
to string
pattern *regexp.Regexp
}
type WMDConstants struct {
// Languages
HTML int
// Analyse modes
RAW int
SUBITEM int
LINKED int
html_special_characters map[string]string
quote_special map[string]string
item_mark ItemMark
}
type PatternMatch struct {
String string
Groups []string
Span []int
Ok bool
}
type WMDModule struct {
mode int
patterns []*regexp.Regexp
pattern_method func(data string, patterns []*regexp.Regexp) *PatternMatch
method func(matches *PatternMatch, language int, path string) string
}
type WMDModulePattern struct {
patterns []string
method func(data string, patterns []*regexp.Regexp) *PatternMatch
}
type WMarkDown struct {
keys []string
modules map[string]*WMDModule
ids_cache []string
}
type WMDAnalyseItem struct {
exists bool
from int
to int
matches *PatternMatch
}
type PatternsModel struct {
RE_BLOCK_HTML *regexp.Regexp
RE_BLOCK_MARK *regexp.Regexp
RE_NEW_LINES *regexp.Regexp
RE_NEW_LINE *regexp.Regexp
RE_STARTED_WHITE_SPACES *regexp.Regexp
RE_LINES *regexp.Regexp
RE_LIST_LINE *regexp.Regexp
RE_TABLE_LINE *regexp.Regexp
RE_TABLE_ITEM *regexp.Regexp
RE_OPTIONS *regexp.Regexp
RE_OPTION *regexp.Regexp
RE_EXTENSION *regexp.Regexp
RE_CHARACTERS_NO_ID *regexp.Regexp
RE_PHONE_NUMBER *regexp.Regexp
RE_EMAIL_ADDRESS *regexp.Regexp
RE_CLASS_ATTRIBUTE *regexp.Regexp
RE_WHITE_SPACES *regexp.Regexp
RE_LINE_MARKS *regexp.Regexp
RE_INTEGER *regexp.Regexp
RE_CODE_DOC *regexp.Regexp
RE_CODE_DOC_ARGUMENTS *regexp.Regexp
RE_CODE_DOC_SUBARGUMENTS *regexp.Regexp
RE_START_WITH_WHITE_SPACES *regexp.Regexp
RE_DOMAIN *regexp.Regexp
RE_PROTOCOL *regexp.Regexp
RE_TRACE_STACK *regexp.Regexp
}
type WMDListLevel struct {
separator int
_type byte
subtype string
}
type ModulePresentationLinkItem struct {
title string
avatars []string
links []string
}
var RE_BLOCK_HTML_PATTERN string = `^[ \t]*<\!\-{2}[ \t]*\[{2}[ \t]*(wmd|markdown)[ \t]*\]{2}[ \t]*\-{2}>`
var WMD WMDConstants = WMDConstants{
// Languages
HTML: 1 << 0,
// Analyse modes
RAW: 0,
SUBITEM: 1 << 0,
LINKED: 1 << 1,
html_special_characters: map[string]string{
"<": "lt",
">": "gt",
"&": "amp",
},
quote_special: map[string]string{
"?": "ask",
"!": "warning",
">": "answer",
"Q": "comment",
"#": "note",
},
item_mark: ItemMark{
from: "###@&&_",
to: "_&&@###",
pattern: regexp.MustCompile(`\#{3}\@\&{2}_([0-9]+)_\&{2}\@\#{3}`),
},
}
var Patterns PatternsModel = PatternsModel{
RE_BLOCK_HTML: regexp.MustCompile(`(?mi)` + RE_BLOCK_HTML_PATTERN),
RE_BLOCK_MARK: regexp.MustCompile(`(?mi)^[ \t]*((" ?){3}|(\' ?){3}|(` + "`" + ` ?){3})[ \t]*(wmd|wmarkdown)|` + RE_BLOCK_HTML_PATTERN),
RE_NEW_LINES: regexp.MustCompile(`[\r\n]+`),
RE_NEW_LINE: regexp.MustCompile(`\n|\r\n|\r`),
RE_STARTED_WHITE_SPACES: regexp.MustCompile(`^[ \t]*`),
RE_LINES: regexp.MustCompile(`(?m)^[^\r\n]+`),
RE_LIST_LINE: regexp.MustCompile(`(?mi)^([ \t]*)(([\-\*#\+]+)|([0-9]+|[a-z]+)\.)?(.*)$`),
RE_TABLE_LINE: regexp.MustCompile(`(?m)^\|([^\r\n"\']+|"([^"\\\\]+|\\\\(.|[\r\n])|[\r\n]+)*"|\'([^\'\\\\]+|\\\\(.|[\r\n])|[\r\n]+)*\')*[\r\n]*`),
RE_TABLE_ITEM: regexp.MustCompile(`(\|+)(([^\|\'\"]+|"([^"\\\\]+|\\\\(.|[\r\n])|[\r\n]+)*"|\'([^\'\\\\]+|\\\\(.|[\r\n])|[\r\n]+)*\')*)`),
RE_OPTIONS: regexp.MustCompile("(?im)^`" + `{3}[ \t]*w(?:md|markdown)-options[ \t]*(?:\n|\r\n|\r)((?:[^\r\n` + "`" + `]+|[\r\n]+|` + "`{1,2}[^`])*)`{3}"),
RE_OPTION: regexp.MustCompile(`(?m)^([^=\r\n]+)=([^\r\n]*)$`),
RE_EXTENSION: regexp.MustCompile(`(?:([^\.\/\\\\]+)\.)?([^\.\/\\\\]+)$`),
RE_CHARACTERS_NO_ID: regexp.MustCompile(`(?i)[^a-z0-9]+`),
RE_PHONE_NUMBER: regexp.MustCompile(`^\+?[0-9 ]{5,22}$`),
RE_EMAIL_ADDRESS: regexp.MustCompile(`^[a-z\.0-9_\-]+@[a-z\.0-9_\-]+\.[a-z0-9]+$`),
RE_CLASS_ATTRIBUTE: regexp.MustCompile(`(?i)([^a-z0-9_\-]|^)(class=")([^"]+)(")`),
RE_WHITE_SPACES: regexp.MustCompile(`[ \t]+`),
RE_LINE_MARKS: regexp.MustCompile(`(?i)[\*#~\-_]+|\[[ \-x]\]|\([ \-x]\)`),
RE_INTEGER: regexp.MustCompile(`^[0-9]+$`),
RE_CODE_DOC: regexp.MustCompile(`^(\[([^\]]+)\][ \t]+)?([\!\?=\&\*]{0,3})(([^\(\[\]\?\!]+)([\:\.]))([^\(\.\:\[\]\=]+)(\(((.+|[\r\n]+)*)\)|[ \t]*=[ \t]*((.+|[\r\n]+)*))?`),
RE_CODE_DOC_ARGUMENTS: regexp.MustCompile(`([\!\?\&\*]{0,2})([^=]+)[ \t]+([^=]+)(=(.+))?`),
RE_CODE_DOC_SUBARGUMENTS: regexp.MustCompile(`<([^<>]+)>|("[^"]*"|\'[^\']*\')`),
RE_START_WITH_WHITE_SPACES: regexp.MustCompile(`(?m)^[ \t]`),
RE_DOMAIN: regexp.MustCompile(`^[^\:]+\:\/{2}[^\/]+`),
RE_PROTOCOL: regexp.MustCompile(`(?i)^([a-z0-9_\-]+)\:`),
RE_TRACE_STACK: regexp.MustCompile(`[\n\r](([^\.\(\s]+|\..)+)\([^\n\r]+[\r\n]+\s+([^\:\s]+)\:([0-9]+)`),
}
var MAXIMUM_INTEGER int = (1 << 31) - 1
var LIST_MARKS []byte = []byte{'-', '*', '+'}
var LIST_LATIN_MARKS []byte = []byte{'a', 'A', 'i', 'I'}
var TABLE_MARKS []byte = []byte{'^', '_', '='}
func (_self *PatternMatch) GetByGroup(is []int, _default string) string {
var l int = len(_self.Groups)
for _, i := range is {
if i < l && _self.Groups[i] != "" {
return _self.Groups[i]
}
}
return _default
}
func (_self *PatternsModel) GetMatches(_string string, groups [][]byte) *PatternMatch {
var binary []byte = []byte(_string)
var matches PatternMatch
if groups != nil {
var index int = bytes.Index(binary, groups[0])
matches.String = _string
matches.Groups = []string{}
matches.Span = []int{index, index + len(groups[0])}
matches.Ok = true
for _, group := range groups {
matches.Groups = append(matches.Groups, string(group))
}
}
return &matches
}
func NewDebugStackLineModel(line int, file string, method string) DebugStackLineModel {
return DebugStackLineModel{
Line: line,
File: file,
Method: method,
}
}
func NewDebugStackLineFromMatch(match *PatternMatch) DebugStackLineModel {
line, _ := strconv.ParseInt(match.Groups[4], 10, 32)
return NewDebugStackLineModel(int(line), match.Groups[3], match.Groups[1])
}
func GetTraceStack(i int) []DebugStackLineModel {
var stack_map []DebugStackLineModel = []DebugStackLineModel{}
Patterns.Replace(string(debug.Stack()), Patterns.RE_TRACE_STACK, func(matches *PatternMatch) string {
stack_map = append(stack_map, NewDebugStackLineFromMatch(matches))
return ""
})
return stack_map[i+2:]
}
func DebugPrint(items ...any) {
var stack DebugStackLineModel = GetTraceStack(1)[0]
var json_data []byte
json_data, _ = json.Marshal(items)
println(">[DEV]>[" + strconv.FormatInt(int64(stack.Line), 10) + "]" + stack.File + "(" + stack.Method + "): " + string(json_data))
}
func (_self *PatternsModel) Match(_string string, pattern *regexp.Regexp) *PatternMatch {
return _self.GetMatches(_string, pattern.FindSubmatch([]byte(_string)))
}
func (_self *PatternsModel) Replace(_string string, pattern *regexp.Regexp, new_value any) string {
var callback func(matches *PatternMatch) string
var processed string = ""
var binary []byte = []byte(_string)
switch value := new_value.(type) {
case func(matches *PatternMatch) string:
callback = value
case string:
callback = func(matches *PatternMatch) string {
return value
}
}
for {
var matches *PatternMatch = _self.Match(string(binary), pattern)
if !matches.Ok {
break
}
processed += string(binary[:matches.Span[0]]) + callback(matches)
binary = binary[matches.Span[1]:]
}
return processed + string(binary)
}
func NewWMDModule(method func(matches *PatternMatch, language int, path string) string, mode int, pattern any) *WMDModule {
var wmdmodule WMDModule = WMDModule{
mode: mode,
method: method,
}
switch value := pattern.(type) {
case string:
wmdmodule.patterns = []*regexp.Regexp{regexp.MustCompile(value)}
wmdmodule.pattern_method = func(data string, patterns []*regexp.Regexp) *PatternMatch {
return Patterns.Match(data, patterns[0])
}
case WMDModulePattern:
wmdmodule.patterns = []*regexp.Regexp{}
for _, pattern := range value.patterns {
wmdmodule.patterns = append(wmdmodule.patterns, regexp.MustCompile(pattern))
}
wmdmodule.pattern_method = value.method
}
return &wmdmodule
}
func NewWMDModulePattern(patterns []string, method func(data string, patterns []*regexp.Regexp) *PatternMatch) WMDModulePattern {
return WMDModulePattern{
patterns: patterns,
method: method,
}
}
func BoldOrItalicModelMethod(data string, patterns []*regexp.Regexp) *PatternMatch {
var matches *PatternMatch = Patterns.Match(data, patterns[0])
if matches.Ok {
if matches.Groups[1] != "" {
matches.String = matches.String[1:]
matches.Groups[0] = matches.Groups[0][1:]
matches.Span[0]++
}
matches.Groups = []string{matches.Groups[0], matches.Groups[2]}
}
return matches
}
func check_html_module(_type string, matches *PatternMatch) string {
var class_type string = _type
var icon_name string = _type
var input_type string = _type
var disabled string = ""
var checked string = ""
switch _type {
case "radio":
class_type = "radio-button"
icon_name = "radio_button"
case "tick":
input_type = "checkbox"
}
if matches.Groups[1] != "" {
disabled = " disabled"
}
if matches.Groups[2] != "" {
checked = " checked"
}
return `<label class="wmd-icon ` + class_type + `">` +
`<input type="` + input_type + `" readonly onclick="return false;"` + disabled + checked + ` />` +
`<span data-icon="` + icon_name + `"></span>` +
`</label>`
}
func ModuleCheckbox(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
return check_html_module("checkbox", matches)
}
return ""
}
func ModuleRadio(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
return check_html_module("radio", matches)
}
return ""
}
func ModuleTick(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
return check_html_module("tick", matches)
}
return ""
}
func ModuleExclude(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
return `<span class="wmd-excluded">` + matches.Groups[1] + `</span>`
}
return ""
}
func ModuleSpecialCharacters(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
return matches.Groups[1]
}
return ""
}
func GetDirectory(path string) string {
stat, error := os.Stat(path)
if error == nil && stat.IsDir() {
return path
}
return filepath.Dir(path)
}
func PathExists(path string) bool {
_, error := os.Stat(path)
return error == nil
}
func LoadFile(path string) string {
var data string = ""
if stat, error := os.Stat(path); error == nil && !stat.IsDir() {
bytes, _ := os.ReadFile(path)
data = string(bytes)
}
return string(data)
}
func Base64Encode(_string string) string {
return base64.StdEncoding.EncodeToString([]byte(_string))
}
func JSONEncode(data any) string {
json, _ := json.Marshal(data)
return string(json)
}
func ModuleColorSample(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
var color = matches.GetByGroup([]int{2, 3}, "")
return `<span class="wmd-color-sample" style="background-color:` + color + `" title="` + color + `">` +
`<span data-sample="` + color + `"></span>` +
`<span class="color">` + color + `</span>` +
`</span>`
}
return ""
}
func BuildHTMLImage(links []string, _type string, title string) string {
var attributes string = ``
if title != "" {
attributes = ` title="` + title + `" alt="` + title + `"`
}
var html string = `<span class="wmd-media emd-image" data-type="` + _type + `" data-status="unprocessed">` +
`<noscript>`
for _, link := range links {
html += `<img src="` + link + `"` + attributes + ` />`
}
html += `</noscript>` +
`<img src="/images/WMarkDown.png"` + attributes + ` data-sources="` + Base64Encode(JSONEncode(links)) + `" data-i="0" />` +
`<span class="image"></span>`
if title != "" {
html += `<span class="text">` + title + `</span>`
}
return html +
`</span>`
}
func ModulePresentationLinks(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
var items []ModulePresentationLinkItem = []ModulePresentationLinkItem{}
var i int = -1
var html_avatars string = ``
var html_list string = ``
for _, line := range Patterns.RE_NEW_LINES.Split(matches.Groups[1], -1) {
var data string = Trim(line)
if data == "" {
continue
}
var matches *PatternMatch = Patterns.Match(line, Patterns.RE_STARTED_WHITE_SPACES)
var has_spaces bool = matches.Ok && matches.Groups[0] != ""
if has_spaces {
if i == -1 {
continue
}
if data[0] == '*' {
items[i].avatars = append(items[i].avatars, data[1:])
if strings.Contains(data, ".k3y.pw/") {
items[i].avatars = append(items[i].avatars,
strings.ReplaceAll(data[1:], ".k3y.pw/", ".local/"),
strings.ReplaceAll(data[1:], ".k3y.pw/", ".gocal/"),
strings.ReplaceAll(data[1:], ".k3y.pw/", ".cir/"),
)
}
} else {
items[i].links = append(items[i].links, data)
}
} else {
i += 1
items = append(items, ModulePresentationLinkItem{
title: Trim(line),
avatars: []string{},
links: []string{},
})
}
}
for i, item := range items {
html_avatars += `<li title="` + item.title + `" data-i="` + strconv.FormatInt(int64(i), 10) + `" data-links="` + strconv.FormatInt(int64(len(item.links)), 10) + `">`
if len(item.links) != 0 {
html_avatars += `<a href="` + item.links[0] + `" target="_blank">` +
BuildHTMLImage(item.avatars, "image", "") +
`</a>`
} else {
html_avatars += BuildHTMLImage(item.avatars, "image", "")
}
html_list += `<li class="name">` +
`<span>` + item.title + `</span>` +
`<ul class="links">`
for _, link := range item.links {
html_avatars += `<a href="` + link + `" target="_blank" class="wmd-favicon" style="background-image:` +
`url('` + Patterns.Match(link, Patterns.RE_DOMAIN).Groups[0] + `/favicon.ico')`
if strings.Contains(link, ".k3y.pw/") {
html_avatars += `url('` + Patterns.Match(strings.ReplaceAll(link, ".k3y.pw/", ".local/"), Patterns.RE_DOMAIN).Groups[0] + `/favicon.ico')`
}
html_avatars += `;"></a>`
html_list += `<li class="link"><a href="` + link + `" target="_blank" title="` + link + `">` + link + `</a></li>`
}
html_avatars += `</li>`
html_list += `</ul>` +
`</li>`
}
return `<div class="wmd-presentation-links">` +
`<ul class="wmd-avatars">` + html_avatars + `</ul>` +
`<ul class="wmd-list">` + html_list + `</ul>` +
`</div>`
}
return ""
}
func NewWMarkDown() WMarkDown {
var wmarkdown WMarkDown = WMarkDown{
ids_cache: []string{},
keys: []string{
"special_characters", "title", "list", "code_block", "table", "quote", "include",
"media", "code_doc", "presentation_links", "code_sample_block", "paragraph",
"bold", "italic", "underline", "strike", "code_item", "checkbox", "radio", "tick",
"color_sample", "exclude", "link",
},
}
var code_sample_block_module_pattern WMDModulePattern = NewWMDModulePattern([]string{
`(?i)<code\-sample\-block([^>]*)>`,
`(?i)<\/code\-sample\-block>`,
}, func(data string, patterns []*regexp.Regexp) *PatternMatch {
var pattern *PatternMatch = &PatternMatch{}
var matches *PatternMatch = Patterns.Match(data, patterns[0])
if matches.Ok {
var from int = matches.Span[0]
var internal_string string = data[matches.Span[0]:matches.Span[1]]
var attributes string = matches.Groups[1]
var fill int = matches.Span[1]
data = data[fill:]
matches = Patterns.Match(data, patterns[1])
if matches.Ok {
internal_string += data[:matches.Span[1]]
pattern.Ok = true
pattern.Groups = []string{internal_string, attributes, data[:matches.Span[0]]}
pattern.Span = []int{from, fill + matches.Span[1]}
pattern.String = internal_string
}
}
return pattern
})
wmarkdown.modules = map[string]*WMDModule{
"special_characters": NewWMDModule(ModuleSpecialCharacters, WMD.SUBITEM, `\\([\(\{\[\*\\])`),
"title": NewWMDModule(wmarkdown.ModuleTitle, WMD.RAW, `(?m)^(?:(#{1,6})[\t ]*([^\r\n]+)|(={1,6})[\t ]*([^\r\n=]+)={1,6})(?:\n|\r\n|\r){2,}`),
"list": NewWMDModule(wmarkdown.ModuleList, WMD.RAW, `(?mi)^([\-\*#\+]+|([0-9]+|[a-z]+)\.)[^\r\n]*(\n|\r\n|\r)([\t ]*(([\-\*#\+]+|([0-9]+|[a-z]+)\.)[^\r\n]*)(\n|\r\n|\r)?)*`),
"code_block": NewWMDModule(wmarkdown.ModuleCodeBlock, WMD.RAW, `(?m)^(?:"{3}([^\r\n]*)((?:[^"]+|[\r\n]+|"{1,2}[^"])*)"{3}|" " "([^\r\n]*)((?:[^"]+|[\r\n]|"[^ ]|(" ){1,2}[^"])*)" " "|'{3}([^\r\n]*)((?:[^']+|[\r\n]+|'{1,2}[^'])*)'{3}|' ' '([^\r\n]*)((?:[^']+|[\r\n]|'[^ ]|(' ){1,2}[^'])*)' ' '|`+"`{3}([^\\r\\n]*)((?:[^`]+|[\\r\\n]+|`{1,2}[^`])*)`{3}|` ` `([^\\r\\n]*)((?:[^`]+|[\\r\\n]|`[^ ]|(` ){1,2}[^`])*)` ` `)"),
"table": NewWMDModule(wmarkdown.ModuleTable, WMD.RAW, `(?m)^\[\|([^\r\n]*)[\r\n]+((^\|([^\]]([^\r\n"\']|"([^"\\\\]+|\\\\(.|[\r\n])|[\r\n]+)*"|\'([^\'\\\\]+|\\\\(.|[\r\n])|[\r\n]+)*\')*)?[\r\n]+)*)^\|\]`),
"quote": NewWMDModule(wmarkdown.ModuleQuote, WMD.RAW, `(?m)^>[ \t]*(\[\![\t ]*([^\] \t]+)([ \t]+([^\]]+))?\])?(([^\r\n]+(\r\n|[\r\n])?)*)`),
"include": NewWMDModule(wmarkdown.ModuleInclude, WMD.RAW, `(?m)^\[{2}include[ \t]+([^\]]+)\]{2}`),
"media": NewWMDModule(wmarkdown.ModuleMedia, WMD.SUBITEM|WMD.LINKED, `(?i)\({2}\!(image|icon|video|audio|sound|picture)[ \t]+("(([^"]+|[\r\n]+))"|\'(([^\']+|[\r\n]+))\'|([^ \t\)]+))([ \t]*|[ \t]+("(([^\\\\"]+|\\\\.|[\r\n]+)*)"|\'(([^\\\\\']+|\\\\.|[\r\n]+)*)\'|([^\)]+)))\){2}`),
"code_doc": NewWMDModule(wmarkdown.ModuleCodeDoc, WMD.RAW, `(?m)^\[{2}@(([^\]]+|\][^\]])+)\]{2}`),
"presentation_links": NewWMDModule(ModulePresentationLinks, WMD.RAW, `(?m)^\[{2}"{3}(([^"]+|[\r\n]+|"{1,2}[^"])*)"{3}\]{2}`),
"code_sample_block": NewWMDModule(wmarkdown.ModuleCodeSampleBlock, WMD.RAW, code_sample_block_module_pattern),
"paragraph": NewWMDModule(wmarkdown.ModuleParagraph, WMD.RAW, `(?m)^(([^\r\n]+(\n|\r\n|\r)?)+)`),
"bold": NewWMDModule(wmarkdown.ModuleBold, WMD.SUBITEM|WMD.LINKED, NewWMDModulePattern([]string{`(^|[^\\])\*{2}((?:[^\*]+|[\n\r]+|\*[^\*])+\*?)\*{2}`}, BoldOrItalicModelMethod)),
"italic": NewWMDModule(wmarkdown.ModuleItalic, WMD.SUBITEM|WMD.LINKED, NewWMDModulePattern([]string{`(^|[^\\])\*((?:[^\*]+|[\r\n]+|\*{2})+(?:\*{2})?)\*`}, BoldOrItalicModelMethod)),
"underline": NewWMDModule(wmarkdown.ModuleUnderline, WMD.SUBITEM|WMD.LINKED, `__(([^_]+|_[^_])+)__`),
"strike": NewWMDModule(wmarkdown.ModuleStrike, WMD.SUBITEM|WMD.LINKED, `~~(([^~]+|~[^~])+)~~`),
"code_item": NewWMDModule(wmarkdown.ModuleCodeItem, WMD.SUBITEM|WMD.LINKED, "``(([^`]+|`[^`])+)``"),
"checkbox": NewWMDModule(ModuleCheckbox, WMD.SUBITEM|WMD.LINKED, `(?i)\[(?:(\-)|(x)|( ))\]`),
"radio": NewWMDModule(ModuleRadio, WMD.SUBITEM|WMD.LINKED, `(?i)\((?:(\-)|(x)|( ))\)`),
"tick": NewWMDModule(ModuleTick, WMD.SUBITEM|WMD.LINKED, `(?i)\{(?:(w)|(v)|(x))\}`),
"color_sample": NewWMDModule(ModuleColorSample, WMD.SUBITEM|WMD.LINKED, `(?i)\[{2}((\#[a-f0-9]{3,8})|color[ \t]+([^\[\]\t\r\n]+))\]{2}`),
"exclude": NewWMDModule(ModuleExclude, WMD.SUBITEM, `\[{2}\!(([^\]]+|\][^\]]|[\r\n])*)\]{2}`),
"link": NewWMDModule(wmarkdown.ModuleLink, WMD.SUBITEM, `(?i)(\[([^\[\]]+)\])?\((([^\(\) \t]+)[ \t]+("(([^\\\\"]+|\\\\.|[\r\n]+)*)"|\'(([^\\\\\']+|\\\\.|[\r\n]+)*)\'|([^\(\)]+)?))[ \t]*\)|\[(\+?[0-9 ]{5,22}|[^ \]]+)([\t ]+([^\]]*))?\]|([a-z0-9]{3,8}\:\/{2}[^ \(\[\)\]>]+|[a-z\.0-9_\-]+@[a-z\.0-9_\-]+\.[a-z0-9]+)`),
}
return wmarkdown
}
func (_self *WMarkDown) ModuleDefault(matches *PatternMatch, language int, path string) string {
return matches.Groups[0]
}
func (_self *WMarkDown) Id(_string string) string {
var id string = Patterns.Replace(_string, Patterns.RE_CHARACTERS_NO_ID, "-")
if id[0] == '-' {
id = id[1:]
} else if id[len(id)-1] == '-' {
id = id[:len(id)-1]
}
if slices.Contains(_self.ids_cache, id) {
var i int = 2
for ; slices.Contains(_self.ids_cache, id+"-"+strconv.FormatInt(int64(i), 10)); i++ {
}
id += "-" + strconv.FormatInt(int64(i), 10)
}
_self.ids_cache = append(_self.ids_cache, id)
return id
}
func (_self *WMarkDown) ResetIds() {
_self.ids_cache = []string{}
}
func (_self *WMarkDown) ModuleTitle(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
var level string
var content string
if matches.Groups[1] != "" {
level = matches.Groups[1]
} else {
level = matches.Groups[3]
}
level = strconv.FormatInt(int64(len(level)), 10)
if matches.Groups[2] != "" {
content = matches.Groups[2]
} else {
content = matches.Groups[4]
}
return `<h` + level + ` class="wmd-title" id="` + _self.Id(content) + `" title="` + content + `" data-test="jojo">` +
`<span>` + _self.Analyse(content, language, WMD.SUBITEM, path) + `<span>` +
`</h` + (level) + `>`
}
return ""
}
func Trim(_string string) string {
return strings.Trim(_string, " \r\t\n")
}
func (_self *WMarkDown) ModuleParagraph(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
return `<p>` + _self.Analyse(Trim(matches.Groups[0]), language, WMD.SUBITEM, path) + `</p>`
}
return ""
}
func (_self *WMarkDown) ModuleBold(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
return `<b>` + _self.Analyse(matches.Groups[1], language, WMD.SUBITEM, path) + `</b>`
}
return ""
}
func (_self *WMarkDown) ModuleItalic(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
return `<i>` + _self.Analyse(matches.Groups[1], language, WMD.SUBITEM, path) + `</i>`
}
return ""
}
func (_self *WMarkDown) ModuleStrike(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
return `<del>` + _self.Analyse(matches.Groups[1], language, WMD.SUBITEM, path) + `</del>`
}
return ""
}
func (_self *WMarkDown) ModuleUnderline(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
return `<u>` + _self.Analyse(matches.Groups[1], language, WMD.SUBITEM, path) + `</u>`
}
return ""
}
func (_self *WMarkDown) ModuleCodeItem(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
return `<code>` + _self.Analyse(matches.Groups[1], language, WMD.SUBITEM, path) + `</code>`
}
return ""
}
func NewWMDListLevel(separator int, _type byte, subtype string) *WMDListLevel {
return &WMDListLevel{
separator: separator,
_type: _type,
subtype: subtype,
}
}
func list_deployed(level *WMDListLevel, last_mark string, l int) string {
var _type string = level.subtype
var deployable bool = l != 0 && strings.Contains("+-", _type)
var deployed string = "true"
var processed string = ""
if l != 0 && last_mark != "" {
_type = last_mark
}
if !deployable {
deployed = "null"
} else {
processed = ` data-list-unprocessed="true"`
if _type == "-" {
deployed = "false"
}
}
return ` data-deployed="` + deployed + `"` + processed
}
func list_start(unordered bool, _type string) string {
if !unordered {
_type = _type[:len(_type)-1]
if Patterns.Match(_type, Patterns.RE_INTEGER).Ok {
return ` start="` + _type + `"`
}
}
return ``
}
func (_self *WMarkDown) ModuleList(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
var html string = ``
var levels []*WMDListLevel = []*WMDListLevel{NewWMDListLevel(0, 'u', "")}
var l int = 0
var last_mark string = ""
var subtype string = ""
for _, line := range Patterns.RE_NEW_LINE.Split(matches.Groups[0], -1) {
var matches *PatternMatch = Patterns.Match(line, Patterns.RE_LIST_LINE)
if !matches.Ok || matches.String == "" {
continue
}
var separator_by_marks int = len(matches.Groups[3])
var separator int = separator_by_marks
var _type string = matches.Groups[2]
var key string = matches.Groups[4]
var _string string = Trim(_self.Analyse(matches.Groups[5], language, WMD.SUBITEM, path))
var type_character byte = _type[len(_type)-1]
var unordered bool = _type != "" && slices.Contains(LIST_MARKS, type_character)
if separator_by_marks <= 1 {
separator = len(matches.Groups[1])
}
if !unordered {
levels[0]._type = 'o'
}
subtype = _type
if _type != "" {
if levels[l].separator == separator {
levels[l].subtype = subtype
if html != "" {
html += `</li>`
} else {
html += `<` + string(levels[l]._type) + `l class="wmd-list"` + list_start(unordered, _type) + list_deployed(levels[l], last_mark, l)
if key != "" && !unordered && slices.Contains(LIST_LATIN_MARKS, key[0]) {
html += ` type="` + string(key[0]) + `"`
}
html += `>`
}
html += `<li>` + _string
} else if levels[l].separator < separator {
var child_type byte = 'u'
if !unordered {
child_type = 'o'
}
l++
levels = append(levels, NewWMDListLevel(separator, child_type, subtype))
html += `<` + string(levels[l]._type) + `l class="wmd-list"` + list_start(unordered, _type) + list_deployed(levels[l-1], last_mark, l)
if key != "" && !unordered && slices.Contains(LIST_LATIN_MARKS, key[0]) {
html += ` type="` + string(key[0]) + `"`
}
html += `><li>` + _string
} else {
for levels[l].separator > separator {
html += `</li></` + string(levels[l]._type) + `l>`
levels = levels[:l]
l--
if l == 0 {
break
}
}
html += `</li><li>` + _string
}
} else {
html += ` ` + _string
}
last_mark = subtype
}
for l >= 0 {
html += `</li></` + string(levels[l]._type) + `l>`
levels = levels[:l]
l--
}
return html
}
return ""
}
func (_self *WMarkDown) ModuleTable(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
var html map[string]string = map[string]string{
"thead": ``,
"tbody": ``,
"tfoot": ``,
}
var attributes string = Trim(matches.Groups[1])
var data string = Trim(matches.Groups[2])
for {
var line_matches *PatternMatch = Patterns.Match(data, Patterns.RE_TABLE_LINE)
if !line_matches.Ok || line_matches.Groups[0] == "" {
break
}
var line string = line_matches.Groups[0]
var cell_type string = "d"
var tags []string
var row string = `<tr>`
if slices.Contains(TABLE_MARKS, line[1]) {
cell_type = "h"
switch line[1] {
case '^':
tags = []string{"thead"}
case '_':
tags = []string{"tfoot"}
case '=':
tags = []string{"thead", "tfoot"}
}
} else {
tags = []string{"tbody"}
}
if cell_type == "h" {
line = line[:1] + line[2:]
}
for {
var cell_matches *PatternMatch = Patterns.Match(line, Patterns.RE_TABLE_ITEM)
if !cell_matches.Ok || cell_matches.Groups[0] == "" {
break
}
var column_span_i int = len(cell_matches.Groups[1])
var column_span string = ``
var content string = Trim(cell_matches.Groups[2])
if column_span_i > 1 {
column_span = ` colspan="` + strconv.FormatInt(int64(column_span_i), 10) + `"`
}
if len(content) > 1 && slices.Contains([]string{`''`, `""`}, content[:1]+content[len(content)-1:]) {
content = content[1 : len(content)-1]
}
row += `<t` + cell_type + column_span + `>` + _self.Analyse(content, WMD.HTML, WMD.SUBITEM, path) + `</t` + cell_type + `>`
line = line[cell_matches.Span[1]:]
}
row += `</tr>`
for i, tag := range tags {
if i == 0 {
html[tag] += row
} else {
html[tag] = row + html[tag]
}
}
data = data[line_matches.Span[1]:]
}
return `<div class="wmd-table">` +
`<table ` + attributes + `>` +
`<thead>` + html["thead"] + `</thead>` +
`<tbody>` + html["tbody"] + `</tbody>` +
`<tfoot>` + html["tfoot"] + `</tfoot>` +
`</table>` +
`</div>`
}
return ""
}
func (_self *WMarkDown) ModuleLink(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
var raw_url string = Trim(matches.GetByGroup([]int{4, 11, 14}, ""))
var url string = raw_url
var text string = Trim(matches.GetByGroup([]int{2, 13}, raw_url))
var protocol_matches *PatternMatch = Patterns.Match(url, Patterns.RE_PROTOCOL)
var html string
if Patterns.Match(raw_url, Patterns.RE_PHONE_NUMBER).Ok {
url = "tel:" + strings.ReplaceAll(raw_url, " ", "")
} else if Patterns.Match(raw_url, Patterns.RE_EMAIL_ADDRESS).Ok {
url = "mailto:" + raw_url
}
html += `<a href="` + url + `" target="_`
if strings.Contains(url, "://") {
html += "blank"
} else {
html += "self"
}
html += `" title="` + Trim(matches.GetByGroup([]int{6, 8, 10}, text)) + `" data-protocol="`
if protocol_matches.Ok {
html += strings.ToLower(protocol_matches.Groups[1])
} else {
html += "http"
}
return html + `">` + _self.Analyse(text, language, WMD.SUBITEM|WMD.LINKED, path) + `</a>`
}
return ""
}
func (_self *WMarkDown) ModuleQuote(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
var _type string = matches.Groups[2]
var type_text string = matches.Groups[4]
var html string
if value, ok := WMD.quote_special[_type]; ok {
_type = value
}
html = `<blockquote class="wmd-quote"`
if _type != "" {
html += ` data-quote-type="` + _type + `">` +
`<div class="quote-type">`
if _type[0] == '@' {
html += `<span data-avatar="` + strings.ToLower(_type[1:]) + `"></span>` +
`<span class="user">` + strings.ToLower(_type[1:]) + `</span>`
} else {
html += `<span data-icon="` + _type + `"></span>`
if type_text != "" {
html += `<span class="quote-type-name">` + type_text + `</span>`
}
}
html += `</div>`
} else {
html += `>`
}
return html + `<div class="text">` + _self.Analyse(matches.Groups[5], language, WMD.SUBITEM, path) + `</div>` +
`</blockquote>`
}
return ""
}
func code_block_data(key string, value string, language int) string {
if language&WMD.HTML != 0 {
return `<li data-i18n="` + key + `" data-i18n-without="true" title="` + key + `">` +
`<span data-icon="` + key + `"></span>` +
`<span data-i18n="` + key + `">` + key + `</span>` +
`<span class="value">` + value + `</span>` +
`</li>`
}
return ""
}
func FilterHTMLSpecialCharacters(_string string) string {
var l int = len(WMD.html_special_characters)
var z int = -(1 << 28)
var index []int = []int{}
var response string = ``
var characters []string = slices.Collect(maps.Keys(WMD.html_special_characters))
for j := 0; j < l; j++ {
index = append(index, -1)
}
for {
var i int = -1
for j, character := range characters {
if index[j] < z {
continue
}
if index[j] < 0 {
index[j] = strings.Index(_string, character)
if index[j] == -1 {
index[j] = z
continue
}
}
if i == -1 || index[i] > index[j] {
i = j
}
}
if i == -1 {
response += _string
break
}
var length int = index[i]
response += _string[:length] + "&" + WMD.html_special_characters[characters[i]] + ";"
_string = _string[length+1:]
for j := 0; j < l; j++ {
if index[j] != z {
index[j] -= length + 1
}
}
}
return response
}
func GetCodeBlockData(matches *PatternMatch) (string, string) {
var _type string
var content string
for i := 0; i < 6; i++ {
var j int = 1 + i*2
_type = matches.Groups[j]
content = matches.Groups[j+1]
if _type != "" || content != "" {
if _type != "" {
_type = strings.ToLower(_type)
} else {
_type = "unamed"
}
break
}
}
return _type, content
}
func (_self *WMarkDown) ModuleCodeBlock(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
_type, content := GetCodeBlockData(matches)
if _type != "" {
var lines []string = Patterns.RE_NEW_LINE.Split(content, -1)[2:]
var html string = `<div class="wmd-code-block" data-type="` + _type + `" data-processed="false">` +
`<ul class="data">` +
`<li><span data-icon="` + _type + `"></span></li>` +
code_block_data("type", _type, language) +
code_block_data("characters", strconv.FormatInt(int64(len(content)), 10), language) +
code_block_data("lines", strconv.FormatInt(int64(len(lines)), 10), language) +
`</ul>` +
`<div class="code">` +
`<ol class="lines">`
for range lines {
html += `<li></li>`
}
html += `<pre class="content">` + FilterHTMLSpecialCharacters(content) + `</pre>` +
`</div>` +
`</div>`
return html
}
return "UNKNOWN_BLOCK"
}
return ""
}
func (_self *WMarkDown) ModuleInclude(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
var relative_path string = matches.Groups[1]
var directory_origin string = GetDirectory(path)
var new_path string = directory_origin + "/" + relative_path
var extension []string = Patterns.Match(new_path, Patterns.RE_EXTENSION).Groups[1:]
var directory string = GetDirectory(new_path)
var content string = LoadFile(new_path)
var results string = content
var included string = "false"
if content != "" {
if strings.Join(extension, ".") == "w.md" {
results, _ = _self.Process(content, language, directory)
} else if extension[len(extension)-1] == "md" {
results = _self.Analyse(content, language, WMD.RAW, directory)
}
}
if PathExists(new_path) {
included = "true"
}
return `<section data-path="` + relative_path + `" data-include="` + included + `">` +
`<div class="link"><a href="` + relative_path + `" target="_self" title="` + relative_path + `">` + relative_path + `</a></div>` +
results +
`</section>`
}
return ""
}
func (_self *WMarkDown) ModuleMedia(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
var _type string = strings.ToLower(matches.Groups[1])
var links []string = Patterns.RE_WHITE_SPACES.Split(matches.GetByGroup([]int{3, 5, 7}, ""), -1)
var text string = Trim(matches.GetByGroup([]int{10, 12, 14}, ""))
if slices.Contains([]string{"image", "picture", "icon"}, _type) {
return BuildHTMLImage(links, _type, text)
}
if slices.Contains([]string{"video"}, _type) {
var html string
if strings.Contains(links[0], "youtube.com") || strings.Contains(links[0], "youtu.be") {
_type = "youtube"
} else if strings.Contains(links[0], "vimeo.com") {
_type = "vimeo"
}
html = `<span class="wmd-media wmd-video" data-type="` + _type + `" data-status="unprocessed">` +
`<noscript></noscript>`
switch _type {
case "youtube":
html += `<b>YOUTUBE</b>`
case "vimeo":
html += `<b>VIMEO</b>`
default:
var poster string = ""
if len(links) > 1 {
poster = ` poster="` + links[1] + `"`
}
html += `<video src="` + links[0] + `"` + poster + `></video>`
}
return html + `</span>`
}
if slices.Contains([]string{"sound", "audio"}, _type) {
var html string
if strings.Contains(links[0], "soundcloud.com") {
_type = "soundcloud"
}
html = `<span class="wmd-media wmd-audio" data-type="` + _type + `" data-status="unprocessed">` +
`<noscript></noscript>`
switch _type {
case "soundcloud":
html += `<b>SOUNDCLOUD</b>`
default:
html += `<audio src="` + links[0] + `" controls></audio>`
}
return html + `</span>`
}
return ""
}
return ""
}
func MarkReplace(_string string, matches *PatternMatch, fragments *[]string) string {
if matches.Ok {
_string = _string[:matches.Span[0]] + WMD.item_mark.from + strconv.FormatInt(int64(len(*fragments)), 10) + WMD.item_mark.to + _string[matches.Span[1]:]
*fragments = append(*fragments, matches.Groups[0])
}
return _string
}
func RestoreMarks(_string string, fragments *[]string) string {
var l int = len(*fragments)
for {
var matches *PatternMatch = Patterns.Match(_string, WMD.item_mark.pattern)
if !matches.Ok {
break
}
power_i, _ := strconv.ParseInt(matches.Groups[1], 10, 32)
var i int = int(power_i)
var subdata string = ""
if i < l {
subdata = (*fragments)[i]
}
_string = _string[:matches.Span[0]] + subdata + _string[matches.Span[1]:]
}
return _string
}
func doc_type_format(typed string) string {
return FilterHTMLSpecialCharacters(strings.ReplaceAll(strings.ReplaceAll(typed, " ", ""), ",", ", "))
}
func IsReference(scope string) string {
if strings.Contains(scope, "&") {
return "reference"
}
if strings.Contains(scope, "*") {
return "pointer"
}
return ""
}
func (_self *WMarkDown) ModuleCodeDoc(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
var data string = Trim(matches.Groups[1])
var base PatternMatch = *Patterns.Match(data, Patterns.RE_CODE_DOC)
var return_type string = doc_type_format(base.GetByGroup([]int{2}, "void"))
var access string = base.GetByGroup([]int{3}, "public")
var constant string = ""
var reference string = IsReference(access)
var name_space string = base.Groups[5]
var environment string = base.GetByGroup([]int{6}, "global")
var parameters string = base.Groups[9]
var has_parameters bool = parameters != ""
var default_value string = base.Groups[11]
var full_method string = ""
var arguments string = ""
var arguments_definition string = ""
var header string = `<tr>` +
`<th data-i18n="name" title="Name">Name</th>` +
`<th data-i18n="required" title="Required">Required</th>` +
`<th data-i18n="nullable" title="Nullable">Nullable</th>` +
`<th data-i18n="typed" title="Typed">Typed</th>` +
`<th data-i18n="default_value" title="Default value">Default value</th>` +
`</tr>`
var fragments *[]string = &[]string{}
var arguments_l int = 0
if name_space != "" {
full_method = name_space + "."
}
full_method += Trim(base.Groups[7])
if strings.Contains(access, "=") {
constant = "constant"
}
if strings.Contains(access, "?") {
access = "protected"
} else if strings.Contains(access, "!") {
access = "private"
} else {
access = "global"
}
switch environment {
case ":":
environment = "static"
case ".":
environment = "object"
}
if has_parameters {
for {
var matches *PatternMatch = Patterns.Match(parameters, Patterns.RE_CODE_DOC_SUBARGUMENTS)
if !matches.Ok {
break
}
parameters = MarkReplace(parameters, matches, fragments)
}
}
for parameter := range strings.SplitSeq(parameters, ",") {
var matches *PatternMatch = Patterns.Match(Trim(parameter), Patterns.RE_CODE_DOC_ARGUMENTS)
if matches.Ok {
var scopes string = matches.Groups[1]
var required bool = strings.Contains(scopes, "!")
var nullish bool = strings.Contains(scopes, "?")
var subreference string = IsReference(access)
var typed string = doc_type_format(RestoreMarks(matches.Groups[2], fragments))
var name string = Trim(matches.Groups[3])
var default_value string = FilterHTMLSpecialCharacters(RestoreMarks(Trim(matches.Groups[5]), fragments))
arguments += `<span class="argument">`
if required {
arguments += `<span class="required" data-i18n="required" title="Required">Required</span>`
}
if nullish {
arguments += `<span class="nullish" data-i18n="nullish" title="Nullish">Nullish</span>`
}
if subreference != "" {
arguments += `<span class="reference" data-i18n="reference" data-i18n-without="true" title="Reference">` + subreference + `</span>`
}
arguments += `<span class="typed" data-i18n="typed" data-i18n-without="true" title="Typed">` + typed + `</span>` +
`<span class="name" data-i18n="name" data-i18n-without="true" title="Name">` + name + `</span>`
if default_value != "" {
arguments += `<span class="default_value" data-i18n="default_value" title="Default value">` + default_value + `</span>`
}
arguments += `</span>`
arguments_definition += `<tr>` +
`<td data-i18n="name" data-i18n-without="true" title="Name">` + name + `</td>` +
`<td data-i18n="required" data-i18n-without="true" title="Required">`
if required {
arguments_definition += `True`
} else {
arguments_definition += `False`
}
arguments_definition += `</td>` +
`<td data-i18n="nullish" data-i18n-without="true" title="Nullish">`
if nullish {
arguments_definition += `True`
} else {
arguments_definition += `False`
}
arguments_definition += `</td>` +
`<td data-i18n="typed" data-i18n-without="true" title="Typed">` + typed + `</td>` +
`<td data-i18n="default_value" data-i18n-without="true" title="Default value">` + default_value + `</td>` +
`</tr>`
arguments_l++
}
}
var html string = `<fieldset class="wmd-code-doc" data-arguments-l="` + strconv.FormatInt(int64(arguments_l), 10) + `">` +
`<legend>` + full_method + `</legend>` +
`<div class="description">`
if reference != "" {
html += `<span class="reference" data-i18n="reference" data-i18n-without="true" title="Reference">` + reference + `</span>`
}
html += `<span class="return-type" data-i18n="return" data-i18n-without="true" title="Return">` + return_type + `</span>`
if constant != "" {
html += `<span class="constant" data-i18n="constant" data-i18n-without="true" title="Constant">` + constant + `</span>`
}
html += `<span class="environment" data-i18n="environment" data-i18n-without="true" title="Environment">` + environment + `</span>` +
`<span class="access" data-i18n="access" data-i18n-without="true" title="Access">` + access + `</span>` +
`<span class="full-method" data-i18n="method" data-i18n-without="true" title="Method">` + full_method + `</span>`
if has_parameters {
html += `<span class="arguments" data-i18n="arguments" data-i18n-without="true" title="Arguments">` + arguments + `</span>`
}
if default_value != "" {
html += `<span class="default-value" data-i18n="default_value" data-i18n-without="true" title="Default value">` + default_value + `</span>`
}
return html + `</div>` +
`<div class="wmd-table definition">` +
`<table>` +
`<thead>` + header + `</thead>` +
`<tbody>` + arguments_definition + `</tbody>` +
`<tfoot>` + header + `</tfoot>` +
`</table>` +
`</div>` +
`</fieldset>`
}
return ""
}
func (_self *WMarkDown) ModuleCodeSampleBlock(matches *PatternMatch, language int, path string) string {
if language&WMD.HTML != 0 {
var attributes string = matches.Groups[1]
var submatches *PatternMatch = Patterns.Match(attributes, Patterns.RE_CLASS_ATTRIBUTE)
var classes []string = []string{}
var content string = matches.Groups[2]
var languages string = ``
var html string = ``
var i int = 0
if submatches.Ok {
classes = Patterns.RE_WHITE_SPACES.Split(Trim(submatches.Groups[1]), -1)
}
for {
var submatches *PatternMatch = Patterns.Match(content, _self.modules["code_block"].patterns[0])
if !submatches.Ok {
break
}
_type, _ := GetCodeBlockData(submatches)
languages += `<li data-language="` + _type + `" data-i18n="` + _type + `" title="` + _type + `" data-selected="`
if i == 0 {
languages += "true"
} else {
languages += "false"
}
languages += `">` + _type + `</li>`
html += _self.ModuleCodeBlock(submatches, language, path)
content = content[submatches.Span[1]:]
}
if len(classes) == 0 {
classes = append(classes, "wmd-code-sample-block")
}
return `<div class="` + strings.Join(classes, " ") + `"` + attributes + ` data-processed="false">` +
`<nav class="languages"><ul>` + languages + `</ul></nav>` +
`<div class="content">` + html + `</div>` +
`</div>`
}
return ""
}
func WMDRemoveDefaultTabulations(data string) string {
var spaces int = len(data)
for _, line := range Patterns.RE_NEW_LINES.Split(data, -1) {
if line == "" {
continue
}
var white_spaces int = len(Patterns.Match(line, Patterns.RE_STARTED_WHITE_SPACES).Groups[0])
if white_spaces < spaces {
spaces = white_spaces
}
}
return Patterns.Replace(data, Patterns.RE_LINES, func(matches *PatternMatch) string {
return matches.Groups[0][spaces:]
})
}
func (_self WMarkDown) build(data string, language int, path string) string {
if language&WMD.HTML != 0 {
return `<div class="wmd wmarkdown" data-dictionary-processed="false" data-menu-processed="false">` +
_self.Analyse(WMDRemoveDefaultTabulations(data), language, WMD.RAW, path) +
`<div class="wmd-process-and-loaded"></div>` +
`</div>`
}
return ""
}
func (_self *WMarkDown) Process(data string, language int, path string) (string, map[string]string) {
var results string = ""
var variables map[string]string = map[string]string{}
var options *PatternMatch = Patterns.Match(data, Patterns.RE_OPTIONS)
if options.Ok {
var options_data string = options.Groups[1]
for {
var option *PatternMatch = Patterns.Match(options_data, Patterns.RE_OPTION)
if !option.Ok {
break
}
variables[Trim(option.Groups[1])] = Trim(option.Groups[2])
options_data = options_data[option.Span[1]:]
}
data = data[:options.Span[0]] + data[options.Span[1]:]
}
for {
var matches *PatternMatch = Patterns.Match(data, Patterns.RE_BLOCK_MARK)
if !matches.Ok {
results += data
break
}
var mark string = matches.Groups[1]
var RE_CLOSE *regexp.Regexp = Patterns.RE_BLOCK_MARK
if mark != "" {
RE_CLOSE = regexp.MustCompile(`(?m)^\s*` + mark)
}
results += data[:matches.Span[0]]
data = data[matches.Span[1]:]
var close_matches *PatternMatch = Patterns.Match(data, RE_CLOSE)
if close_matches.Ok {
results += _self.build(data[:close_matches.Span[0]], language, path)
data = data[close_matches.Span[1]:]
} else {
results += _self.build(data, language, path)
break
}
}
return results, variables
}
func (_self *WMarkDown) Analyse(data string, language int, mode int, path string) string {
var response string = ""
var items map[string]*WMDAnalyseItem = map[string]*WMDAnalyseItem{}
var current_keys []string = []string{}
for _, key := range _self.keys {
if mode == WMD.RAW || mode|_self.modules[key].mode == _self.modules[key].mode {
items[key] = &WMDAnalyseItem{
exists: true,
from: -1,
to: -1,
matches: &PatternMatch{
Ok: false,
String: "",
Groups: []string{},
Span: []int{-1, -1},
},
}
current_keys = append(current_keys, key)
}
}
for {
var l int = len(data)
var selected string = ""
for _, key := range current_keys {
if !items[key].exists {
continue
}
var module WMDModule = *_self.modules[key]
if items[key].from < 0 {
items[key].matches = module.pattern_method(data, module.patterns)
if items[key].matches.Ok {
items[key].from = items[key].matches.Span[0]
items[key].to = items[key].matches.Span[1]
} else {
items[key].exists = false
continue
}
}
if items[key].from < l {
l = items[key].from
selected = key
}
}
if selected == "" {
response += data
break
}
var to int = items[selected].to
response += data[:items[selected].from] + _self.modules[selected].method(items[selected].matches, language, path)
data = data[to:]
for _, item := range items {
if item.matches.Ok {
item.from -= to
item.to -= to
}
}
}
return response
}
type DebugStackLineModel struct {
Line int
Method string
File string
}
func (_self *WMarkDown) ToHTML(data string, path string) string {
return _self.Analyse(data, WMD.HTML, WMD.RAW, path)
}