1690 lines
48 KiB
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)
|
|
}
|