extml
Version:
Converts html tagged templates to ExtJS component object.
1,212 lines (1,089 loc) • 44 kB
JavaScript
/* Extml, version: 2.45.3 - April 9, 2025 10:19:19 */
const STYLE_PREFIX = 'extml-style-';
function composeStyleInner(cssContent, tag) {
if (typeof cssContent !== 'string')
return;
//cssContent = mapper.getAll(cssContent);
let sanitizeTagForAnimation = tag.replace(/\w/g, '');
cssContent = cssContent
.replace(/<\/?style>/g, '')
.replace(/{/g, '{\n')
.replace(/}/g, '}\n')
.replace(/^(\s+)?:(component)(\s+)?{/gm, tag + ' {')
.replace(/:(component)/g, '')
.replace(/(@(?:[\w-]+-)?keyframes\s+)([\w-_]+)/g, `$1 ${sanitizeTagForAnimation}-$2`)
.replace(/((?:[\w-]+-)?animation(?:-name)?(?:\s+)?:(?:\s+))([\w-_]+)/g, `$1 ${sanitizeTagForAnimation}-$2`)
// Remove comments
.replace(/\/\*[\s\S]*?\*\/|([^:]|^)\/\/.*$/gm, '')
.replace(/\S.*{/gm, match => {
if (/^(@|:host|(from|to|\d+%)[^-_])/.test(match))
return match;
let part = match.split(',');
const sameTag = new RegExp(`^${tag.replace(/[[\]]/g, '\\$&')}(\\s+)?{`);
for (let i = 0; i < part.length; i++) {
part[i] = part[i].trim();
if (sameTag.test(part[i]))
continue;
if (/^:global/.test(part[i]))
part[i] = part[i].replace(':global', '');
else
part[i] = `${tag} ${part[i]}`;
}
match = part.join(',');
return match;
});
cssContent = cssContent
.replace(/\s{2,}/g, ' ')
.replace(/{ /g, '{')
.replace(/ }/g, '}')
.replace(/\s:/g, ':') //remove space before pseudo classes
.replace(/\n/g, '')
.trim();
return cssContent;
}
function createStyle() {
if (!this.stylesheet) return;
let id = this.getId();
let styleElement = document.createElement('style');
styleElement.id = STYLE_PREFIX + id;
this.stylesheetStateListeners = [];
let stylesheet = '';
if (this.stylesheet.some(
(item) => typeof item === 'function' && item.$$isState === true
)) {
let stateItems = [];
let buildStyle = () => this.stylesheet.map(item => {
if (typeof item === 'function' && item.$$isState) {
stateItems.push(item);
return item()
} else {
return item
}
}).join('');
stylesheet = buildStyle();
stateItems.forEach(item => {
this.stylesheetStateListeners.push(item.$$subscribe(value => {
styleElement.innerHTML = composeStyleInner(buildStyle(), '#' + id);
}));
});
} else {
stylesheet = this.stylesheet.join('');
}
styleElement.innerHTML = composeStyleInner(stylesheet, '#' + id);
document.head.appendChild(styleElement);
}
function destroyStyle() {
if (!this.stylesheet) return;
if (this.stylesheetStateListeners) {
this.stylesheetStateListeners.forEach(listener => listener());
}
document.getElementById(STYLE_PREFIX + this.getId()).remove();
}function createContext() {
Ext.getApplication().appContext = Ext.getApplication().appContext || {};
this.appContext = Ext.getApplication().appContext;
let controller = this.getController();
//append context to controller
let children = this.query ? this.query('*') : [];
if (this.contextName) {
if (this.appContext[this.contextName] !== undefined) {
// throw new Error('A context with this name already exists: ' + this.contextName);
console.error('A context with this name already exists: ' + this.contextName, 'itemId:', this.getItemId());
}
this.appContext[this.contextName] = /*this.context[this.contextName] ||*/ {};
this.appContext[this.contextName][this.getItemId()] = this;
children.forEach(item => {
this.appContext[this.contextName][item.getItemId()] = item;
item.appContext = this.appContext;
});
}
//if (this.isContext) {
if (!this.context) {
this.context = {};
this.context[this.getItemId()] = this;
children.forEach(item => {
this.context[item.getItemId()] = item;
item.context = this.context;
});
}
if (controller) {
controller.appContext = this.appContext;
controller.context = this.context;
controller.props = controller.view.props;
}
}
function destroyContext() {
Ext.getApplication().appContext = Ext.getApplication().appContext || {};
if (this.contextName) {
delete Ext.getApplication().appContext[this.contextName];
}
let itemId = this.getItemId();
if (Ext.getApplication().appContext[itemId]) {
delete Ext.getApplication().appContext[itemId];
}
}function initialize() {
createStyle.apply(this);
createContext.apply(this);
}
function destroy() {
destroyStyle.apply(this);
destroyContext.apply(this);
}const htmlTags = [
"a", "abbr", "acronym", "address", "applet", "area", "article", "aside", "audio", "b", "base", "basefont", "bdi", "bdo", "bgsound", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "command", "content", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "element", "em", "embed", "fieldset", "figcaption", "figure", "font", "footer", "form", "frame", "frameset", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "iframe", "image", "img", "input", "ins", "isindex", "kbd", "keygen", "label", "legend", "li", "link", "listing", "main", "map", "mark", "marquee", "math", "menu", "menuitem", "meta", "meter", "multicol", "nav", "nextid", "nobr", "noembed", "noframes", "noscript", "object", "ol", "optgroup", "option", "output", "p", "param", "picture", "plaintext", "pre", "progress", "q", "rb", "rbc", "rp", "rt", "rtc", "ruby", "s", "samp", "script", "section", "select", "shadow", "slot", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "svg", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr", "xmp",
//custom tag
"x-s", "x-h", "x-f"
];function defineExtClass(tag) {
let className = 'html-' + tag;
if (window.__extHtmlClass[className]) {
return
}
window.__extHtmlClass[className] = window.Ext.define(className, {
extend: 'Ext.Container',
xtype: className,
element: {
tag,
reference: 'element'
},
template: [
{
tag: 'ghost-tag',
reference: 'bodyElement',
cls: '',
uiCls: ''
}
],
getInnerHtmlElement() {
return this.getRenderTarget();
},
doHack() {
{
if (!this.el || !this.el.dom ) {
return
}
//this.el.dom.className = '';
//this.innerElement.dom.className = '';
this.el.dom.removeAttribute('class');
// l'id mi serve per lo style
if (!this.stylesheet)
this.el.dom.removeAttribute('id');
this.el.dom.removeAttribute('data-componentid');
this.el.dom.removeAttribute('data-xid');
if (this._propsAttributes) {
Object.keys(this._propsAttributes).forEach(attribute => {
if (attribute === 'ref' && this._propsAttributes[attribute].$$isRef) {
if (this._propsAttributes[attribute].$$isExtRef) {
this._propsAttributes[attribute](this);
} else {
this._propsAttributes[attribute](this.el.dom);
}
} else if (this._propsAttributes[attribute].$$isState) {
this.el.dom.setAttribute(attribute, String(this._propsAttributes[attribute]()));
// o.$$stateListener = this._propsAttributes[attribute].$$subscribe(value => {
this.$$stateListener = this._propsAttributes[attribute].$$subscribe(value => {
this.el.dom.setAttribute(attribute, String(value));
});
} else if (attribute.startsWith('on') && typeof this._propsAttributes[attribute] === 'function') {
this.el.dom.addEventListener(attribute.substring(2), this._propsAttributes[attribute]);
} else {
if (Array.isArray(this._propsAttributes[attribute]) && this._propsAttributes[attribute].$$hasState) {
//console.log(this._propsAttributes[attribute])
// mi serve a costruire il valore dell'attributo concatenando i risultati degli state con le eventuali stringhe presenti
let buildAttributeValue = () => this.el.dom.setAttribute(attribute, this._propsAttributes[attribute].map(item => {
if (typeof item === 'function' && item.$$isState) {
return item()
} else {
return item
}
}).join(''));
buildAttributeValue();
// o.$$attributesStateListeners = [];
this.$$attributesStateListeners = [];
// per ogni state sottoscrivo un listener per ricostruire nuovamente il valore dell'attributo ad ogni cambiamento
this._propsAttributes[attribute].forEach(item => {
if (typeof item === 'function' && item.$$isState) {
// o.$$attributesStateListeners.push(item.$$subscribe(value => {
this.$$attributesStateListeners.push(item.$$subscribe(value => {
buildAttributeValue();
}));
}
});
} else {
this.el.dom.setAttribute(attribute, String(this._propsAttributes[attribute]));
}
}
});
}
}
},
removeMonitorsElements() {
// Rimuovo i due div del monitor, nodi dovrebbero essere sempre 3, quindi vado a colpo sicuro,
// il primo è il bodyElement, quello che poi conterrà eventuali figli
let sizeMonitorsEl = this.el.dom.childNodes[1];
let paintMonitorEl = this.el.dom.childNodes[2];
if (sizeMonitorsEl)
sizeMonitorsEl.remove();
if (paintMonitorEl)
paintMonitorEl.remove();
},
moveElementsToNewParent() {
// sposto gli elementi dal ghost-tag al parent
let newParent = this.el.dom;
let oldParent = this.innerElement.dom;
if (newParent && oldParent) {
function move() {
while (oldParent.childNodes.length > 0) {
newParent.appendChild(oldParent.childNodes[0]);
}
}
move();
this.innerElement.dom = this.el.dom;
// Rimuovo ghost-tag
oldParent.remove();
}
},
listeners: [
{
initialize() {
requestAnimationFrame(() => this.doHack());
this.removeMonitorsElements();
this.moveElementsToNewParent();
}
},
{
destroy(o) {
if(o.$$stateListener) {
o.$$stateListener();
}
if (o.$$attributesStateListeners) {
o.$$attributesStateListeners.forEach(listener => listener());
}
}
}
]
});
}
function generateHtmlClass() {
if (!window.__extHtmlClass) {
window.__extHtmlClass = {};
htmlTags.forEach(tag => defineExtClass(tag));
}
}const MODE_SLASH = 0;
const MODE_TEXT = 1;
const MODE_WHITESPACE = 2;
const MODE_TAGNAME = 3;
const MODE_COMMENT = 4;
const MODE_PROP_SET = 5;
const MODE_PROP_APPEND = 6;
const CHILD_APPEND = 0;
const CHILD_RECURSE = 2;
const TAG_SET = 3;
const PROPS_ASSIGN = 4;
const PROP_SET = MODE_PROP_SET;
const PROP_APPEND = MODE_PROP_APPEND;
const evaluate = (h, built, fields, args) => {
let tmp;
// `build()` used the first element of the operation list as
// temporary workspace. Now that `build()` is done we can use
// that space to track whether the current element is "dynamic"
// (i.e. it or any of its descendants depend on dynamic values).
built[0] = 0;
for (let i = 1; i < built.length; i++) {
const type = built[i++];
// Set `built[0]`'s appropriate bits if this element depends on a dynamic value.
const value = built[i] ? ((built[0] |= type ? 1 : 2), fields[built[i++]]) : built[++i];
if (type === TAG_SET) {
args[0] = value;
}
else if (type === PROPS_ASSIGN) {
args[1] = Object.assign(args[1] || {}, value);
}
else if (type === PROP_SET) {
(args[1] = args[1] || {})[built[++i]] = value;
}
else if (type === PROP_APPEND) {
++i;
// support for array of prop
if (typeof value === "function" && value.$$isState) {
if (!Array.isArray(args[1][built[i]])) {
args[1][built[i]] = [args[1][built[i]]];
args[1][built[i]].$$hasState = true;
}
}
if (Array.isArray(args[1][built[i]])) {
args[1][built[i]].push(value);
} else {
args[1][built[i]] += (value + '');
}
}
else if (type) { // type === CHILD_RECURSE
// Set the operation list (including the staticness bits) as
// `this` for the `h` call.
tmp = h.apply(value, evaluate(h, value, fields, ['', null]));
args.push(tmp);
if (value[0]) {
// Set the 2nd lowest bit it the child element is dynamic.
built[0] |= 2;
}
else {
// Rewrite the operation list in-place if the child element is static.
// The currently evaluated piece `CHILD_RECURSE, 0, [...]` becomes
// `CHILD_APPEND, 0, tmp`.
// Essentially the operation list gets optimized for potential future
// re-evaluations.
built[i-2] = CHILD_APPEND;
built[i] = tmp;
}
}
else { // type === CHILD_APPEND
args.push(value);
}
}
return args;
};
const build = function(statics) {
let mode = MODE_TEXT;
let buffer = '';
let quote = '';
let current = [0];
let char, propName;
const commit = field => {
if (mode === MODE_TEXT && (field || (buffer = buffer.replace(/^\s*\n\s*|\s*\n\s*$/g,'')))) {
{
current.push(CHILD_APPEND, field, buffer);
}
}
else if (mode === MODE_TAGNAME && (field || buffer)) {
{
current.push(TAG_SET, field, buffer);
}
mode = MODE_WHITESPACE;
}
else if (mode === MODE_WHITESPACE && buffer === '...' && field) {
{
current.push(PROPS_ASSIGN, field, 0);
}
}
else if (mode === MODE_WHITESPACE && buffer && !field) {
{
current.push(PROP_SET, 0, true, buffer);
}
}
else if (mode >= MODE_PROP_SET) {
{
if (buffer || (!field && mode === MODE_PROP_SET)) {
current.push(mode, 0, buffer, propName);
mode = MODE_PROP_APPEND;
}
if (field) {
current.push(mode, field, 0, propName);
mode = MODE_PROP_APPEND;
}
}
}
buffer = '';
};
for (let i=0; i<statics.length; i++) {
if (i) {
if (mode === MODE_TEXT) {
commit();
}
commit(i);
}
for (let j=0; j<statics[i].length;j++) {
char = statics[i][j];
if (mode === MODE_TEXT) {
if (char === '<') {
// commit buffer
commit();
{
current = [current];
}
mode = MODE_TAGNAME;
}
else {
buffer += char;
}
}
else if (mode === MODE_COMMENT) {
// Ignore everything until the last three characters are '-', '-' and '>'
if (buffer === '--' && char === '>') {
mode = MODE_TEXT;
buffer = '';
}
else {
buffer = char + buffer[0];
}
}
else if (quote) {
if (char === quote) {
quote = '';
}
else {
buffer += char;
}
}
else if (char === '"' || char === "'") {
quote = char;
}
else if (char === '>') {
commit();
mode = MODE_TEXT;
}
else if (!mode) ;
else if (char === '=') {
mode = MODE_PROP_SET;
propName = buffer;
buffer = '';
}
else if (char === '/' && (mode < MODE_PROP_SET || statics[i][j+1] === '>')) {
commit();
if (mode === MODE_TAGNAME) {
current = current[0];
}
mode = current;
{
(current = current[0]).push(CHILD_RECURSE, 0, mode);
}
mode = MODE_SLASH;
}
else if (char === ' ' || char === '\t' || char === '\n' || char === '\r') {
// <a disabled>
commit();
mode = MODE_WHITESPACE;
}
else {
buffer += char;
}
if (mode === MODE_TAGNAME && buffer === '!--') {
mode = MODE_COMMENT;
current = current[0];
}
}
}
commit();
return current;
};/**
* Copyright 2018 Google Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const CACHES = new Map();
const regular = function(statics) {
let tmp = CACHES.get(this);
if (!tmp) {
tmp = new Map();
CACHES.set(this, tmp);
}
tmp = evaluate(this, tmp.get(statics) || (tmp.set(statics, tmp = build(statics)), tmp), arguments, []);
return tmp.length > 1 ? tmp : tmp[0];
};
var htm = regular;function isEvent(prop) {
return prop.startsWith('on')
}
function extractListenerName(prop) {
return prop.substring(2)
}
function createEventObject(name, handle) {
let event = {};
event[name] = handle;
return event;
}
function addEvent(componentConfig, eventObject) {
componentConfig.listeners.push(eventObject);
}function isHtmlType(type) {
return (type).startsWith('html-')
}const columnTypes = [
'gridcolumn', 'column', 'templatecolumn', 'booleancolumn',
'checkcolumn', 'datecolumn', 'numbercolumn', 'rownumberer',
'textcolumn', 'treecolumn'
];
function createComponentConfig(type, props, children, propsFunction, isResolvedFunction) {
// Default configuration
let componentConfig = initializeComponentConfig(type);
// Configuration based on props
let configFromProps = Object.assign({}, props, propsFunction);
if (isHtmlType(configFromProps.xtype || type)) {
componentConfig._propsAttributes = props;
}
applyPropsToConfig(componentConfig, configFromProps);
// Configuration based on children
configureChildren(componentConfig, children, type);
return componentConfig;
}
// Function to initialize the base configuration
function initializeComponentConfig(type) {
return {
xtype: type.toLowerCase(),
listeners: [
createEventObject('initialize', initialize),
createEventObject('destroy', destroy)
]
};
}
// Function to apply props to componentConfig
function applyPropsToConfig(config, props) {
for (let prop in props) {
if (isEvent(prop)) {
addEvent(
config,
createEventObject(extractListenerName(prop), props[prop])
);
} else if (prop === 'controller' && typeof props[prop] === 'function') {
config[prop] = props[prop]();
} else if (prop === 'bindState' && props[prop] && props[prop].$$setState) {
//config.listeners = config.listeners || [];
config.listeners.push(createEventObject('initialize', (o) => {
props[prop].$$setState(o.getValue());
}));
config.listeners.push(createEventObject('change', (o, v) => {
props[prop].$$setState(v);
}));
} else if (prop === 'ref' && props[prop] && props[prop].$$isRef) {
//config.listeners = config.listeners || [];
config.listeners.push(createEventObject('initialize', (o) => {
if (props[prop].$$isExtRef) {
props[prop](o);
} else {
props[prop](o.el.dom);
}
}));
} else if (Array.isArray(props[prop]) && props[prop].$$hasState) {
let buildAttributeValue = () => props[prop].map(item => {
if (typeof item === 'function' && item.$$isState) {
return item()
} else {
return item
}
}).join('');
config[prop] = buildAttributeValue();
//config.listeners = config.listeners || [];
config.listeners.push(createEventObject('initialize', (o) => {
o.$$attributesStateListeners = [];
// per ogni state sottoscrivo un listener per ricostruire nuovamente il valore dell'attributo ad ogni cambiamento
props[prop].forEach(item => {
if (typeof item === 'function' && item.$$isState) {
o.$$attributesStateListeners.push(item.$$subscribe(value => {
let setterName = createSetterName(prop);
if (prop === 'class' && !isHtmlType(o.xtype)) {
setterName = 'setCls';
}
if (typeof o[setterName] === 'function' && !o.destroyed) {
// o[createSetterName(prop)](buildAttributeValue());
o[setterName](buildAttributeValue());
}
}));
}
});
}));
config.listeners.push(createEventObject('destroy', (o) => {
if (o.$$attributesStateListeners) {
o.$$attributesStateListeners.forEach(listener => listener());
}
}));
} else if (typeof props[prop] === 'function') {
let propsProp = props[prop];
if (propsProp.$$isState) {
config.listeners = config.listeners || [];
config.listeners.push(createEventObject('initialize', (o) => {
o.$$stateListener = propsProp.$$subscribe(value => {
let setterName = createSetterName(prop);
if (prop === 'class' && !isHtmlType(o.xtype)) {
setterName = 'setCls';
}
if (typeof o[setterName] === 'function' && !o.destroyed) {
// o[createSetterName(prop)](value);
o[setterName](value);
}
});
}));
config.listeners.push(createEventObject('destroy', (o) => {
if (o.$$stateListener) {
o.$$stateListener();
}
}));
props[prop] = props[prop]();
}
config[prop] = props[prop];
} else if (prop === 'class') {
config['cls'] = props[prop];
} else {
config[prop] = props[prop];
}
}
}
// Function to configure componentConfig based on children
function configureChildren(config, children, type) {
children.forEach(child => {
if (typeof child === 'undefined') return;
if (child.xtype && columnTypes.includes(child.xtype)) {
addToArray(config, 'columns', child);
} else if (child.xtype === 'menu' && type === 'button') {
addSingle(config, 'menu', child);
} else if (child.xtype === 'button' && columnTypes.includes(type)) {
addSingle(config, 'cell', {
xtype: 'widgetcell',
forceWidth: true,
widget: {
xtype: 'container',
items: []
}
});
addToArray(config.cell.widget, 'items', child);
} else if (child.xtype === 'button' && type === 'widgetcell') {
// create widget child container
addSingle(config, 'widget', {
xtype: 'container',
items: []
});
addToArray(config.widget, 'items', child);
} else if (child.xtype === 'widgetcell') {
addSingle(config, 'cell', child);
} else if (child.xtype) {
addToArray(config, 'items', child);
} else {
if (child.$$isState) {
// create state component
addToArray(config, 'items', {
xtype: 'html-x-s',
html: String(child()),
listeners: [
createEventObject('initialize', (o) => {
o.$$stateListener = child.$$subscribe(value => {
o.bodyElement.el.dom.innerHTML = String(value);
});
}),
createEventObject('destroy', (o) => {
if (o.$$stateListener) {
o.$$stateListener();
}
})
]
});
} else {
let processedValue = processValueForHtml(child);
// console.log(children.length, processedValue, isPlainText(processedValue), children.some(
// (item) => typeof item === 'function' && item.$$isState === true
// ))
// console.log(children)
// if there is some state in the children array it's better to use the html-x-html
if (isPlainText(processedValue) && (children.length > 1 || children.some(
(item) => typeof item === 'function' && item.$$isState === true
))) {
// create html text component
// console.log(processValueForHtml(child))
addToArray(config, 'items', {
xtype: 'html-x-h',
html: processedValue,
listeners: [
createEventObject('initialize', initialize),
createEventObject('destroy', destroy)
]
});
} else {
config.html = config.html || '';
config.html += processedValue;
}
}
}
});
}
function isPlainText(str) {
const htmlTagPattern = /<[^>]*>/g;
return !htmlTagPattern.test(str);
}
// Function to safely add an element to an array property
function addToArray(config, key, item) {
if (!config[key]) {
config[key] = [];
}
config[key].push(item);
}
// Function to add a single item to a property if it's not already present
function addSingle(config, key, item) {
if (!config[key]) {
config[key] = item;
}
}
// Function to handle value types and convert them to strings for HTML content
function processValueForHtml(value) {
const convertableTypes = ['string', 'number', 'boolean'];
// Verifica se il valore è di un tipo convertibile direttamente
if (convertableTypes.includes(typeof value)) {
return String(value);
}
// Se è una funzione, eseguila e processa il risultato
if (typeof value === 'function') {
const result = value();
return convertableTypes.includes(typeof result) ? String(result) : '';
}
// Warning per tipi non gestiti
console.warn('Unhandled value type:', value);
return ''; // Valore di default per tipi non supportati
}
function createSetterName(attribute) {
return `set${attribute.charAt(0).toUpperCase()}${attribute.slice(1)}`;
}function detectClassType(xtype) {
if (xtype.startsWith('ext-')) {
xtype = xtype.split('ext-')[1];
} else if (!xtype.startsWith('html-')) {
xtype = 'html-' + xtype;
}
return xtype;
}// import htm from "htm";
// import {isHtmlType} from "./isHtmlType.js";
// export function _h(type, props, ...children) {
// if (type === 'style') {
// //console.log(children)
// return {isStyle: true, content: children}
// } else if (type === 'context') {
// return {isContext: true, props, children: children[0]}
// } else if (typeof type === 'function') {
// let resolvedFunction = type(props)
// return createComponentConfig(resolvedFunction.xtype, type(props), children, props)
// } else {
// return createComponentConfig(detectClassType(type), props, children);
// }
// }
function _h(type, props, ...children) {
if (type === 'style') {
//console.log(children)
return {isStyle: true, content: children}
} else if (type === 'context') {
return {isContext: true, props, children: children[0]}
} else if (typeof type === 'function') {
return createComponentConfig(detectClassType(type.name), type(props), children, props)
}
return createComponentConfig(detectClassType(type), props, children);
}
function h(strings, ...values) {
// console.log(strings, ...values)
let parsed = htm.bind(_h)(strings, ...values);
//get style by component definition
if (parsed.length > 1 && parsed[0].isStyle) {
let styleContent = parsed[0].content;
parsed = parsed[1];
parsed.stylesheet = styleContent;
}
//get context
if (parsed.isContext) {
parsed.props = parsed.props || {};
let contextName = parsed.props.name;
//get possible stylesheet obtained from the upper block
let stylesheet = parsed.stylesheet;
parsed = parsed.children;
parsed.stylesheet = stylesheet;
parsed.contextName = contextName;
parsed.isContext = true;
}
return parsed
}let activeTracker = null;
function getActiveTracker() {
return activeTracker;
}
function setActiveTracker(tracker) {
activeTracker = tracker;
}function createState(initialValue, context = null, treatAsSingleEntity = false, sync = false) {
const stateCache = createState.stateCache || (createState.stateCache = new WeakMap());
const getPersistentState = (context, valueInitializer) => {
if (!stateCache.has(context)) {
stateCache.set(context, valueInitializer());
}
return stateCache.get(context);
};
if (context) {
return getPersistentState(context, () => {
return createState(initialValue, null, treatAsSingleEntity, sync);
});
}
const isObject = typeof initialValue === 'object' && initialValue !== null && !Array.isArray(initialValue) && !(initialValue instanceof Date) && !treatAsSingleEntity;
let state = isObject ? { ...initialValue } : initialValue;
const globalListeners = new Set();
const propertyListeners = isObject
? Object.fromEntries(Object.keys(state).map(key => [key, new Set()]))
: null;
let batchUpdates = [];
let pendingUpdate = false;
const getState = () => {
const tracker = getActiveTracker();
if (tracker) {
tracker.add(getState);
}
return state;
};
getState.$$isState = true;
getState.$$subscribe = listener => {
globalListeners.add(listener);
return () => globalListeners.delete(listener);
};
const setState = (newValue) => {
if (typeof newValue === "function") {
newValue = newValue(state);
}
if (sync) {
applyState(newValue);
return Promise.resolve();
} else {
batchUpdates.push(newValue);
if (!pendingUpdate) {
pendingUpdate = true;
return new Promise(resolve => {
queueMicrotask(() => {
pendingUpdate = false;
processBatchUpdates();
resolve();
});
});
} else {
return Promise.resolve();
}
}
};
getState.$$setState = setState;
const applyState = (newValue) => {
let hasChanges = false;
if (isObject) {
const newState = { ...state };
Object.keys(newValue).forEach(key => {
if (newValue[key] !== state[key]) {
newState[key] = newValue[key];
hasChanges = true;
propertyListeners[key].forEach(listener => listener(newState[key]));
}
});
if (hasChanges) {
state = newState;
}
} else {
if (newValue !== state) {
state = newValue;
hasChanges = true;
}
}
if (hasChanges) {
globalListeners.forEach(listener => listener(state));
}
};
const processBatchUpdates = () => {
batchUpdates.forEach(newValue => applyState(newValue));
batchUpdates = [];
};
if (isObject) {
Object.keys(state).forEach(key => {
setState[key] = (newValue) => {
const updatedValue = typeof newValue === "function" ? newValue(state[key]) : newValue;
if (state[key] !== updatedValue) {
state = { ...state, [key]: updatedValue };
propertyListeners[key].forEach(listener => listener(state[key]));
globalListeners.forEach(listener => listener(state));
}
};
});
}
const stateGetters = isObject
? Object.fromEntries(
Object.keys(state).map(key => {
const propertyGetter = () => {
const tracker = getActiveTracker();
if (tracker) {
tracker.add(propertyGetter);
}
return state[key];
};
propertyGetter.$$isState = true;
propertyGetter.$$setState = setState[key];
propertyGetter.$$subscribe = listener => {
propertyListeners[key].add(listener);
return () => propertyListeners[key].delete(listener);
};
return [key, propertyGetter];
})
)
: getState;
return [isObject ? stateGetters : getState, setState, getState.$$subscribe];
}function createRef(onChange = null, isExtRef = false) {
let _current = null;
const subscribers = [];
const ref = function(value) {
if (arguments.length === 0) {
// Getter
return _current;
} else {
// Setter
if (_current !== value) {
_current = value;
if (typeof onChange === "function") {
onChange(value);
}
// Notifica tutti i subscriber del cambiamento
subscribers.forEach(callback => callback(value));
}
}
};
// Proprietà speciale per identificare l'oggetto come ref
ref.$$isRef = true;
ref.$$isExtRef = isExtRef;
// Metodo per aggiungere subscriber
ref.$$subscribe = function(listener) {
if (typeof listener === "function") {
subscribers.push(listener);
return () => {
// Restituisci una funzione di unsubscribe
const index = subscribers.indexOf(listener);
if (index !== -1) {
subscribers.splice(index, 1);
}
};
}
throw new Error("Listener must be a function");
};
return ref;
}function createExtRef(onChange = null) {
return createRef(onChange, true);
}function createEffect(effect, dependencies, runInitially = false) {
if (typeof effect !== "function") {
throw new Error("Effect must be a function");
}
if (!Array.isArray(dependencies)) {
throw new Error("Dependencies must be an array");
}
let cleanup; // Variabile per memorizzare il cleanup dell'effetto
// Funzione per eseguire l'effetto e gestire il cleanup
const runEffect = () => {
if (typeof cleanup === "function") {
cleanup(); // Puliamo prima di eseguire un nuovo effetto
}
cleanup = effect(); // Salviamo il cleanup restituito dall'effetto
};
// Eseguiamo l'effetto inizialmente se richiesto
if (runInitially) runEffect();
// const unsubscribes = dependencies.map(dep => {
// if (!dep || typeof dep !== "object" || typeof dep.$$subscribe !== "function") {
// throw new Error("Dependencies must be objects with the method $$subscribe");
// }
// return dep.$$subscribe(runEffect);
// });
const unsubscribes = dependencies.map(dep => {
if (dep && typeof dep.$$subscribe === "function") {
return dep.$$subscribe(runEffect);
} else {
throw new Error("Dependencies must be objects with the method $$subscribe");
}
});
return () => {
if (typeof cleanup === "function") cleanup(); // Cleanup finale
unsubscribes.forEach(unsub => unsub?.());
};
}function createPropertyObserver(target, path, callback = null) {
if (typeof target !== "object" || target === null) {
throw new Error("Target must be an object");
}
const parts = path.split('.');
let current = target;
for (let i = 0; i < parts.length - 1; i++) {
current = current[parts[i]];
if (typeof current !== "object" || current === null) {
throw new Error(`Invalid property path: ${path}`);
}
}
const prop = parts[parts.length - 1];
const listeners = [];
let value = current[prop];
Object.defineProperty(current, prop, {
get() {
return value;
},
set(newValue) {
const oldValue = value;
value = newValue;
listeners.forEach(callback => callback(newValue, oldValue));
if (callback) callback(newValue, oldValue);
},
configurable: true,
enumerable: true,
});
return {
$$subscribe(callback) {
listeners.push(callback);
return () => {
const index = listeners.indexOf(callback);
if (index !== -1) listeners.splice(index, 1);
};
}
};
}function createDerivedState(transformer, sync = false, ...args) {
const dependencies = new Set();
const trackingWrapper = (...args) => {
setActiveTracker(dependencies);
try {
return transformer(...args.map(arg => (arg.$$isState ? arg() : arg)));
} finally {
setActiveTracker(null);
}
};
const [derived, setDerived] = createState(trackingWrapper(...args), null, false, sync);
const updateDerivedState = () => {
if (sync) {
setDerived(trackingWrapper(...args));
} else {
queueMicrotask(() => {
setDerived(trackingWrapper(...args));
});
}
};
dependencies.forEach(state => {
state.$$subscribe(updateDerivedState);
});
return derived;
}function conditionalState(state, trueValue = true, falseValue = false) {
return createDerivedState(() => state() ? trueValue : falseValue);
}function toggleState(state) {
return state.$$setState(currentState => !currentState);
}function For({ each, effect, getKey = (item) => item.id || item.name, tag = 'ext-container', attributes = {} }) {
function onInitialize(component) {
const childStateMap = new Map(); // Mappa per gestire lo stato dei figli
const updateChildren = (newItems) => {
const newStateMap = new Map(); // Nuova mappa per gli stati aggiornati
// Rigenera sempre tutti gli elementi
newItems.forEach((item, index) => {
const key = getKey(item); // Ottieni una chiave univoca per ogni elemento
const state = childStateMap.get(key) || {};
newStateMap.set(key, state);
const child = effect(item, index, state); // Genera il figlio
if (component.items.getAt(index)) {
component.removeAt(index);
component.insert(index, child);
} else {
component.add(child);
}
});
// Rimuove eventuali elementi extra
while (component.items.length > newItems.length) {
component.removeAt(component.items.length - 1);
}
// Aggiorna la mappa degli stati
childStateMap.clear();
for (const [key, value] of newStateMap.entries()) {
childStateMap.set(key, value);
}
};
if (Array.isArray(each)) {
updateChildren(each);
} else {
updateChildren(each());
if (each.$$isState) {
each.$$subscribe(updateChildren);
}
}
}
return h`<${tag} ...${attributes} oninitialize="${onInitialize}"></>`;
}try {
if (window) {
generateHtmlClass();
}
} catch (e) {}export{For,conditionalState,createDerivedState,createEffect,createExtRef,createPropertyObserver,createRef,createState,defineExtClass,destroy,generateHtmlClass,h,initialize,toggleState};