WMarkDown/Public/ecma/WMarkDown.ecma.js

972 lines
33 KiB
JavaScript
Raw Normal View History

"use strict";
/**
* @callback wmarkdown_ajax_callback
* @param {?string} response
* @param {!number} status
* @param {!number} state
* @param {!boolean} ok
* @param {!string} message
* @return {void}
*/
/**
* @callback wmarkdown_preload_callback
* @param {?HTMLElement} element
* @param {!boolean} asynchronous
* @param {!integer} error
* @returns {void}
*/
/**
* @callback wmarkdown_element_event_callback
* @param {!HTMLElement} element
* @param {!Event} event
* @returns {void}
*/
/**
* @class
* @constructor
* @param {?string|Object.<string, any|null>} [inputs]
* @returns {void}
* @access public
*/
export const WMarkDown = (function(){
/**
* @constructs WMarkDown
* @param {?string|Object.<string, any|null>} [inputs]
* @returns {void}
* @access private
*/
const WMarkDown = function(inputs){
/** @type {WMarkDown} */
const self = this,
/** @type {Array.<string>} */
dictionary_done = [],
/** @type {Array.<Object.<string, Array.<RegExp, string>|Array.<String>>>} */
dictionary = [],
/** @type {Array.<HTMLElement>} */
root_boxes = [],
/** @type {string} */
hash_alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
/** @type {number} */
hash_length = 11,
/** @type {Array.<string>} */
hashes = [],
/** @type {Array.<Array.<string>>} */
type_dictionary = [
["JavaScript/ECMAScript", "js", "javascript", "ecma", "ecmascript", "node", "nodejs", "typescript", "ts"],
["Python", "python", "py"]
];
/** @type {number|null} */
let thread_inteval = null,
/** @type {boolean} */
dictionary_loaded = false,
/** @type {Array.<string, string, RegExp} */
dictionary_item_mark = ["###@==_", "_==@###", /\#{3}\@\={2}_([0-9]+)_\={2}\@\#{3}/g],
/** @type {number} */
dictionary_z = 500,
/** @type {Array.<string>} */
dictionary_boxes = [],
/** @type {boolean|null} */
check_gui_controls = null;
/**
* @returns {void}
* @access private
*/
const constructor = () => {
if(typeof inputs == "string")
inputs = {dictionary : inputs};
thread_inteval = setInterval(thread_method, 250);
if(inputs.dictionary){
/** @type {number} */
let loaded = 0;
/** @type {Array.<string>} */
const dictionaries = inputs.dictionary instanceof Array ? inputs.dictionary : [inputs.dictionary],
/**
* @returns {void}
*/
end = () => ++ loaded == dictionaries.length && (dictionary_loaded = true);
dictionaries.forEach(url => WMarkDown.get(url, data => {
try{
self.add_to_dictionary(JSON.parse(data));
}catch(exception){
console.error(exception);
};
end();
}));
};
};
/**
* @param {!Array.<Array.<string, string>, Array.<string>, Array.<string>>} data
* @returns {void}
* @access public
*/
this.add_to_dictionary = data => data.forEach(([patterns, definition, links]) => {
/** @type {number|null} */
let i = null;
patterns[0] instanceof Array || (patterns = [patterns]);
patterns.forEach(([pattern, text]) => {
if(!dictionary_done.includes(text)){
if(i === null)
dictionary[i = dictionary.length] = {
patterns : [[WMarkDown.format_pattern(pattern), text]],
definition : typeof definition == "string" ? definition : definition.join(""),
links : links
};
else
dictionary[i].patterns.push([WMarkDown.format_pattern(pattern), text]);
};
});
});
/**
* @returns {string}
* @access public
*/
this.get_hash = () => {
/** @type {string} */
let hash;
/** @type {number} */
const l = hash_alphabet.length;
do{
hash = "";
while((hash += hash_alphabet[Math.random() * l >> 0]).length < hash_length);
}while(
hashes.includes(hash) ||
/^[0-9]/.test(hash) ||
document.querySelector("." + hash + ",#" + hash + ",[name=" + hash + "]")
);
hashes.push(hash);
return hash;
};
/**
* @param {!NodeList} block
* @returns {void}
* @access public
*/
this.format_dictionary = block => block.childNodes.forEach((item, i) => {
/** @type {Array.<Array.<number, HTMLSpanElement>>} */
const blocks = [];
if(item.nodeName == "#text"){
if(item.textContent.trim()){
/** @type {string} */
let html = item.textContent;
/** @type {Array.<number, RegExpMatchArray, string>} */
const items = [];
dictionary.forEach((item, k) => {
item.patterns.forEach(([pattern, text]) => {
html = html.replace(pattern, (...matches) => {
/** @type {number} */
const j = items.length;
items.push([k, matches, text.replace(/\$([0-9])/g, (all, match_i) => {
return matches[match_i] !== null && matches[match_i] !== undefined ? matches[match_i] : "";
})]);
return dictionary_item_mark[0] + j + dictionary_item_mark[1];
});
});
});
if(html != item.textContent){
/** @type {HTMLSpanElement} */
const element = document.createElement("span");
blocks.push([i, element]);
element.innerHTML = html.replace(dictionary_item_mark[2], (_, j) => {
/** @type {Array.<number, RegExpMatchArray, string>} */
const [k, matches, text] = items[Number(j)];
return `<span class="wmd-dictionary-item" data-role="link" data-i="` + k + `" data-hash="` + self.get_hash() + `" data-preload="` + self.preload_hash(span => {
span.addEventListener("click", self.dictionary_over);
}) + `">` + text + `</span>`;
});
};
};
}else if(
!["img", "a", "audio", "canvas", "picture"].includes((item.tagName || item.nodeName).toLowerCase()) &&
!["wmd-excluded", "wmd-code-block", "wmd-code-doc"].some(class_name => item.classList && item.classList.contains(class_name))
){
self.format_dictionary(item);
addEventListener("click", self.dictionary_out);
};
blocks.forEach(([i, element]) => {
block.insertBefore(element, block.childNodes[i]);
block.childNodes[i + 1].remove();
});
});
/**
* @param {!HTMLElement} item
* @returns {HTMLElement|null}
* @access public
*/
this.get_root_box = item => {
/** @type {HTMLElement|null} */
let box = null;
while(item && item.classList && (item = item.parentNode))
item.classList && item.classList.contains("wmarkdown") && (box = item);
return box;
};
/**
* @param {!HTMLUListElement} data_box
* @param {!String} i18n
* @param {!String} text
* @param {!String} action
* @returns {HTMLLIElement}
* @access private
*/
const add_button_data = (data_box, i18n, text, action) => {
/** @type {HTMLLIElement} */
const button = data_box.appendChild(document.createElement("li"));
button.setAttribute("data-i18n", i18n);
button.setAttribute("data-i18n-without", true);
button.setAttribute("title", text);
button.addEventListener("click", action);
button.innerHTML = (`
<span data-icon="` + i18n + `"></span>
<span data-i18n="` + i18n + `">` + text + `</span>
<span class="value">` + text + `</span>
`);
return button;
};
/**
* @param {!HTMLElement} box
* @param {!String} name
* @param {!Array.<String, String, String>} [buttons]
* @returns {void}
* @access private
*/
const set_special_type = (box, name, buttons) => {
while(!box.classList.contains("wmd-code-block") && (box = box.parentNode));
/** @type {HTMLUListElement} */
const data = box.querySelector(".data");
box.querySelector("li[data-i18n=type]>.value").innerHTML = name;
add_button_data(data, "view_switch", "View switch", event => WMarkDown.view_switch(event.target, event));
buttons && buttons.forEach(([i18n, text, action]) => add_button_data(data, i18n, text, action));
};
/**
* @param {!HTMLElement} content
* @param {!String} language
* @returns {HTMLDivElement}
* @access private
*/
const build_special_type = (content, language) => {
content.parentNode.childNodes.forEach(item => item.tagName && item.setAttribute("data-visible", false));
/** @type {HTMLDivElement} */
const box = content.parentNode.appendChild(document.createElement("div"));
box.setAttribute("class", "view");
box.setAttribute("data-visible", true);
set_special_type(content, language);
return box;
};
/**
* @param {HTMLElement} item
* @returns {HTMLElement|null}
* @access public
*/
this.get_anp = item => {
while(!item.classList.contains("anp") && (item = item.parentNode))
if(!item.classList){
item = null;
break;
};
return item;
};
const code_block_format = () => {
document.querySelectorAll(".wmd-code-block[data-processed=false]").forEach(block => {
/** @type {string} */
const language = block.getAttribute("data-type").toLowerCase(),
/** @type {HTMLElement} */
content = block.querySelector(".content"),
/** @type {String} */
type = block.getAttribute("data-type").toLowerCase(),
/** @type {HTMLElement|null} */
anp_item = self.get_anp(block),
/** @type {Boolean} */
dark_mode = (
anp_item ? (anp_item.getAttribute("data-gui-mode") == "dark" || (anp_item.getAttribute("data-gui-mode") == "default" && anp_item.getAttribute("data-dark-mode") == "true")) :
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches);
block.setAttribute("data-processed", true);
mermaid.initialize({
theme : dark_mode ? "dark" : "default",
themeVariables : {fontSize : "16px"}
});
if(["math", "maths", "mathjax"].includes(language)){
build_special_type(content, "MathJax").innerHTML = MathJax.tex2chtml(content.innerText).outerHTML;
MathJax.startup.document.clear();
MathJax.startup.document.updateDocument();
}else if(["mermaid", "mermaidjs", "mermaid_js"].includes(language)){
/** @type {HTMLDivElement} */
const box = build_special_type(content, "Mermaid JS");
mermaid.render(self.get_hash(), content.innerText).then(graph => box.innerHTML = graph.svg);
}else
try{
content.innerHTML = hljs.highlight(content.innerText, {language : (
["wmd-examples", "wmd", "wmarkdown"].includes(language) ? "markdown" :
language)}).value;
type_dictionary.some(alternatives => {
if(alternatives.includes(type)){
block.querySelector("[data-i18n=type]>.value").innerText = alternatives[0];
return true;
};
return false;
});
}catch(exception){};
});
};
const build_dictionary_items = () => {
if(dictionary_loaded){
/** @type {HTMLElement} */
const block = document.querySelector("[data-dictionary-processed=false]");
if(block && [...block.childNodes].slice(-3).some(item => (
item.classList &&
item.classList.contains("wmd-process-and-loaded")
))){
block.querySelectorAll(".wmd-process-and-loaded").forEach(item => {
item.parentNode.hasAttribute("data-dictionary-processed") &&
item.parentNode.setAttribute("data-dictionary-processed", true);
item.remove();
});
self.format_dictionary(block);
};
};
};
const process_lists = () => document.querySelectorAll("[data-list-unprocessed=true]").forEach(list => {
/** @type {HTMLSpanElement} */
const deployer = list.parentNode.insertBefore(document.createElement("span"), list.parentNode.childNodes[0]);
deployer.addEventListener("click", WMarkDown.deploy);
deployer.setAttribute("data-deployed", list.getAttribute("data-deployed"));
deployer.innerHTML = (`
<span data-icon="deploy"></span>
<span data-i18n="deploy">Deploy</span>
`);
list.setAttribute("data-list-unprocessed", false);
});
const autoload_media = body => document.querySelectorAll(".wmd-media[data-status=unloaded]").forEach((item, i) => {
/** @type {DOMRect} */
const bounds = item.getBoundingClientRect();
if(
(bounds.y + bounds.height > -100 && bounds.y < body.offsetHeight + 100) &&
(bounds.x + bounds.width > -100 && bounds.x < body.offsetWidth + 100)
){
/** @type {HTMLElement} */
const main_item = item.querySelector("noscript+*");
item.setAttribute("data-status", "loading");
if(main_item.tagName.toLowerCase() == "img"){
main_item.addEventListener("load", event => WMarkDown.image_loaded(event.target, event));
main_item.addEventListener("error", event => WMarkDown.image_loaded(event.target, event));
item.setAttribute("data-status", "loading");
WMarkDown.image_load(main_item);
};
};
});
/**
* @returns {void}
* @access private
*/
const thread_method = () => {
/** @type {HTMLBodyElement} */
const body = document.querySelector("body");
code_block_format();
build_dictionary_items();
process_lists();
document.querySelectorAll(".wmd-media[data-status=unprocessed]").forEach(item => item.setAttribute("data-status", "unloaded"));
if(body){
autoload_media(body);
document.querySelectorAll(".wmarkdown[data-menu-processed=false]").forEach(block => {
if(block.getAttribute("data-menu-processed") == "true")
return;
/** @type {HTMLUListElement|null} */
let menu = document.querySelector(".wmd-main-menu>ul");
/** @type {Array.<HTMLHeadingElement>} */
const items = block.querySelectorAll("h1,h2,h3,h4,h5,h6");
if(items.length){
/** @type {number} */
let current_level = 0;
if(!menu){
/** @type {HTMLDivElement} */
const button = document.querySelector("header").appendChild(document.createElement("div"));
(menu = (
document.querySelector("[data-cells]") || document.querySelector("body")
).appendChild(document.createElement("nav"))).appendChild(document.createElement("ul"));
menu.setAttribute("class", "wmd-main-menu");
menu.setAttribute("data-visible", false);
menu = menu.childNodes[0];
addEventListener("click", WMarkDown.hide_menu);
button.innerHTML += (`
<span data-i18n="menu" data-i18n-without="true" title="Menu" data-preload="` + self.preload_hash(span => {
span.addEventListener("click", event => WMarkDown.show_menu(event.target, event));
}) + `">
<span data-icon="menu"></span>
<span data-i18n="menu">Menu</span>
</span>
`);
button.setAttribute("class", "wmd-main-menu-button");
button.setAttribute("data-role", "link");
};
current_level = [...items].reduce((lower, item) => {
const level = Number(item.tagName[1]);
return level < lower ? level : lower
}, 6);
items.forEach(item => {
const level = Number(item.tagName[1]),
menu_item = document.createElement("li"),
anchor = menu_item.appendChild(document.createElement("a"));
menu_item.setAttribute("data-level", level);
anchor.innerText = item.innerText;
anchor.setAttribute("href", "#" + item.getAttribute("id"));
anchor.setAttribute("target", "_self");
anchor.setAttribute("title", item.innerText);
if(current_level < level){
const subblock = menu.childNodes[menu.childNodes.length - 1];
if(!(menu = subblock.childNodes[menu.childNodes.length - 1]) || menu.tagName.toLowerCase() != "ul"){
const button_deployer = subblock.insertBefore(document.createElement("span"), subblock.childNodes[0]);
menu = subblock.appendChild(document.createElement("ul"));
button_deployer.setAttribute("data-deployed", false);
button_deployer.addEventListener("click", WMarkDown.deploy);
button_deployer.innerHTML = (`
<span data-icon="deploy"></span>
<span data-i18n="deploy">Deploy</span>
`);
button_deployer.setAttribute("data-role", "link");
};
current_level ++;
}else
while(current_level > level && menu.parentNode.parentNode.tagName.toLowerCase() == "ul"){
current_level --;
menu = menu.parentNode.parentNode;
};
menu.appendChild(menu_item);
});
};
block.setAttribute("data-menu-processed", true);
block.querySelectorAll(".wmarkdown[data-menu-processed=false]").forEach(subblock => subblock.setAttribute("data-menu-processed", true));
window.location.hash && (window.location.href = window.location.hash);
});
};
};
/**
* @param {!HTMLElement} box
* @returns {number}
* @access public
*/
this.get_next_z = box => {
/** @type {number} */
let z = 10;
box.childNodes.forEach(node => {
if(node && node.style){
/** @type {number} */
const item_z = Number(node.style.zIndex) || 0;
item_z >= z && (z = item_z + 1);
};
});
return z;
};
/**
* @param {!MouseEvent} event
* @returns {void}
* @access public
*/
this.dictionary_over = event => setTimeout(() => {
/** @type {string|null} */
const hash = event.target.getAttribute("data-hash");
if(!hash || dictionary_boxes.includes(hash))
return;
/** @type {HTMLBodyElement} */
const body = document.querySelector("body"),
/** @type {HTMLDivElement} */
box = document.querySelector("body").appendChild(document.createElement("div")),
/** @type {DOMRect} */
bounds = event.target.getBoundingClientRect(),
/** @type {number} */
x = bounds.x + (bounds.width / 2),
/** @type {number} */
y = bounds.y + (bounds.height / 2),
/** @type {number} */
i = Number(event.target.getAttribute("data-i")),
/** @type {string} */
own_keys = [];
box.setAttribute("class", "wmd-dictionary-box");
box.setAttribute("data-dictionary-box", hash);
dictionary_boxes.push(hash);
box.innerHTML = (`
<div class="definition">` + dictionary[i].patterns.reduce((definition, [pattern, text]) => (
definition.replace(pattern, () => {
own_keys.push(text.replace(/\$[0-9]/g, ""));
return dictionary_item_mark[0] + (own_keys.length - 1) + dictionary_item_mark[1];
})
), dictionary[i].definition).replace(dictionary_item_mark[2], (all, i) => `<b class="wmd-excluded">` + own_keys[i] + `</b>`) + `</div>
<nav class="links">` + dictionary[i].links.map(link => `<a href="` + link + `" target="_blank" title="` + link + `" data-type="` + (
link.toLowerCase().match(/^(http|ftp|ws)s?\:\/{2}(w+[a-z0-9]\.)?([^\/]+)/i)[3].replace(/[^a-z0-9]+/g, "_")
) + `" style="background-image:url('` + link.match(/^[^\:]+\:\/{2}[^\/]+/)[0] + `/favicon.ico');"></a>`).join("") + `</nav>
`);
// box.style.zIndex = dictionary_z ++;
box.style.zIndex = self.get_next_z(box.parentNode);
if(x > body.offsetWidth / 2)
box.style.right = (body.offsetWidth - x) + "px";
else
box.style.left = x + "px";
if(y > body.offsetHeight / 2)
box.style.bottom = (body.offsetHeight - y) + "px";
else
box.style.top = y + "px";
setTimeout(() => self.format_dictionary(box.querySelector(".definition")), 100);
}, 100);
/**
* @param {!MouseEvent} event
* @returns {void}
* @access public
* @static
*/
this.dictionary_out = event => {
/** @type {string|null} */
let hash = null,
/** @type {HTMLElement} */
item = event.target,
/** @type {string|null} */
box_hash = null,
/** @type {string|null} */
item_hash = null;
while(item.classList){
if(item.classList.contains("wmd-dictionary-box")){
box_hash = hash = item.getAttribute("data-dictionary-box");
break;
}else if(item.classList.contains("wmd-dictionary-item"))
item_hash = hash = item.getAttribute("data-hash");
item = item.parentNode;
};
if(hash){
/** @type {number} */
let i = dictionary_boxes.indexOf(hash);
if(++ i){
/** @type {number} */
let j,
/** @type {number} */
k;
if(box_hash)
[j, k] = (
!dictionary_boxes.includes(item_hash) ? [i, dictionary_boxes.length] :
[i = dictionary_boxes.indexOf(item_hash) + 1, dictionary_boxes.length]);
else{
[j, k] = [0, dictionary_boxes.length];
i = 0;
};
dictionary_boxes.slice(j, k).forEach(hash => {
document.querySelector(".wmd-dictionary-box[data-dictionary-box=" + hash + "]").remove();
dictionary_boxes.splice(i, 1);
});
return;
};
};
document.querySelectorAll(".wmd-dictionary-box").forEach(box => box.remove());
dictionary_boxes = [];
};
/**
* @param {!wmarkdown_preload_callback} callback
* @returns {string}
* @access public
*/
this.preload_hash = callback => {
/** @type {string} */
const hash = self.get_hash();
WMarkDown.preload("[data-preload=" + hash + "]", item => {
item && item.removeAttribute("data-preload");
hashes.splice(hashes.indexOf(hash), 1);
callback(item);
});
return hash;
};
constructor();
};
/**
* @param {!string} pattern
* @returns {RegExp}
* @access public
* @static
*/
WMarkDown.format_pattern = pattern => {
/** @type {RegExpMatchArray} */
const matches = pattern.match(/^\/(.+)\/([a-z]*)$/);
matches || console.log([pattern, matches]);
return new RegExp(matches[1], matches[2]);
};
/**
* @param {!string} url
* @param {!wmarkdown_ajax_callback} callback
* @returns {XMLHttpRequest}
* @access public
* @static
*/
WMarkDown.get = (url, callback) => {
/** @type {boolean} */
let ended = false;
/** @type {XMLHttpRequest} */
const ajax = new XMLHttpRequest(),
/** @type {number} */
time = Date.now(),
/**
* @param {!string} message
* @returns {void}
*/
end = message => !ended && (ended = true) && typeof callback == "function" && callback(
ajax.responseText,
ajax.status,
ajax.readyState,
message == "OK",
message
);
ajax.open("get", url, true);
ajax.timeout = 2000;
ajax.onreadystatechange = () => {
if(ended)
return;
if(ajax.readyState == 4)
end((ajax.status >= 200 && ajax.status < 300) || [301, 302, 304].includes(ajax.status) ? "OK" : "HTTP_ERROR");
else if(Date.now() - time > 2000)
end("FORCED_TIMEOUT");
};
ajax.send(null);
ajax.onerror = () => end("ERROR");
ajax.onabort = () => end("ABORTED");
ajax.ontimeout = () => end("TIMEOUT");
return ajax;
};
/**
* @param {!MouseEvent} event
* @returns {void}
* @access public
* @static
*/
WMarkDown.deploy = event => {
const item = event.target.hasAttribute("data-deployed") ? event.target : event.target.parentNode;
item.setAttribute("data-deployed", item.getAttribute("data-deployed") == "false");
};
/**
* @param {!HTMLImageElement} item
* @param {!ErrorEvent} [event]
* @returns {void}
* @access public
* @static
*/
WMarkDown.image_load = (item, event) => {
/** @type {Array.<string>} */
const images = JSON.parse(atob(item.getAttribute("data-sources"))),
/** @type {number} */
i = Number(item.getAttribute("data-i"));
if(i >= images.length){
item.parentNode.setAttribute("data-status", "error");
return;
};
item.setAttribute("src", images[i]);
item.setAttribute("data-i", i + 1);
};
/**
* @param {!HTMLImageElement} item
* @param {!EventTarget} [event]
* @returns {void}
* @access public
* @static
*/
WMarkDown.image_loaded = (item, event) => {
/** @type {HTMLSpanElement|Null} */
const span_image = item.parentNode.querySelector(".image");
item.parentNode.setAttribute("data-status", "success");
span_image && (span_image.style.backgroundImage = "url('" + item.src + "')");
};
/**
* @param {!EventTarget} [event]
* @returns {void}
* @access public
* @static
*/
WMarkDown.show_menu = event => document.querySelector(".wmd-main-menu").setAttribute("data-visible", true);
/**
* @param {!EventTarget} event
* @returns {void}
* @access public
* @static
*/
WMarkDown.hide_menu = event => {
if(
event.target.parentNode.parentNode.classList &&
event.target.parentNode.parentNode.classList.contains("wmd-main-menu-button")
)
return;
/** @type {HTMLElement|null} */
const main_menu = document.querySelector(".wmd-main-menu");
if(!main_menu || main_menu.getAttribute("data-visible") == "false")
return;
/** @type {HTMLElement} */
let item = event.target;
while(item.tagName.toLowerCase() != "body" && item != main_menu)
item = item.parentNode;
item != main_menu && main_menu.setAttribute("data-visible", false);
};
/**
* @param {!HTMLElement} item
* @param {!MouseEvent} [event]
* @returns {void}
* @access public
* @static
*/
WMarkDown.view_switch = (item, event) => {
/** @type {HTMLDivElement} */
const box = item.parentNode.parentNode.querySelector(".code");
if(!box)
return;
/** @type {HTMLDivElement} */
const view = box.querySelector(".view"),
/** @type {Boolean} */
visible = view.getAttribute("data-visible") != "true";
view.setAttribute("data-visible", visible);
["lines", "content"].forEach(key => box.querySelector("." + key).setAttribute("data-visible", !visible));
};
/**
* @param {!(string|HTMLElement)} selector
* @param {!wmarkdown_preload_callback} callback
* @returns {void}
* @access public
* @static
*/
WMarkDown.preload = (selector, callback) => {
if(typeof callback == "function"){
if(!selector)
callback(null, false, 1 << 1);
else if(selector.tagName || selector.nodeName)
callback(selector, false, 0);
else if(typeof selector == "string"){
/** @type {HTMLElement|null} */
let item = null;
try{
if(item = document.querySelector(selector)){
callback(item, false, 0);
return;
};
}catch(exception){
callback(null, false, 1 << 0);
return;
};
/** @type {number} */
const date = Date.now(),
/** @type {number} */
interval = setInterval(() => {
if(item = document.querySelector(selector)){
clearInterval(interval);
callback(item, true, 0);
}else if(Date.now() - date > 2000){
clearInterval(interval);
callback(null, false, 1 << 2);
};
}, 250);
}else
callback(null, false, 1 << 3);
};
};
return WMarkDown;
})();