Add project files
This commit is contained in:
commit
ab339c8a2c
81 changed files with 2567 additions and 0 deletions
|
@ -0,0 +1,340 @@
|
|||
extends RefCounted
|
||||
|
||||
const UpdaterConfig = preload("updater_config.gd")
|
||||
var config = UpdaterConfig.get_user_config()
|
||||
|
||||
const FONT_H1 := 42
|
||||
const FONT_H2 := 32
|
||||
const FONT_H3 := 26
|
||||
const FONT_H4 := 22
|
||||
const FONT_H5 := 16
|
||||
const FONT_H6 := 12
|
||||
|
||||
var HORIZONTAL_RULE: String = "[img=4000x2]res://addons/%s/generated/updater/assets/horizontal-line2.png[/img]\n" % config.plugin_name
|
||||
const HEADER_RULE := "[font_size=%d]$1[/font_size]\n"
|
||||
const HEADER_CENTERED_RULE := "[font_size=%d][center]$1[/center][/font_size]\n"
|
||||
|
||||
const image_download_folder := "res://cached/updater/"
|
||||
|
||||
const exclude_font_size := "\b(?!(?:(font_size))\b)"
|
||||
|
||||
var md_replace_patterns := [
|
||||
# horizontal rules
|
||||
[regex("(?m)^[ ]{0,3}---$"), HORIZONTAL_RULE],
|
||||
[regex("(?m)^[ ]{0,3}___$"), HORIZONTAL_RULE],
|
||||
[regex("(?m)^[ ]{0,3}\\*\\*\\*$"), HORIZONTAL_RULE],
|
||||
|
||||
# headers
|
||||
[regex("(?m)^###### (.*)"), HEADER_RULE % FONT_H6],
|
||||
[regex("(?m)^##### (.*)"), HEADER_RULE % FONT_H5],
|
||||
[regex("(?m)^#### (.*)"), HEADER_RULE % FONT_H4],
|
||||
[regex("(?m)^### (.*)"), HEADER_RULE % FONT_H3],
|
||||
[regex("(?m)^## (.*)"), (HEADER_RULE + HORIZONTAL_RULE) % FONT_H2],
|
||||
[regex("(?m)^# (.*)"), (HEADER_RULE + HORIZONTAL_RULE) % FONT_H1],
|
||||
[regex("(?m)^(.+)=={2,}$"), HEADER_RULE % FONT_H1],
|
||||
[regex("(?m)^(.+)--{2,}$"), HEADER_RULE % FONT_H2],
|
||||
# html headers
|
||||
[regex("<h1>((.*?\\R?)+)<\\/h1>"), (HEADER_RULE + HORIZONTAL_RULE) % FONT_H1],
|
||||
[regex("<h1[ ]*align[ ]*=[ ]*\"center\">((.*?\\R?)+)<\\/h1>"), (HEADER_CENTERED_RULE + HORIZONTAL_RULE) % FONT_H1],
|
||||
[regex("<h2>((.*?\\R?)+)<\\/h2>"), (HEADER_RULE + HORIZONTAL_RULE) % FONT_H2],
|
||||
[regex("<h2[ ]*align[ ]*=[ ]*\"center\">((.*?\\R?)+)<\\/h2>"), (HEADER_CENTERED_RULE + HORIZONTAL_RULE) % FONT_H1],
|
||||
[regex("<h3>((.*?\\R?)+)<\\/h3>"), HEADER_RULE % FONT_H3],
|
||||
[regex("<h3[ ]*align[ ]*=[ ]*\"center\">((.*?\\R?)+)<\\/h3>"), HEADER_CENTERED_RULE % FONT_H3],
|
||||
[regex("<h4>((.*?\\R?)+)<\\/h4>"), HEADER_RULE % FONT_H4],
|
||||
[regex("<h4[ ]*align[ ]*=[ ]*\"center\">((.*?\\R?)+)<\\/h4>"), HEADER_CENTERED_RULE % FONT_H4],
|
||||
[regex("<h5>((.*?\\R?)+)<\\/h5>"), HEADER_RULE % FONT_H5],
|
||||
[regex("<h5[ ]*align[ ]*=[ ]*\"center\">((.*?\\R?)+)<\\/h5>"), HEADER_CENTERED_RULE % FONT_H5],
|
||||
[regex("<h6>((.*?\\R?)+)<\\/h6>"), HEADER_RULE % FONT_H6],
|
||||
[regex("<h6[ ]*align[ ]*=[ ]*\"center\">((.*?\\R?)+)<\\/h6>"), HEADER_CENTERED_RULE % FONT_H6],
|
||||
|
||||
# asterics
|
||||
#[regex("(\\*)"), "xxx$1xxx"],
|
||||
|
||||
# extract/compile image references
|
||||
[regex("!\\[(.*?)\\]\\[(.*?)\\]"), Callable(self, "process_image_references")],
|
||||
# extract images with path and optional tool tip
|
||||
[regex("!\\[(.*?)\\]\\((.*?)(( )+(.*?))?\\)"), Callable(self, "process_image")],
|
||||
|
||||
# links
|
||||
[regex("([!]|)\\[(.+)\\]\\(([^ ]+?)\\)"), "[url={\"url\":\"$3\"}]$2[/url]"],
|
||||
# links with tool tip
|
||||
[regex("([!]|)\\[(.+)\\]\\(([^ ]+?)( \"(.+)\")?\\)"), "[url={\"url\":\"$3\", \"tool_tip\":\"$5\"}]$2[/url]"],
|
||||
|
||||
# embeded text
|
||||
[regex("(?m)^[ ]{0,3}>(.*?)$"), "[img=50x14]res://addons/%s/generated/updater/assets/embedded.png[/img][i]$1[/i]" % config.plugin_name],
|
||||
|
||||
# italic + bold font
|
||||
[regex("[_]{3}(.*?)[_]{3}"), "[i][b]$1[/b][/i]"],
|
||||
[regex("[\\*]{3}(.*?)[\\*]{3}"), "[i][b]$1[/b][/i]"],
|
||||
# bold font
|
||||
[regex("<b>(.*?)<\\/b>"), "[b]$1[/b]"],
|
||||
[regex("[_]{2}(.*?)[_]{2}"), "[b]$1[/b]"],
|
||||
[regex("[\\*]{2}(.*?)[\\*]{2}"), "[b]$1[/b]"],
|
||||
# italic font
|
||||
[regex("<i>(.*?)<\\/i>"), "[i]$1[/i]"],
|
||||
[regex(exclude_font_size+"_(.*?)_"), "[i]$1[/i]"],
|
||||
[regex("\\*(.*?)\\*"), "[i]$1[/i]"],
|
||||
|
||||
# strikethrough font
|
||||
[regex("<s>(.*?)</s>"), "[s]$1[/s]"],
|
||||
[regex("~~(.*?)~~"), "[s]$1[/s]"],
|
||||
[regex("~(.*?)~"), "[s]$1[/s]"],
|
||||
|
||||
# handling lists
|
||||
# using an image for dots as workaroud because list is not supported checked Godot 3.x
|
||||
[regex("(?m)^[ ]{0,1}[*\\-+] (.*)$"), list_replace(0)],
|
||||
[regex("(?m)^[ ]{2,3}[*\\-+] (.*)$"), list_replace(1)],
|
||||
[regex("(?m)^[ ]{4,5}[*\\-+] (.*)$"), list_replace(2)],
|
||||
[regex("(?m)^[ ]{6,7}[*\\-+] (.*)$"), list_replace(3)],
|
||||
[regex("(?m)^[ ]{8,9}[*\\-+] (.*)$"), list_replace(4)],
|
||||
|
||||
# code blocks, code blocks looks not like code blocks in richtext
|
||||
[regex("```(javascript|python|shell|gdscript)([\\s\\S]*?\n)```"), code_block("$2", true)],
|
||||
[regex("``([\\s\\S]*?)``"), code_block("$1")],
|
||||
[regex("`([\\s\\S]*?)`{1,2}"), code_block("$1")],
|
||||
]
|
||||
|
||||
var _img_replace_regex := RegEx.new()
|
||||
var _image_urls := PackedStringArray()
|
||||
var _on_table_tag := false
|
||||
var _client
|
||||
|
||||
|
||||
func regex(pattern :String) -> RegEx:
|
||||
var regex_ := RegEx.new()
|
||||
var err = regex_.compile(pattern)
|
||||
if err != OK:
|
||||
push_error("error '%s' checked pattern '%s'" % [err, pattern])
|
||||
return null
|
||||
return regex_
|
||||
|
||||
|
||||
func _init():
|
||||
_img_replace_regex.compile("\\[img\\]((.*?))\\[/img\\]")
|
||||
|
||||
|
||||
func set_http_client(client) -> void:
|
||||
_client = client
|
||||
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_PREDELETE:
|
||||
# finally remove_at the downloaded images
|
||||
for image in _image_urls:
|
||||
DirAccess.remove_absolute(image)
|
||||
DirAccess.remove_absolute(image + ".import")
|
||||
|
||||
|
||||
func list_replace(indent :int) -> String:
|
||||
var replace_pattern: String = ("[img=12x12]res://addons/%s/generated/updater/assets/dot2.png[/img]" % config.plugin_name) if indent %2 else ("[img=12x12]res://addons/%s/generated/updater/assets/dot1.png[/img]" % config.plugin_name)
|
||||
replace_pattern += " $1"
|
||||
|
||||
for index in indent:
|
||||
replace_pattern = replace_pattern.insert(0, " ")
|
||||
return replace_pattern
|
||||
|
||||
|
||||
func code_block(replace :String, border :bool = false) -> String:
|
||||
var cb := "[code][color=aqua][font_size=16]%s[/font_size][/color][/code]" % replace
|
||||
if border:
|
||||
return ("[img=1400x14]res://addons/%s/generated/updater/assets/border_top.png[/img]" % config.plugin_name)\
|
||||
+ "[indent]" + cb + "[/indent]"\
|
||||
+ ("[img=1400x14]res://addons/%s/generated/updater/assets/border_bottom.png[/img]\n" % config.plugin_name)
|
||||
return cb
|
||||
|
||||
|
||||
func to_bbcode(input :String) -> String:
|
||||
input = process_tables(input)
|
||||
|
||||
for pattern in md_replace_patterns:
|
||||
var regex_ :RegEx = pattern[0]
|
||||
var bb_replace = pattern[1]
|
||||
if bb_replace is Callable:
|
||||
input = await bb_replace.call(regex_, input)
|
||||
else:
|
||||
input = regex_.sub(input, bb_replace, true)
|
||||
return input + "\n"
|
||||
|
||||
|
||||
func process_tables(input :String) -> String:
|
||||
var bbcode := Array()
|
||||
var lines := Array(input.split("\n"))
|
||||
while not lines.is_empty():
|
||||
if is_table(lines[0]):
|
||||
bbcode.append_array(parse_table(lines))
|
||||
continue
|
||||
bbcode.append(lines.pop_front())
|
||||
return "\n".join(PackedStringArray(bbcode))
|
||||
|
||||
|
||||
class Table:
|
||||
var _columns :int
|
||||
var _rows := Array()
|
||||
|
||||
class Row:
|
||||
var _cells := PackedStringArray()
|
||||
|
||||
func _init(cells :PackedStringArray,columns :int):
|
||||
_cells = cells
|
||||
for i in range(_cells.size(), columns):
|
||||
_cells.append("")
|
||||
|
||||
func to_bbcode(cell_sizes :PackedInt32Array, bold :bool) -> String:
|
||||
var cells := PackedStringArray()
|
||||
for cell_index in _cells.size():
|
||||
var cell :String = _cells[cell_index]
|
||||
if cell.strip_edges() == "--":
|
||||
cell = create_line(cell_sizes[cell_index])
|
||||
if bold:
|
||||
cell = "[b]%s[/b]" % cell
|
||||
cells.append("[cell]%s[/cell]" % cell)
|
||||
return "|".join(cells)
|
||||
|
||||
func create_line(length :int) -> String:
|
||||
var line := ""
|
||||
for i in length:
|
||||
line += "-"
|
||||
return line
|
||||
|
||||
func _init(columns :int):
|
||||
_columns = columns
|
||||
|
||||
func parse_row(line :String) -> bool:
|
||||
# is line containing cells?
|
||||
if line.find("|") == -1:
|
||||
return false
|
||||
_rows.append(Row.new(line.split("|"), _columns))
|
||||
return true
|
||||
|
||||
func calculate_max_cell_sizes() -> PackedInt32Array:
|
||||
var cells_size := PackedInt32Array()
|
||||
for column in _columns:
|
||||
cells_size.append(0)
|
||||
|
||||
for row_index in _rows.size():
|
||||
var row :Row = _rows[row_index]
|
||||
for cell_index in row._cells.size():
|
||||
var cell_size :int = cells_size[cell_index]
|
||||
var size := row._cells[cell_index].length()
|
||||
if size > cell_size:
|
||||
cells_size[cell_index] = size
|
||||
return cells_size
|
||||
|
||||
func to_bbcode() -> PackedStringArray:
|
||||
var cell_sizes := calculate_max_cell_sizes()
|
||||
var bb_code := PackedStringArray()
|
||||
|
||||
bb_code.append("[table=%d]" % _columns)
|
||||
for row_index in _rows.size():
|
||||
bb_code.append(_rows[row_index].to_bbcode(cell_sizes, row_index==0))
|
||||
bb_code.append("[/table]\n")
|
||||
return bb_code
|
||||
|
||||
|
||||
func parse_table(lines :Array) -> PackedStringArray:
|
||||
var line :String = lines[0]
|
||||
var table := Table.new(line.count("|") + 1)
|
||||
while not lines.is_empty():
|
||||
line = lines.pop_front()
|
||||
if not table.parse_row(line):
|
||||
break
|
||||
return table.to_bbcode()
|
||||
|
||||
|
||||
func is_table(line :String) -> bool:
|
||||
return line.find("|") != -1
|
||||
|
||||
|
||||
func open_table(line :String) -> String:
|
||||
_on_table_tag = true
|
||||
return "[table=%d]" % (line.count("|") + 1)
|
||||
|
||||
|
||||
func close_table() -> String:
|
||||
_on_table_tag = false
|
||||
return "[/table]"
|
||||
|
||||
|
||||
func extract_cells(line :String, bold := false) -> String:
|
||||
var cells := ""
|
||||
for cell in line.split("|"):
|
||||
if bold:
|
||||
cell = "[b]%s[/b]" % cell
|
||||
cells += "[cell]%s[/cell]" % cell
|
||||
return cells
|
||||
|
||||
|
||||
func process_image_references(p_regex :RegEx, p_input :String) -> String:
|
||||
# exists references?
|
||||
var matches := p_regex.search_all(p_input)
|
||||
if matches.is_empty():
|
||||
return p_input
|
||||
# collect image references and remove_at it
|
||||
var references := Dictionary()
|
||||
var link_regex := regex("\\[(\\S+)\\]:(\\S+)([ ]\"(.*)\")?")
|
||||
# create copy of original source to replace checked it
|
||||
var input := p_input.replace("\r", "")
|
||||
var extracted_references := p_input.replace("\r", "")
|
||||
for reg_match in link_regex.search_all(input):
|
||||
var line = reg_match.get_string(0) + "\n"
|
||||
var ref = reg_match.get_string(1)
|
||||
#var topl_tip = reg_match.get_string(4)
|
||||
# collect reference and url
|
||||
references[ref] = reg_match.get_string(2)
|
||||
extracted_references = extracted_references.replace(line, "")
|
||||
|
||||
# replace image references by collected url's
|
||||
for reference_key in references.keys():
|
||||
var regex_key := regex("\\](\\[%s\\])" % reference_key)
|
||||
for reg_match in regex_key.search_all(extracted_references):
|
||||
var ref :String = reg_match.get_string(0)
|
||||
var image_url :String = "](%s)" % references.get(reference_key)
|
||||
extracted_references = extracted_references.replace(ref, image_url)
|
||||
return extracted_references
|
||||
|
||||
|
||||
func process_image(p_regex :RegEx, p_input :String) -> String:
|
||||
var to_replace := PackedStringArray()
|
||||
var tool_tips := PackedStringArray()
|
||||
# find all matches
|
||||
var matches := p_regex.search_all(p_input)
|
||||
if matches.is_empty():
|
||||
return p_input
|
||||
for reg_match in matches:
|
||||
# grap the parts to replace and store temporay because a direct replace will distort the offsets
|
||||
to_replace.append(p_input.substr(reg_match.get_start(0), reg_match.get_end(0)))
|
||||
# grap optional tool tips
|
||||
tool_tips.append(reg_match.get_string(5))
|
||||
# finally replace all findings
|
||||
for replace in to_replace:
|
||||
var re := p_regex.sub(replace, "[img]$2[/img]")
|
||||
p_input = p_input.replace(replace, re)
|
||||
return await _process_external_image_resources(p_input)
|
||||
|
||||
|
||||
func _process_external_image_resources(input :String) -> String:
|
||||
DirAccess.make_dir_recursive_absolute(image_download_folder)
|
||||
# scan all img for external resources and download it
|
||||
for value in _img_replace_regex.search_all(input):
|
||||
if value.get_group_count() >= 1:
|
||||
var image_url :String = value.get_string(1)
|
||||
# if not a local resource we need to download it
|
||||
if image_url.begins_with("http"):
|
||||
if OS.is_stdout_verbose():
|
||||
prints("download image:", image_url)
|
||||
var response = await _client.request_image(image_url)
|
||||
if response.code() == 200:
|
||||
var image = Image.new()
|
||||
var error = image.load_png_from_buffer(response.body())
|
||||
if error != OK:
|
||||
prints("Error creating image from response", error)
|
||||
# replace characters where format characters
|
||||
var new_url := image_download_folder + image_url.get_file().replace("_", "-")
|
||||
if new_url.get_extension() != 'png':
|
||||
new_url = new_url + '.png'
|
||||
var err := image.save_png(new_url)
|
||||
if err:
|
||||
push_error("Can't save image to '%s'. Error: %s" % [new_url, error_string(err)])
|
||||
_image_urls.append(new_url)
|
||||
input = input.replace(image_url, new_url)
|
||||
return input
|
Reference in a new issue