You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
RnQ/Distro/Template/colorizer.mjs

696 lines
16 KiB
JavaScript

export function colorize() {
const isEditor = this.tag == "plaintext";
let doStyle;
let doScript;
function doMarkup(tz) {
let tagStart = null; // [node,offset]
let tagScript = false;
let tagScriptType = false;
let tagStyle = false;
let textElement;
let tt;
while (tt = tz.nextToken()) {
if (isEditor && tz.element != textElement) {
textElement = tz.element;
textElement.setAttribute("type", "markup");
}
switch (tt) {
case "tag-start": {
tagStart = tz.tokenRange.start;
const tag = tz.markupTag;
tagScript = tag == "script";
tagStyle = tag == "style";
break;
}
case "tag-head-end": {
(new Range(tagStart, tz.tokenRange.end)).highlight("tag");
if (tagScript) {
tz.push(tagScriptType, "");
doScript(tz, tagScriptType, true);
} else if (tagStyle) {
tz.push("text/css", "");
doStyle(tz, true);
}
break;
}
case "tag-end": tz.tokenRange.highlight("tag"); break;
case "tag-attr": {
if (tagScript && tz.markupAttributeName == "type") tagScriptType = tz.markupAttributeValue;
if (tz.markupAttributeName == "id") tz.tokenRange.highlight("tag-id");
break;
}
}
}
}
doScript = function(tz, typ, embedded = false) {
const KEYWORDS_C = [
"alignas",
"alignof",
"and",
"and_eq",
"asm",
"atomic_cancel",
"atomic_commit",
"atomic_noexcept",
"auto",
"bitand",
"bitor",
"bool",
"break",
"case",
"catch",
"char",
"char16_t",
"char32_t",
"char8_t",
"class",
"compl",
"concept",
"const",
"consteval",
"constexpr",
"constinit",
"const_cast",
"continue",
"co_await",
"co_return",
"co_yield",
"decltype",
"default",
"delete",
"do",
"double",
"dynamic_cast",
"else",
"endif",
"enum",
"explicit",
"export",
"extern",
"float",
"for",
"friend",
"goto",
"if",
"include",
"inline",
"int",
"long",
"mutable",
"namespace",
"new",
"noexcept",
"not",
"not_eq",
"nullptr",
"operator",
"or",
"or_eq",
"private",
"protected",
"public",
"reflexpr",
"register",
"reinterpret_cast",
"requires",
"return",
"short",
"signed",
"sizeof",
"static",
"static_assert",
"static_cast",
"struct",
"switch",
"synchronized",
"template",
"this",
"thread_local",
"throw",
"try",
"typedef",
"typeid",
"typename",
"union",
"unsigned",
"using",
"virtual",
"void",
"volatile",
"wchar_t",
"while",
"xor",
"xor_eq"
];
const LITERALS_C = [ "true", "false", "null" ];
const SUPPORT_C = [
"pow", "sqrt", "min", "max", "swap", "gcd", "toupper", "tolower",
"floor", "ceil", "struct", "union", "enum"
];
const KEYWORDS_PHP = [
"abstract", "and", "as", "break", "callable", "case", "catch", "class",
"clone", "const", "continue", "declare", "default",
"do", "echo", "else", "elseif", "enddeclare", "endfor", "endforeach", "endif",
"endswitch", "endwhile", "extends", "final", "finally", "fn", "for", "foreach",
"function", "global", "goto", "if", "implements", "include", "include_once",
"instanceof", "insteadof", "interface", "match", "namespace", "new", "or", "print",
"private", "protected", "public", "readonly", "require", "require_once", "return",
"static", "switch", "throw", "trait", "try", "use", "var", "while", "xor", "yield",
"yield", "from"
];
const LITERALS_PHP = [ "true", "false", "null" ];
const SUPPORT_PHP = [
"array",
"array_diff",
"array_filter",
"array_keys",
"array_key_exists",
"array_map",
"array_merge",
"array_pop",
"array_push",
"array_reverse",
"array_search",
"array_shift",
"array_slice",
"array_unique",
"array_unshift",
"array_values",
"base64_encode",
"basename",
"call_user_func",
"call_user_func_array",
"ceil",
"chr",
"class_exists",
"count",
"date",
"define",
"defined",
"die",
"dir",
"dirname",
"each",
"echo",
"empty",
"end",
"error_reporting",
"eval",
"exit",
"explode",
"extension_loaded",
"fclose",
"file",
"file_exists",
"file_get_contents",
"file_put_contents",
"floor",
"fmod",
"fopen",
"function_exists",
"func_get_args",
"fwrite",
"gettype",
"get_class",
"header",
"htmlspecialchars",
"idate",
"implode",
"ini_get",
"ini_set",
"intval",
"in_a",
"in_array",
"isset",
"is_array",
"is_bool",
"is_callable",
"is_dir",
"is_file",
"is_int",
"is_null",
"is_numeric",
"is_object",
"is_string",
"json_decode",
"json_encode",
"ksort",
"link",
"list",
"log",
"ltrim",
"mail",
"max",
"md5",
"method_exists",
"microtime",
"min",
"mkdir",
"ob_start",
"ord",
"parse_url",
"pos",
"preg_match",
"preg_match_all",
"preg_replace",
"preg_split",
"printf",
"print_r",
"realpath",
"reset",
"round",
"rtrim",
"serialize",
"sort",
"sprintf",
"stripos",
"strlen",
"strpos",
"strrpos",
"strtolower",
"strtotime",
"strtoupper",
"strtr",
"str_repeat",
"str_replace",
"substr",
"time",
"trim",
"ucfirst",
"uniqid",
"unlink",
"unserialize",
"unset",
"urlencode",
"version_compare",
];
const KEYWORDS_JS = [
"abstract",
"arguments",
"assert",
"async",
"await",
"boolean",
"break",
"byte",
"case",
"catch",
"char",
"class",
"const",
"continue",
"debug",
"debugger",
"default",
"delete",
"do",
"double",
"else",
"enum",
"eval",
"export",
"extends",
"final",
"finally",
"float",
"for",
"function",
"get",
"goto",
"if",
"implements",
"import",
"in",
"include",
"instanceof",
"int",
"interface",
"let",
"like",
"long",
"namespace",
"native",
"new",
"of",
"otherwise",
"package",
"private",
"property",
"protected",
"public",
"return",
"set",
"short",
"static",
"super",
"switch",
"synchronized",
"this",
"throw",
"throws",
"transient",
"try",
"type",
"typeof",
"var",
"void",
"volatile",
"while",
"with",
"yield",
"__file__",
"__line__",
"__trace__",
];
const LITERALS_JS = [ "true", "false", "null", "undefined", "Infinity", "NaN" ];
const SUPPORT_JS = [
"addEventListener",
"appendChild",
"Array",
"ArrayBuffer",
"AsyncFunction",
"AsyncGenerator",
"AsyncGeneratorFunction",
"AsyncIterator",
"Atomics",
"BigInt",
"Boolean",
"clearInterval",
"clearTimeout",
"createElement",
"DataView",
"Date",
"decodeURI",
"decodeURIComponent",
"dispatchEvent",
"document",
"encodeURI",
"encodeURIComponent",
"escape",
"eval",
"extends",
"fetch",
"Function",
"Generator",
"GeneratorFunction",
"getAttribute",
"getComputedStyle",
"getElementById",
"getElementsByClassName",
"getElementsByTagName",
"getSelection",
"isFinite",
"isNaN",
"Iterator",
"JSON",
"Map",
"Math",
"Number",
"Object",
"parseFloat",
"parseInt",
"Promise",
"promise",
"Proxy",
"push",
"Reflect",
"RegExp",
"removeChild",
"removeEventListener",
"replace",
"Set",
"setAttribute",
"setInterval",
"setTimeout",
"SharedArrayBuffer",
"String",
"Symbol",
"unescape",
"WeakMap",
"WeakSet",
"window"
];
const KEYWORDS_DELPHI = [
"end" , "array" , "begin" , "dispinterface" ,
"finalization" , "goto" , "implementation" , "inherited" ,
"initialization" , "label" , "of" , "packed" ,
"set" , "type" , "uses" , "with" ,
"constructor" , "destructor" , "function" , "operator" ,
"procedure" , "property" , "const" , "out" ,
"threadvar" , "var" , "for" , "to" ,
"downto" , "while" , "repeat" , "until" ,
"do" , "if" , "then" , "case" ,
"else" , "try" , "finally" , "except" ,
"on" , "raise" , "at" , "class" ,
"interface" , "object" , "record" , "cdecl" ,
"pascal" , "register" , "safecall" , "stdcall" ,
"winapi" , "library" , "package" , "program" ,
"unit" , "absolute" , "abstract" , "assembler" ,
"delayed" , "deprecated" , "dispid" , "dynamic" ,
"experimental" , "export" , "external" , "final" ,
"forward" , "implements" , "inline" , "name" ,
"message" , "overload" , "override" , "packed" ,
"platform" , "readonly" , "reintroduce" , "static" ,
"unsafe" , "varargs" , "virtual" , "writeonly" ,
"helper" , "reference" , "sealed" , "contains" ,
"requires" , "far" , "near" , "resident" ,
"private" , "protected" , "public" , "published" ,
"strict" , "default" , "index" , "node" ,
"fault" , "read" , "stored" , "write" ,
"and" , "as" , "div" , "in" ,
"is" , "mod" , "not" , "or" ,
"shr" , "shl" , "xor" ,
// types
"byte" , "integer" , "cardinal" , "pointer" ,
"single" , "double" , "extended" , "comp" ,
"currency" , "bool" , "dword" , "ulong" ,
"hdc" , "hresult" , "hwnd" , "word" ,
"longword" , "longbool" , "boolean" , "shortint" ,
"smallint" , "longint" , "nativeint" , "nativeuint" ,
"uint8" , "uint16" , "uint32" , "uint64" ,
"uint128" , "int8" , "int16" , "int32" ,
"int64" , "int128" , "ansichar" , "ansistring" ,
"real48" , "real" , "widechar" , "widestring" ,
"unicodestring" , "shortstring" , "rawbytestring" , "variant" ,
"olevariant"
];
const LITERALS_DELPHI = [ "true", "false", "nil" ];
const SUPPORT_DELPHI = [ "result", "self", "assert" ];
const LITERALS_JSON = [ "true", "false", "null" ];
const KEYWORDS_COMMON = [
"begin" , "end" , "and" , "array" ,
"as" , "bool" , "boolean" , "break" ,
"case" , "catch" , "char" , "class" ,
"const" , "continue" , "def" , "delete" ,
"do" , "double" , "echo" , "else" ,
"elseif" , "exit" , "extends" , "except" ,
"finally" , "float" , "for" , "foreach" ,
"function" , "global" , "if" , "import" ,
"int" , "integer" , "long" , "new" ,
"object" , "or" , "print" , "private" ,
"protected" , "public" , "return" , "self" ,
"string" , "struct" , "static" , "switch" ,
"then" , "this" , "throw" , "try" ,
"signed" , "unsigned" , "var" , "void" ,
"while" , "procedure"
];
var KEYWORDS = [];
var LITERALS = [];
var SUPPORT = [];
if (typ == "text/x-pascal") {
KEYWORDS = KEYWORDS_DELPHI;
LITERALS = LITERALS_DELPHI;
SUPPORT = SUPPORT_DELPHI;
} else if (typ == "text/x-c") {
KEYWORDS = KEYWORDS_C;
LITERALS = LITERALS_C;
SUPPORT = SUPPORT_C;
} else if (typ == "application/x-php") {
KEYWORDS = KEYWORDS_PHP;
LITERALS = LITERALS_PHP;
SUPPORT = SUPPORT_PHP;
} else if (typ == "text/javascript") {
KEYWORDS = KEYWORDS_JS;
LITERALS = LITERALS_JS;
SUPPORT = SUPPORT_JS;
} else if (typ == "application/json") {
LITERALS = LITERALS_JSON;
} else {
LITERALS = LITERALS_JSON;
KEYWORDS = KEYWORDS_COMMON;
SUPPORT = SUPPORT_DELPHI;
}
let firstElement = null;
let lastElement = null;
let tt;
let isVal = false;
let isInArr = false;
loop: while (tt = tz.nextToken()) {
var el = tz.element;
firstElement = firstElement || el;
lastElement = el;
switch (tt) {
case "number": tz.tokenRange.highlight("number"); break;
case "number-unit": tz.tokenRange.highlight("number-unit"); break;
case "string": {
if ((typ == "application/json" && (isVal || isInArr)) || typ !== "application/json")
tz.tokenRange.highlight("string");
break;
}
case "operator": {
isVal = tz.tokenValue == ":";
if (tz.tokenValue == "[")
isInArr = true;
else if (tz.tokenValue == "]" || tz.tokenValue == "{")
isInArr = false;
break;
}
case "name": {
const val = tz.tokenValue;
if (val[0] == '#' || val[0] == '$')
tz.tokenRange.highlight("symbol");
else if (KEYWORDS.includes(val))
tz.tokenRange.highlight("keyword");
else if (LITERALS.includes(val))
tz.tokenRange.highlight("literal");
else if (SUPPORT.includes(val))
tz.tokenRange.highlight("support");
break;
}
case "comment": tz.tokenRange.highlight("comment"); break;
case "island-end":
tz.pop();
break loop;
}
if (tt !== "operator") isVal = false;
}
if (isEditor && embedded) {
for (var el = firstElement; el; el = el.nextElementSibling) {
if (el == lastElement)
break;
el.setAttribute("type", "script");
}
}
};
doStyle = function(tz, embedded = false) {
const KEYWORDS =
{
"rgb": true, "rgba": true, "url": true, "var": true,
"@import": true, "@media": true, "@set": true, "@const": true,
"@mixin": true, "@supports": true,
};
const LITERALS = {"inherit": true};
let firstElement;
let lastElement;
let tt;
loop: while (tt = tz.nextToken()) {
var el = tz.element;
if (!firstElement) firstElement = el;
lastElement = el;
switch (tt) {
case "number": tz.tokenRange.highlight("number"); break;
case "number-unit": tz.tokenRange.highlight("number-unit"); break;
case "string": tz.tokenRange.highlight("string"); break;
case "name":
{
const val = tz.tokenValue;
if (val[0] == "#")
tz.tokenRange.highlight("literal");
else if (KEYWORDS[val])
tz.tokenRange.highlight("keyword");
else if (LITERALS[val])
tz.tokenRange.highlight("literal");
break;
}
case "comment": tz.tokenRange.highlight("comment"); break;
case "island-end":
// got
tz.pop(); // pop tokenizer layer
break loop;
}
}
if (isEditor && embedded) {
for (var el = firstElement; el; el = el.nextElementSibling) {
if (el == lastElement) break;
el.setAttribute("type", "style");
}
}
};
const me = this;
function doIt() {
if (me.attributes["nosyntax"] === "true") return;
var syntax = me.attributes["type"];
if (syntax === "htm" || syntax === "html") syntax = "text/html";
else if (syntax === "js") syntax = "text/javascript";
else if (syntax === "css") syntax = "text/css";
else if (syntax === "json") syntax = "application/json";
else if (syntax === "php") syntax = "application/x-php";
else if (syntax === "delphi" || syntax === "pas") syntax = "text/x-pascal";
else if (syntax === "c") syntax = "text/x-c";
else if (syntax === "") syntax = "text/script";
if (!syntax.includes("/")) syntax = "text/plain";
const tz = new Tokenizer(me, syntax);
if (syntax.endsWith("/htm") || syntax.endsWith("/html") || syntax.endsWith("/svg") || syntax.endsWith("/xml"))
doMarkup(tz);
else if (syntax.endsWith("/css"))
doStyle(tz);
else
doScript(tz, syntax);
}
doIt();
Object.defineProperty(me, "value", {
get: function() {
return me.state.value;
},
set: function(value) {
me.state.value = value;
doIt();
},
});
Object.defineProperty(me, "colorize", {
value: function() { doIt(); },
});
if (isEditor) {
this.loadContent = function(text) {
this.plaintext.content = text;
doIt();
};
this.on("change", function() {
this.timer(40, doIt);
});
}
}