2026-02-26 22:58:02 -06:00
|
|
|
|
extends Object
|
|
|
|
|
|
class_name Util
|
|
|
|
|
|
|
|
|
|
|
|
#region HTML RegEx and UnEscape HTML
|
2026-03-09 18:41:40 -05:00
|
|
|
|
static var _img_re: RegEx:
|
|
|
|
|
|
get():
|
|
|
|
|
|
if not _img_re: _img_re = RegEx.create_from_string(r'<img[^>]*src=["\']([^"\']+)["\'][^>]*/?>', true)
|
|
|
|
|
|
return _img_re
|
|
|
|
|
|
|
|
|
|
|
|
static var _h2_open_re: RegEx:
|
|
|
|
|
|
get():
|
|
|
|
|
|
if not _h2_open_re: _h2_open_re = RegEx.create_from_string(r'<h2[^>]*>', true)
|
|
|
|
|
|
return _h2_open_re
|
|
|
|
|
|
|
|
|
|
|
|
static var _p_open_re: RegEx:
|
|
|
|
|
|
get():
|
|
|
|
|
|
if not _p_open_re: _p_open_re = RegEx.create_from_string(r'<p[^>]*>', true)
|
|
|
|
|
|
return _p_open_re
|
|
|
|
|
|
|
|
|
|
|
|
static var _ul_re: RegEx:
|
|
|
|
|
|
get():
|
|
|
|
|
|
if not _ul_re: _ul_re = RegEx.create_from_string(r'</?ul[^>]*>', true)
|
|
|
|
|
|
return _ul_re
|
|
|
|
|
|
|
|
|
|
|
|
static var _div_re: RegEx:
|
|
|
|
|
|
get():
|
|
|
|
|
|
if not _div_re: _div_re = RegEx.create_from_string(r'</?div[^>]*>', true)
|
|
|
|
|
|
return _div_re
|
|
|
|
|
|
|
|
|
|
|
|
static var _any_tag_re: RegEx:
|
|
|
|
|
|
get():
|
|
|
|
|
|
if not _any_tag_re: _any_tag_re = RegEx.create_from_string(r'<[^>]*>', true)
|
|
|
|
|
|
return _any_tag_re
|
|
|
|
|
|
|
|
|
|
|
|
static var _html_replacements: Dictionary[String, String]:
|
|
|
|
|
|
get():
|
|
|
|
|
|
if not _html_replacements: _html_replacements = {
|
|
|
|
|
|
"<": "<", ">": ">", "&": "&", """: "\"",
|
|
|
|
|
|
"'": "'", "'": "'", "/": "/", "`": "`",
|
|
|
|
|
|
" ": " ", "©": "©", "®": "®", "€": "€",
|
|
|
|
|
|
"£": "£", "¥": "¥", "–": "–", "—": "—",
|
|
|
|
|
|
"‘": "‘", "’": "’", "“": "“", "”": "”",
|
|
|
|
|
|
"…": "…", "•": "•"
|
|
|
|
|
|
}
|
|
|
|
|
|
return _html_replacements
|
2026-02-26 22:58:02 -06:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
static func convert_html_to_bbcode(html: String) -> String:
|
|
|
|
|
|
# NOTE: Unescape HTML entities
|
|
|
|
|
|
var bb = html_unescape(html)
|
|
|
|
|
|
|
|
|
|
|
|
# NOTE: Replace <img ... src="..."> with [img]...[/img]
|
|
|
|
|
|
bb = _img_re.sub(bb, "[img]$1[/img]", true)
|
|
|
|
|
|
|
2026-03-06 21:20:45 -06:00
|
|
|
|
# NOTE: Replace <h2 ...>...</h2> tags, with [b]...[/b]
|
2026-02-26 22:58:02 -06:00
|
|
|
|
bb = _h2_open_re.sub(bb, "[b]", true)
|
|
|
|
|
|
bb = bb.replace("</h2>", "[/b]\n")
|
|
|
|
|
|
|
2026-03-06 21:20:45 -06:00
|
|
|
|
# NOTE: Remove <p> tags, convert </p> to new line
|
|
|
|
|
|
bb = _p_open_re.sub(bb, "", true)
|
|
|
|
|
|
bb = bb.replace("</p>", "\n")
|
|
|
|
|
|
|
2026-02-26 22:58:02 -06:00
|
|
|
|
# NOTE: List Items: <li> -> •, </li> -> newline
|
|
|
|
|
|
bb = bb.replace("<li>", "• ")
|
|
|
|
|
|
bb = bb.replace("</li>", "\n")
|
|
|
|
|
|
|
|
|
|
|
|
# NOTE: Remove <ul> and </ul>
|
|
|
|
|
|
bb = _ul_re.sub(bb, "", true)
|
|
|
|
|
|
|
|
|
|
|
|
# NOTE: Remove <div ...> and </div>
|
|
|
|
|
|
bb = _div_re.sub(bb, "", true)
|
|
|
|
|
|
|
|
|
|
|
|
# NOTE: Remove any remaining tags
|
|
|
|
|
|
bb = _any_tag_re.sub(bb, "", true)
|
|
|
|
|
|
|
|
|
|
|
|
# NOTE: Trim and normalize spacing
|
|
|
|
|
|
bb = bb.strip_edges()
|
|
|
|
|
|
bb = bb.replace("\n\n\n", "\n\n")
|
|
|
|
|
|
|
|
|
|
|
|
return bb
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static func html_unescape(text: String) -> String:
|
2026-03-09 18:41:40 -05:00
|
|
|
|
var new_text: String = String(text)
|
|
|
|
|
|
for key: String in _html_replacements.keys():
|
|
|
|
|
|
new_text = new_text.replace(key, _html_replacements[key])
|
|
|
|
|
|
return new_text
|
2026-02-26 22:58:02 -06:00
|
|
|
|
|
|
|
|
|
|
static func resize_img_to_max_dim(img: Image, max_dim: int) -> Image:
|
|
|
|
|
|
var width: int
|
|
|
|
|
|
var height: int
|
|
|
|
|
|
var _is_taller: bool = img.get_size().x < img.get_size().y
|
|
|
|
|
|
if _is_taller:
|
|
|
|
|
|
@warning_ignore("integer_division")
|
|
|
|
|
|
width = (max_dim * img.get_size().x) / img.get_size().y
|
|
|
|
|
|
height = max_dim
|
|
|
|
|
|
else:
|
|
|
|
|
|
width = max_dim
|
|
|
|
|
|
@warning_ignore("integer_division")
|
|
|
|
|
|
height = (max_dim * img.get_size().y) / img.get_size().x
|
|
|
|
|
|
|
|
|
|
|
|
var resized_image: Image = img.duplicate()
|
|
|
|
|
|
resized_image.resize(width, height)
|
|
|
|
|
|
return resized_image
|
|
|
|
|
|
|
|
|
|
|
|
#region Time Parsers
|
|
|
|
|
|
const WEEKDAYS = ["Sun","Mon","Tue", "Wed", "Thu", "Fri", "Sat"]
|
|
|
|
|
|
const MONTHS_SHORT = ["Jan","Feb","Mar","Apr","May", "Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
|
|
|
|
|
|
const FORMAT_STRING_TIME = "%d:%02d:%02d"
|
|
|
|
|
|
const FORMAT_STRING_DATE = "{day} {month} {year}"
|
|
|
|
|
|
const FORMAT_STRING_DATE_FILE = "{year}_{month}_{day}"
|
|
|
|
|
|
const FORMAT_STRING_TIME_FILE = "%d_%02d_%02d"
|
|
|
|
|
|
|
|
|
|
|
|
static func unix_to_string(
|
|
|
|
|
|
unix: float,
|
|
|
|
|
|
include_weekday := true,
|
|
|
|
|
|
include_time := true,
|
|
|
|
|
|
dz_enabled := true) -> String:
|
|
|
|
|
|
if !unix: return "Never"
|
|
|
|
|
|
if dz_enabled:
|
|
|
|
|
|
var tz_offset_usec = Time.get_time_zone_from_system().bias * 60
|
|
|
|
|
|
unix += tz_offset_usec
|
|
|
|
|
|
var dict: Dictionary = Time.get_datetime_dict_from_unix_time(floori(unix))
|
|
|
|
|
|
dict.weekday = WEEKDAYS[dict.weekday]
|
|
|
|
|
|
dict.month = MONTHS_SHORT[dict.month-1]
|
|
|
|
|
|
|
|
|
|
|
|
var format_string: String = FORMAT_STRING_DATE
|
|
|
|
|
|
if include_weekday:
|
|
|
|
|
|
format_string = "{weekday} " + format_string
|
|
|
|
|
|
if include_time:
|
|
|
|
|
|
var time_string: String = (FORMAT_STRING_TIME % [dict.hour, dict.minute, dict.second])
|
|
|
|
|
|
format_string = format_string + " - " + time_string
|
|
|
|
|
|
return format_string.format(dict)
|
|
|
|
|
|
|
|
|
|
|
|
static func unix_to_string_filepath(unix: float, include_time := false) -> String:
|
|
|
|
|
|
if !unix: return "Never"
|
|
|
|
|
|
|
|
|
|
|
|
var tz_offset_usec = Time.get_time_zone_from_system().bias * 60
|
|
|
|
|
|
unix += tz_offset_usec
|
|
|
|
|
|
var dict: Dictionary = Time.get_datetime_dict_from_unix_time(floori(unix))
|
|
|
|
|
|
dict.weekday = WEEKDAYS[dict.weekday]
|
|
|
|
|
|
dict.month = MONTHS_SHORT[dict.month-1]
|
|
|
|
|
|
|
|
|
|
|
|
var format_string: String = FORMAT_STRING_DATE_FILE.format(dict)
|
|
|
|
|
|
|
|
|
|
|
|
if include_time:
|
|
|
|
|
|
var time_string: String = (FORMAT_STRING_TIME_FILE % [dict.hour, dict.minute, dict.second])
|
|
|
|
|
|
format_string += "_" + time_string
|
|
|
|
|
|
|
|
|
|
|
|
return format_string
|
|
|
|
|
|
#endregion
|