feat(py-wasm): Attributes and Components module done.

This commit is contained in:
KyMAN 2025-03-26 14:59:24 +01:00
parent f598ed3217
commit 8438243782
9 changed files with 521 additions and 27 deletions

View File

@ -33,6 +33,15 @@
* @returns {void}
*/
/**
* @callback python_wasm_preload_callback
* @param {HTMLElement|null} item
* @param {boolean} asynchronous
* @param {number} error
* @param {Array.<string>} error_messages
* @returns {void}
*/
/**
* @class
* @constructor
@ -92,7 +101,9 @@ export const PythonWASM = (function(){
/** @type {string} */
gui_mode = PythonWASM.get_value("gui_mode", inputs, "default"),
/** @type {number} */
ajax_timeout = PythonWASM.get_value("ajax_timeout", inputs, 2000);
ajax_timeout = PythonWASM.get_value(["ajax_timeout", "timeout"], inputs, 2000),
/** @type {number} */
preload_timeout = PythonWASM.get_value(["preload_timeout", "timeout"], inputs, 2000);
/** @type {PyodideAPI|null} */
this.pyodide = null;
@ -133,10 +144,22 @@ export const PythonWASM = (function(){
null)).filter(file => file);
(async function(){
self._print("info", "Building the Pyodide module...");
self.pyodide = await loadPyodide();
self.pyodide.globals.set("load_file", self.load_file);
self.pyodide.globals.set("synchronous_post", self.synchronous_post);
["load_file", "synchronous_post", "preload_html_item"].forEach(parameters => {
/** @type {[string|Array.<string>, string]} */
const [names, key] = PythonWASM.is_array(parameters) ? parameters : [parameters, parameters];
PythonWASM.get_keys(names).forEach(name => {
self._print("info", "Implementing the '{key}' action interface.", {key : name});
self.pyodide.globals.set(name, self[key]);
});
});
self._print("info", "Loading the Python files...");
self.pyodide.runPython(`
from pyodide.http import pyfetch
@ -149,6 +172,7 @@ export const PythonWASM = (function(){
});
PythonWASM.execute(callback, self);
});
})();
};
@ -319,12 +343,13 @@ export const PythonWASM = (function(){
/**
* @param {!string} url
* @param {?python_wasm_ajax_callback} [callback = null]
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @param {!number} [options = 0]
* @returns {XMLHttpRequest}
* @access public
*/
this.load_file = (url, callback = null, options = 0) => {
// console.log(url);
this.load_file = (url, callback = null, inputs = null, options = 0) => {
inputs = PythonWASM.get_default_inputs(inputs);
/** @type {boolean} */
let ended = false;
@ -332,23 +357,24 @@ export const PythonWASM = (function(){
const ajax = new XMLHttpRequest(),
/** @type {number} */
date = Date.now(),
/** @type {number} */
timeout = PythonWASM.get_value(["ajax_timeout", "timeout"], inputs, ajax_timeout),
/**
* @param {!string} message
* @returns {void}
*/
end = message => {
// !ended && console.log([callback, ajax.responseText, ajax.status, ajax.readyState, message == "OK", message]);
!ended && (ended = true) && PythonWASM.execute(callback, ajax.responseText, ajax.status, ajax.readyState, message == "OK", message);
};
ajax.open("get", url, true);
ajax.timeout = ajax_timeout;
ajax.timeout = timeout;
ajax.onreadystatechange = () => {
if(ended)
return;
if(ajax.readyState == 4)
end([301, 302, 304].includes(ajax.status) || (ajax.status >= 200 && ajax.status < 300) ? "OK" : "HTTP_ERROR");
else if(Date.now() - date > ajax_timeout)
else if(Date.now() - date > timeout)
end("FORCED_TIMOUT");
};
ajax.send(null);
@ -364,11 +390,13 @@ export const PythonWASM = (function(){
* @param {!string} url
* @param {?Object.<string, any|null>} [variables = null]
* @param {?python_wasm_ajax_callback} [callback = null]
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @param {!number} [options = 0]
* @returns {XMLHttpRequest}
* @access public
*/
this.synchronous_post = (url, variables = null, callback = null, options = 0) => {
this.synchronous_post = (url, variables = null, callback = null, inputs = null, options = 0) => {
inputs = PythonWASM.get_default_inputs(inputs);
/** @type {boolean} */
let ended = false,
@ -378,6 +406,8 @@ export const PythonWASM = (function(){
const ajax = new XMLHttpRequest(),
/** @type {number} */
date = Date.now(),
/** @type {number} */
timeout = PythonWASM.get_value(["ajax_timeout", "timeout"], inputs, ajax_timeout),
/**
* @param {!string} message
* @returns {void}
@ -391,14 +421,14 @@ export const PythonWASM = (function(){
variables_uri += (variables_uri ? "&" : "") + encodeURIComponent(key) + "=" + encodeURIComponent(variables[key]);
ajax.open("get", url, true);
ajax.timeout = ajax_timeout;
ajax.timeout = timeout;
ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded;charset=UTF-8")
ajax.onreadystatechange = () => {
if(ended)
return;
if(ajax.readyState == 4)
end([301, 302, 304].includes(ajax.status) || (ajax.status >= 200 && ajax.status < 300) ? "OK" : "HTTP_ERROR");
else if(Date.now() - date > ajax_timeout)
else if(Date.now() - date > timeout)
end("FORCED_TIMOUT");
};
ajax.send(variables_uri);
@ -410,6 +440,72 @@ export const PythonWASM = (function(){
return ajax;
};
/**
* @param {!(string|HTMLElement)} selector
* @param {?python_wasm_preload_callback} [callback = null]
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @param {!number} [options = 0]
* @returns {Array.<HTMLElement|null, number>}
* @access public
*/
this.preload_html_item = (selector, callback = null, inputs = null, options = 0) => {
inputs = PythonWASM.get_default_inputs(inputs);
/** @type {boolean} */
let is_html_item = selector && (selector.tagName || selector.nodeName),
/** @type {HTMLElement|null} */
item = is_html_item ? selector : null,
/** @type {number} */
error = (
((
callback === undefined ? 1 << 0 :
callback === null ? 1 << 1 :
typeof callback != "function" ? 1 << 2 :
0) << 0) |
((
selector === undefined ? 1 << 0 :
selector === null ? 1 << 1 :
typeof selector != "string" && !is_html_item ? 1 << 2 :
0) << 3) |
0) << 1,
/** @type {boolean} */
done = is_html_item;
if(!done && !error){
try{
done = !!(item = document.querySelector(selector));
}catch(exception){
error |= 1 << 7;
done = true;
};
if(!done){
/** @type {number} */
const date = Date.now(),
/** @type {number} */
timeout = Utils.get_value(["preload_timeout", "timeout"], inputs, preload_timeout),
/** @type {number} */
interval = setInterval(() => {
if(item = document.querySelector(selector)){
clearInterval(interval);
PythonWASM.execute(callback, item, true, error, PythonWASM.PRELOAD_ITEM_HTML_ERROR_MESSAGES);
}else if(Date.now() - date > timeout){
clearInterval(interval);
PythonWASM.execute(callback, item, true, error |= 1 << 8, PythonWASM.PRELOAD_ITEM_HTML_ERROR_MESSAGES)
};
}, 1000 / 24);
};
};
done && PythonWASM.execute(callback, false, error, PythonWASM.PRELOAD_ITEM_HTML_ERROR_MESSAGES);
return error;
};
constructor();
};
@ -428,6 +524,7 @@ export const PythonWASM = (function(){
["Modules/ErrorsManager.py", "https://errorsmanager." + PythonWASM.DOMAIN + "/py/ErrorsManager.py"],
"/py/common.py",
"/py/Abstracts/AnPMap.py",
"/py/Abstracts/PyodideMap.py",
"/py/Utils/Patterns.py",
"/py/Utils/Options.py",
"/py/Utils/Check.py",
@ -446,9 +543,24 @@ export const PythonWASM = (function(){
"/py/Managers/RandomKeys.py",
"/py/Models/Thread.py",
"/py/Managers/Threads.py",
"/py/Application/Attributes.py",
"/py/Application/Components.py",
// "/py/run.py"
];
/** @type {Array.<string>} */
PythonWASM.PRELOAD_ITEM_HTML_ERROR_MESSAGES = [
"exception",
"callback_undefined",
"callback_null",
"callback_not_function",
"selector_undefined",
"selector_null",
"selector_bad_type",
"selector_bad_format",
"timeout"
];
/**
* @param {?any} item
* @returns {boolean}
@ -497,6 +609,22 @@ export const PythonWASM = (function(){
*/
PythonWASM.is_function = item => typeof item == "function";
/**
* @param {?any} item
* @returns {boolean}
* @access public
* @static
*/
PythonWASM.is_integer = item => typeof item == "number" && item >> 0 == item;
/**
* @param {?any} item
* @returns {boolean}
* @access public
* @static
*/
PythonWASM.is_json_item = item => PythonWASM.is_dictionary(item) || PythonWASM.is_array(item);
/**
* @param {?any} item
* @returns {Array.<any|null>}
@ -712,7 +840,10 @@ export const PythonWASM = (function(){
{object_name}:AnP = AnP({
"pyodide_javascript_interface_methods" : {
"load_file" : load_file
"load_file" : load_file,
"post" : synchronous_post,
"post_synchronous" : synchronous_post,
"preload_html_item" : preload_html_item
}
})
@ -720,5 +851,16 @@ export const PythonWASM = (function(){
object_name : "anp"
}, inputs]);
/**
* @param {?(Object.<string, any|null>|Array.<any|null>)} inputs
* @returns {Object.<string, any|null>|Array.<any|null>|null}
* @access public
* @static
*/
PythonWASM.get_default_inputs = inputs => (
PythonWASM.is_integer(inputs) ? {timeout : inputs} :
PythonWASM.is_json_item(inputs) ? inputs :
null);
return PythonWASM;
})();

View File

@ -4,10 +4,47 @@
from Modules.ErrorsManager import ErrorsManager
from typing import Self, Optional, Any, Callable
from Utils.Options import Options
from re import Match as REMatch
from typing import NewType
from sys import platform as SYS_PLATFORM
AnP = NewType("AnP", Any)
is_emcripten:bool = SYS_PLATFORM == "emscripten"
if is_emcripten:
class ClassList:
def contains(self:Self, kebab:str) -> bool:pass
def add(self:Self, kebab:str) -> None:pass
def remove(self:Self, kebab:str) -> None:pass
class Style:pass
class DOM:
def querySelector(selector:str) -> Self|None:pass
def querySelectorAll(selector:str) -> list[Self]:pass
def remove() -> None:pass
def hasAttribute(name:str) -> bool:pass
def getAttribute(name:str) -> str:pass
def setAttribute(name:str, value:Any|None) -> None:pass
class HTMLElement(DOM, Style):
def __init__(self):
self.style:Style = None
class HTMLBodyElement(HTMLElement):pass
class BaseAbstract:
def __init__(self:Self, anp:AnP, key:str, inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None]] = None) -> None:
@ -34,18 +71,22 @@ class BaseAbstract:
def is_started(self:Self) -> bool:pass
class PyodideJavaScriptInterface(BaseAbstract):
if is_emcripten:
def __init__(self:Self, anp:AnP, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> None:
self.load_file:Callable[[str, Callable[[str|None, int, int, bool, str], str|None], int], tuple[str|None, int, bool, int]] = None
self.post:Callable[[str, Callable[[str|None, int, int, bool, str], str|None], int], tuple[str|None, int, bool, int]] = None
self.instances_threads:Callable[[int], bool] = None
class PyodideJavaScriptInterface(BaseAbstract):
def _set_basics(self:Self, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None):pass
def __init__(self:Self, anp:AnP, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> None:
self.load_file:Callable[[str, Optional[Callable[[str|None, int, int, bool, str], str|None]], Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]], int], tuple[str|None, int, bool, int]] = None
self.post:Callable[[str, dict[str|Any|None]|str, Optional[Callable[[str|None, int, int, bool, str], str|None]], Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]], int], tuple[str|None, int, bool, int]] = None
self.post_synchronous:Callable[[str, dict[str|Any|None]|str, Optional[Callable[[str|None, int, int, bool, str], str|None]], Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]], int], tuple[str|None, int, bool, int]] = None
self.preload_html_item:Callable[[str|HTMLElement, Optional[Callable[[HTMLElement|None, bool, int, list[str]], None]], int], tuple[HTMLElement|None, bool, int, list[str]]] = None
self.instances_threads:Callable[[int], bool] = None
def _build(self:Self, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> None:pass
def _set_basics(self:Self, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None):pass
def add(self:Self, inputs:dict[str, Callable[[tuple[Any|None]], Any|None]]|list[Any|None]|tuple[Any|None], options:int = 0) -> int:pass
def _build(self:Self, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> None:pass
def add(self:Self, inputs:dict[str, Callable[[tuple[Any|None]], Any|None]]|list[Any|None]|tuple[Any|None], options:int = 0) -> int:pass
class Console:
@ -130,7 +171,7 @@ class JSONLoader:
end_callback:Optional[Callable[[], None]] = None,
only_dictionaries:bool = False,
options:int = 0
) -> None:
):
self.anp:AnP = None
self.has:Options = None
self.has_console:bool = None
@ -364,6 +405,79 @@ class TerminalManager(BaseAbstract):
def _close_command(self:Self, dictionary:dict[str, Any|None], *arguments:list[Any|None]) -> None:pass
class CommonAttributes(BaseAbstract):
def __init__(self:Self, anp:AnP, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|tuple[Any|None]] = None) -> None:
self.standard_names:list[str] = None
@staticmethod
def set_names_block(names:str|list[str]|tuple[str]|None) -> list[str]|None:pass
@staticmethod
def check(name:str, exclude:list[str]|tuple[str]|None, only:Optional[list[str]|tuple[str]] = None) -> bool:pass
@staticmethod
def set_match_group(matches:REMatch) -> str:pass
def name_format(self:Self, name:str) -> str:pass
def set_string(self:Self,
attributes:dict[str, Any|None],
exclude:Optional[str|list[str]|tuple[str]] = None,
only:Optional[str|list[str]|tuple[str]] = None
) -> str:pass
if is_emcripten:
class BaseAttributes(CommonAttributes):
def set(self:Self,
item:HTMLElement|None,
attributes:dict[str, Any|None],
exclude:Optional[str|list[str]|tuple[str]] = None,
only:Optional[str|list[str]|tuple[str]] = None
) -> str|None:pass
else:
class BaseAttributes(CommonAttributes):
def set(self:Self,
_:Any|None,
attributes:dict[str, Any|None],
exclude:Optional[str|list[str]|tuple[str]] = None,
only:Optional[str|list[str]|tuple[str]] = None
) -> str|None:pass
class Attributes(BaseAttributes):pass
if is_emcripten:
class BaseComponents(BaseAbstract):
def __init__(self:Self, anp:AnP, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> None:pass
def preload(self:Self,
selector:HTMLElement|None,
callback:Optional[Callable[[HTMLElement|None, bool, int, list[str]], None]] = None,
inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None,
options:int = 0
) -> tuple[HTMLElement|None, bool, int, list[str]]:pass
else:
class BaseComponents(BaseAbstract):
def __init__(self:Self, anp:AnP, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> None:pass
class Components(BaseComponents):
def set(self:Self, items:list[Any|None]|tuple[Any|None]) -> str:pass
def i18n(self:Self, i18n:str, tag:str = "span", inputs:Optional[str|dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> str:pass
def icon(self:Self, icon:str, tag:str = "span", inputs:Optional[str|dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> str:pass
class AnP:
def __init__(self:Self, inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None]] = None) -> None:
@ -376,6 +490,8 @@ class AnP:
self.i18n:I18NManager = None
self.random_keys:RandomKeysManager = None
self.threads:ThreadsManager = None
self.attributes:Attributes = None
self.components:Components = None
self.terminal:TerminalManager = None
def set_subbasics(self:Self, inputs:Optional[dict[str, Any|None]|list[Any|None]|tuple[Any|None]] = None) -> None:pass

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from common import Self, Any
class ClassList:
def contains(self:Self, kebab:str) -> bool:None
def add(self:Self, kebab:str) -> None:None
def remove(self:Self, kebab:str) -> None:None
class Style:pass
class DOM:
def querySelector(selector:str) -> Self|None:pass
def querySelectorAll(selector:str) -> list[Self]:pass
def remove() -> None:pass
def hasAttribute(name:str) -> bool:pass
def getAttribute(name:str) -> str:pass
def setAttribute(name:str, value:Any|None) -> None:pass
class HTMLElement(DOM, Style):
def __init__(self:Self) -> None:
self.style:Style = None
class HTMLBodyElement(HTMLElement):pass

View File

@ -13,6 +13,8 @@ from Managers.Settings import SettingsManager
from Managers.I18N import I18NManager
from Managers.RandomKeys import RandomKeysManager
from Managers.Threads import ThreadsManager
from Application.Attributes import Attributes
from Application.Components import Components
from Utils.Check import Check
if not Check.is_emscripten():
@ -71,6 +73,8 @@ class AnP:
self.i18n:I18NManager = I18NManager(self, inputs)
self.random_keys:RandomKeysManager = RandomKeysManager(self, inputs)
self.threads:ThreadsManager = ThreadsManager(self, inputs)
self.attributes:Attributes = Attributes(self, inputs)
self.components:Components = Components(self, inputs)
if not is_emscripten:
self.terminal:TerminalManager = TerminalManager(self, inputs)
@ -127,7 +131,8 @@ class AnP:
try:
Utils.execute_asynchronous((
("console", "json", "pyodide", "request", "settings", "i18n", "random_keys", "threads") +
(tuple() if is_emscripten else ("terminal",))
(tuple() if is_emscripten else ("terminal",)) +
("attributes", "components")
), self.__start_module, lambda:Utils.execute(callback, True))
except Exception as exception:
self.__error |= 1 << 1
@ -168,6 +173,7 @@ class AnP:
try:
Utils.execute_asynchronous((
("components", "attributes") +
(tuple() if is_emscripten else ("terminal",)) +
("i18n", "settings", "request", "pyodide", "json", "console")
), self.__close_module, lambda:Utils.execute(callback, True))

View File

@ -0,0 +1,107 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Abstracts.Base import BaseAbstract
from Abstracts.AnPMap import AnP
from common import Self, Optional, Any, REMatch
from Utils.Check import Check
from Utils.Utils import Utils
from Utils.Patterns import Patterns
class CommonAttributes(BaseAbstract):
def __init__(self:Self, anp:AnP, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|tuple[Any|None]] = None) -> None:
super().__init__(anp, "attributes", inputs)
self.standard_names:list[str] = []
@staticmethod
def set_names_block(names:str|list[str]|tuple[str]|None) -> list[str]|None:
array:list[str] = Utils.get_selector_keys(names)
return array if len(array) else None
@staticmethod
def check(name:str, exclude:list[str]|tuple[str]|None, only:Optional[list[str]|tuple[str]] = None) -> bool:
return (not exclude or name not in exclude) and (not only or name in only)
@staticmethod
def set_match_group(matches:REMatch) -> str:
return "-" + (str(matches.group(1)).lower() if matches.group(1) else "")
def name_format(self:Self, name:str) -> str:
name = Patterns.RE_TO_KEBAB.sub(name, CommonAttributes.set_match_group)
return name if name in self.standard_names or name[:5] in ("data-", "aria-") else "data-" + name
def set_string(self:Self,
attributes:dict[str, Any|None],
exclude:Optional[str|list[str]|tuple[str]] = None,
only:Optional[str|list[str]|tuple[str]] = None
) -> str:
styles:str = ""
exclude = CommonAttributes.set_names_block(exclude)
only = CommonAttributes.set_names_block(only)
for name, value in Utils.get_dictionary(attributes).items():
if CommonAttributes.check(name):
styles += " " + self.name_format(name) + "=\"" + value + "\""
return styles
if Check.is_emscripten():
from Abstracts.PyodideMap import HTMLElement
class BaseAttributes(CommonAttributes):
def set(self:Self,
item:HTMLElement|None,
attributes:dict[str, Any|None],
exclude:Optional[str|list[str]|tuple[str]] = None,
only:Optional[str|list[str]|tuple[str]] = None
) -> str|None:
styles:str|None = None
if Check.is_json_item(item):
(item, attributes, exclude, only) = (None, item, attributes, exclude)
if item:
name:str
value:Any|None
exclude = CommonAttributes.set_names_block(exclude)
only = CommonAttributes.set_names_block(only)
for name, value in Utils.get_dictionary(attributes).items():
CommonAttributes.check(name) and item.setAttribute(self.name_format(name), value)
else:
styles = self.set_string(attributes, exclude, only)
return styles
else:
class BaseAttributes(CommonAttributes):
def set(self:Self,
_:Any|None,
attributes:dict[str, Any|None],
exclude:Optional[str|list[str]|tuple[str]] = None,
only:Optional[str|list[str]|tuple[str]] = None
) -> str|None:
if Check.is_dictionary(_):
(_, attributes, exclude, only) = (None, _, attributes, exclude)
return self.set_string(attributes, exclude, only)
class Attributes(BaseAttributes):
pass

View File

@ -0,0 +1,78 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Abstracts.Base import BaseAbstract
from Abstracts.AnPMap import AnP
from common import Self, Optional, Any, Callable
from Utils.Check import Check
from Utils.Utils import Utils
if Check.is_emscripten():
from Abstracts.PyodideMap import HTMLElement
from js import document
class BaseComponents(BaseAbstract):
def __init__(self:Self, anp:AnP, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> None:
super().__init__(anp, "components", inputs)
def preload(self:Self,
selector:HTMLElement|None,
callback:Optional[Callable[[HTMLElement|None, bool, int, list[str]], None]] = None,
inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None,
options:int = 0
) -> tuple[HTMLElement|None, bool, int, list[str]]:
return self.anp.pyodide.preload_html_item(selector, callback, inputs, options)
else:
class BaseComponents(BaseAbstract):
def __init__(self:Self, anp:AnP, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> None:
super().__init__(anp, "components", inputs)
class Components(BaseComponents):
def set(self:Self, items:list[Any|None]|tuple[Any|None]) -> str:
html:str = ''
if Check.is_array(items):
for item in items:
item = Utils.get_array(item)
l:int = len(item)
if l == 1 and Check.is_array(item[0]):
if len(item[0]) and Check.is_string(item[0][0]) and hasattr(self.anp.components, item[0][0]):
method:Callable[[list[Any|None]], str] = getattr(self.anp.components, item[0][0])
if callable(method):
html += method(*item[0][1:])
else:
tag:str = item[0]
attributes:dict[str, Any|None]|tuple[Any|None]|list[Any|None]|None = item[1] if l > 1 else None
childs:list[Any|None]|tuple[Any|None]|None = item[2] if l > 2 else None
html += '<' + tag + self.anp.attributes.set(attributes) + '>' + self.set(childs) + '</' + tag + '>'
elif Check.is_string(items):
html += items
return html
def i18n(self:Self, i18n:str, tag:str = "span", inputs:Optional[str|dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> str:
text:str = self.anp.i18n.get(i18n)
return self.set([[tag, (inputs, {
"i18n" : i18n
}), text]])
def icon(self:Self, icon:str, tag:str = "span", inputs:Optional[str|dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> str:
return self.set([[tag, (inputs, {
"icon" : icon
})]])

View File

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
from Abstracts.Base import BaseAbstract
from Abstracts.PyodideMap import HTMLElement
from Abstracts.AnPMap import AnP
from common import Self, Any, Callable, Optional
from Utils.Options import Options
@ -27,7 +28,7 @@ class PyodideJavaScriptInterface(BaseAbstract):
"default_pyodide_javascript_interface_methods", "default_javascript_interface_methods",
"pyodide_javascript_interface_methods", "javascript_interface_methods"
):
self.add(get(key, inputs)[0])
self.add(get(key, inputs)[0], Options.OVERWRITE)
def _build(self:Self, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> None:
@ -40,8 +41,10 @@ class PyodideJavaScriptInterface(BaseAbstract):
self.__show_add_error:bool = True
self.__show_add_ok:bool = False
self.load_file:Callable[[str, Callable[[str|None, int, int, bool, str], str|None], int], tuple[str|None, int, bool, int]] = None
self.post:Callable[[str, Callable[[str|None, int, int, bool, str], str|None], int], tuple[str|None, int, bool, int]] = None
self.load_file:Callable[[str, Optional[Callable[[str|None, int, int, bool, str], str|None]], Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]], int], tuple[str|None, int, bool, int]] = None
self.post:Callable[[str, dict[str|Any|None]|str, Optional[Callable[[str|None, int, int, bool, str], str|None]], Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]], int], tuple[str|None, int, bool, int]] = None
self.post_synchronous:Callable[[str, dict[str|Any|None]|str, Optional[Callable[[str|None, int, int, bool, str], str|None]], Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]], int], tuple[str|None, int, bool, int]] = None
self.preload_html_item:Callable[[str|HTMLElement, Optional[Callable[[HTMLElement|None, bool, int, list[str]], None]], int], tuple[HTMLElement|None, bool, int, list[str]]] = None
self.instances_threads:Callable[[int], bool] = None
def __init__(self:Self, anp:AnP, inputs:Optional[dict[str, Any|None]|tuple[Any|None]|list[Any|None]] = None) -> None:

View File

@ -5,6 +5,7 @@ from common import REPattern, re_compile, RE_IGNORE_CASE
class Patterns:
RE_KEY:REPattern = re_compile(r'^[a-z0-9_]+$', RE_IGNORE_CASE)
RE_SELECTOR_KEY:REPattern = re_compile(r'^[a-z0-9_\-]+$', RE_IGNORE_CASE)
RE_STRING_VARIABLES:REPattern = re_compile(r'\{([a-z0-9_]+)\}', RE_IGNORE_CASE)
RE_COLOR_HEXADECIMAL:REPattern = re_compile(r'^\#([0-9a-f]{3}|([0-9a-f]{2}){3,4})$', RE_IGNORE_CASE)
RE_COLOR_RGB:REPattern = re_compile(r'^rgba?\((' +
@ -21,4 +22,5 @@ class Patterns:
RE_BREAK_LINES:REPattern = re_compile(r'\r\n|[\r\n]')
RE_COMMAND_AND_PARAMETERS:REPattern = re_compile(r'^\s*([^\s]+)(\s+(.*))?$')
RE_COMMAND_PARAMETER:REPattern = re_compile(r'((\-{,2})([a-z0-9_]+)\s*[\=\:]\s*("(([^\\\\"]+|\\.)*)"|\'(([^\\\\\']+|\\.)*)\'|([^\s]*)))|"(([^\\\\"]+|\\.)*)"|\'(([^\\\\\']+|\\.)*)\'|([^\s]+)', RE_IGNORE_CASE)
RE_SLASH_ESCAPE:REPattern = re_compile(r'\\(.)')
RE_SLASH_ESCAPE:REPattern = re_compile(r'\\(.)')
RE_TO_KEBAB:REPattern = re_compile('([A-Z0-9]([A-Z0-9]*|[a-z0-9]*))|[^a-zA-Z0-9]+')

View File

@ -215,5 +215,10 @@ class Utils:
return alphabet
@staticmethod
def get_random_item(items:list[Any|None]|tuple[Any|None]) -> Any|None:
return get_random_item(items)
return get_random_item(items)
@staticmethod
def get_selector_keys(keys:Any|None) -> list[str]:
return [key for key in Utils.get_array(keys) if key and Check.is_string(key) and Patterns.RE_SELECTOR_KEY.match(key)]