/** Datei: assets/js/arunox.OLD/templates.js **/ /** * /arunox/core/templates.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ window.ARUNOX ??= {}; ARUNOX.Templates = { _type : 'templates', _componentData : { content : undefined, config : undefined, data : undefined, htmlTag : 'div', template : null, container : null, // DOM-Object root : null, // DOM-Object parent : null, // ARX-Object children : [], // ARX-Object nodes : () => Object.create(null), hooks : () => Object.create(null), keepOnReplace : () => ({ classes : [], // CSS-Klassen die erhalten bleiben sollen attributes : [], // Attribute die erhalten bleiben sollen datasets : [], // Data-Attribute die erhalten bleiben sollen structure : null, // DOM-Struktur die erhalten werden soll }), takeOnReplace : () => ({ classes : [], // CSS-Klassen die übernommen werden sollen attributes : [], // Attribute die übernommen werden sollen datasets : [], // Data-Attribute die übernommen werden sollen }), mounted : false, destroyed : false, }, _componentProto : { apply : function( hook, ...args ) { ARUNOX.Hooks.apply.call( this, hook, ...args ); if( this.extends ) ARUNOX.Hooks.apply.call( this, `${this.extends}->${hook}`, ...args ); ARUNOX.Hooks.apply.call( this, `${this.template}->${hook}`, ...args ); }, filter : function( hook, value, ...args ) { value = ARUNOX.Hooks.filter.call( this, hook, value, ...args ); if( this.extends ) value = ARUNOX.Hooks.filter.call( this, `${this.extends}->${hook}`, value, ...args ); return ARUNOX.Hooks.filter.call( this, `${this.template}->${hook}`, value, ...args ); }, render : function( templateName, config, data, container ) { return ARUNOX.Templates.render( templateName, config, data, container || this.root, this ); }, replace : function( template, config, data ) { const newCtx = typeof template === 'object' ? template : ARUNOX.Templates.render( template, config, data, null, null ); const newRoot = newCtx && newCtx.root instanceof HTMLElement ? newCtx.root : null; if( !newRoot ) return this.root; const preserveData = this._collectPreservables( this, newCtx ); if( this.mounted && this.root instanceof HTMLElement ) { this.root.replaceWith( newRoot ); } this._applyPreserved( newRoot, preserveData ); this.root = newRoot; this.nodes = newCtx.nodes || Object.create(null); return this; }, node : function( nodeName, nodeConfig = {}, placement = 'append', refElement = null ) { if( !this.nodes ) this.nodes = {}; if( !this.nodes[ nodeName ] ) { nodeConfig = typeof nodeConfig === 'object' ? nodeConfig : { textNode: nodeConfig }; nodeConfig = Object.assign( { htmlTag : 'div', datasets : { arx : { node : nodeName, }, }, }, nodeConfig ); nodeConfig = this.filter(`node`, nodeConfig ); nodeConfig = this.filter(`node${nodeName.toUpperCaseFirst()}`, nodeConfig ); refElement = refElement instanceof HTMLElement ? refElement : refElement?.root || null; const nodeCtx = ARUNOX.Templates._createContext( nodeName, nodeConfig, this, this.root, { htmlTag: nodeConfig.htmlTag }); const ctxRender = nodeCtx.render; const ctxReplace = nodeCtx.replace; nodeCtx.render = function( template, config, data, container ) { if( !this.root ) { const tmp = ARUNOX.Templates.render( this.config.htmlTag, this.config, {}, this.parent?.root, this.parent ); this.root = tmp.root; this.root.ARX = this; if( placement === 'prepend' ) this.parent.root.prepend( this.root ); else if( placement === 'append' ) this.parent.root.append( this.root ); else if( placement === 'before' && refElement ) refElement.before( this.root ); else if( placement === 'after' && refElement ) refElement.after( this.root ); this.mounted = true; } return ctxRender.call( this, template, config, data, container || this.root ); }; nodeCtx.replace = function( template, config, data ) { if( !this.root ) this.render(); return ctxReplace.call( this, template, config, data ); } this.nodes[ nodeName ] = nodeCtx; } return this.nodes[ nodeName ]; }, closest : function( template ) { if( this.template == template || this.extends == template ) { return this; } return this.parent.closest( template ); }, dataset : function( dataset, namespace = 'arx' ) { dataset = typeof dataset === 'string' ? { [dataset] : true } : dataset; for( const [ key, value ] of Object.entries( dataset ) ) { this.root.setAttribute( `data-${namespace}-${key}`, Array.isArray( value ) ? value.join(',') : value ); } }, _collectPreservables : function( oldCtx, newCtx ) { const preserveData = { classes : new Set(), attributes : new Map(), datasets : new Map(), structure : null }; if( oldCtx.keepOnReplace ) this._collectPreserveData( oldCtx.root, oldCtx.keepOnReplace, preserveData ); if( newCtx.takeOnReplace ) this._collectPreserveData( oldCtx.root, newCtx.takeOnReplace, preserveData ); return preserveData; }, _collectPreserveData : function( element, rules, preserveData ) { if( !element ) return; // CSS Classes if( rules.classes ) { rules.classes.forEach( pattern => { const matchingClasses = this._matchClasses( element, pattern ); matchingClasses.forEach( cls => preserveData.classes.add( cls ) ); }); } // Attributes if( rules.attributes ) { rules.attributes.forEach( pattern => { const matchingAttrs = this._matchAttributes( element, pattern ); matchingAttrs.forEach( ([name, value]) => { preserveData.attributes.set( name, value ); }); }); } // Datasets if( rules.datasets ) { rules.datasets.forEach( pattern => { const matchingData = this._matchDatasets( element, pattern ); matchingData.forEach( ([name, value]) => { preserveData.datasets.set( name, value ); }); }); } // Structure (komplexerer Fall - erstmal placeholder) if( rules.structure ) { preserveData.structure = rules.structure; } }, _applyPreserved : function( newRoot, preserveData ) { preserveData.classes.forEach( cls => { newRoot.classList.add( cls ); }); preserveData.attributes.forEach( (value, name) => { newRoot.setAttribute( name, value ); }); preserveData.datasets.forEach( (value, name) => { newRoot.setAttribute( `data-${name}`, value ); }); // Structure handling würde hier kommen (komplexer) if( preserveData.structure ) { // TODO: Implementierung für Structure-Preservation console.log( 'Structure preservation not yet implemented:', preserveData.structure ); } }, _matchClasses : function( element, pattern ) { const classes = Array.from( element.classList ); if( pattern.includes('*') ) { // Wildcard matching: "lcars-*" matches "lcars-panel", "lcars-frame", etc. const regex = new RegExp( '^' + pattern.replace(/\*/g, '.*') + '$' ); return classes.filter( cls => regex.test( cls ) ); } else { // Exact match return classes.includes( pattern ) ? [pattern] : []; } }, _matchAttributes : function( element, pattern ) { const attributes = Array.from( element.attributes ); const matching = []; attributes.forEach( attr => { if( pattern.includes('*') ) { const regex = new RegExp( '^' + pattern.replace(/\*/g, '.*') + '$' ); if( regex.test( attr.name ) ) { matching.push( [attr.name, attr.value] ); } } else if( attr.name === pattern ) { matching.push( [attr.name, attr.value] ); } }); return matching; }, _matchDatasets : function( element, pattern ) { const matching = []; // Durchsuche alle data-* Attribute Array.from( element.attributes ).forEach( attr => { if( attr.name.startsWith('data-') ) { const dataName = attr.name.substring(5); // Remove 'data-' prefix if( pattern.includes('*') ) { const regex = new RegExp( '^' + pattern.replace(/\*/g, '.*') + '$' ); if( regex.test( dataName ) ) { matching.push( [dataName, attr.value] ); } } else if( dataName === pattern ) { matching.push( [dataName, attr.value] ); } } }); return matching; }, }, register : function( template, callback, priority ) { return ARUNOX.Registry.register( this._type, template, callback, priority ); }, once : function( template, callback, priority = 10 ) { return ARUNOX.Registry.once( this._type, template, callback, priority ); }, remove : function( template, callback ) { return ARUNOX.Registry.remove( this._type , template, callback ); }, resolve : function( template ) { const mergeHooks = (baseHooks={}, childHooks={}) => { const out = {}; for( const [ key, fn ] of Object.entries( baseHooks ) ) { out[ key ] = Array.isArray( fn ) ? fn : [ fn ]; } for( const [ key, fn ] of Object.entries( childHooks ) ) { const hooks = Array.isArray( fn ) ? fn : [ fn ]; out[ key ] = out[ key ] ? [ ...out[ key ], ...hooks ] : hooks; } return out; }; const extendsSupport = function( childName ) { const base = ARUNOX.Templates.resolve( this.extends ); const merged = { ...base, ...this }; merged.hooks = mergeHooks( base.hooks, this.hooks ); // merged.extends = base; // delete merged.extends; return merged; }; let def = ARUNOX.Registry.resolve( this._type, template, extendsSupport ); // if( !def && !template.match(/\//)) { // if( ARUNOX.Loader.loadSync(`${ARUNOX.themeComponentPath}/${template}.js`) ) { // ARUNOX.Loader.loadSync(`${ARUNOX.themeHookPath}/${template}.js`); // // def = ARUNOX.Registry.resolve( this._type, template, extendsSupport ); // } // } if( !def ) { if( template.match(/\//) ) return false; if( !( document.createElement( template ) instanceof HTMLUnknownElement ) ) { return { htmlTag: template }; } return false } if( typeof def === 'function' ) return { builder: def }; if( typeof def === 'object') { if( !def.htmlTag ) { if( !( document.createElement( template ) instanceof HTMLUnknownElement ) ) { return { ...def, htmlTag: template }; } throw new Error(`[ARUNOX.Templates] Invalid definition for: ${template}`); } if( def.hooks ) { // Normalisiere alle hooks zu Arrays for (const [key, fn] of Object.entries(def.hooks)) { if (!Array.isArray(fn)) { def.hooks[key] = [fn]; } } } return def; } throw new Error(`[Templates] Invalid definition for: ${template}`); }, exists : function( template ) { return ARUNOX.Registry.exists( this._type, template ); }, all : function( template ) { return [ ...ARUNOX.Registry.all( this._type, template ) ].reverse(); }, config : function( template, config, data ) { return { template, config, data }; }, render : function( template, config, data, container, parentCtx ) { if( !template ) return; const definition = this.resolve( template ); if( !definition ) return; config = Object.assign( {}, config ); const ctx = this._createContext( template, config, parentCtx || null, container, definition ); this._registerHooks( template, ctx ); config = ctx.config = ctx.filter( 'config', config, data ); data = ctx.data = ctx.filter( 'data', data, config ); if( ctx.filter( 'shouldRender', true, config, data ) ) { ctx.apply( 'beforeRender', config, data ); this._buildRoot( definition, ctx, config, data ); this._collectNodes( ctx ); this._applyConfig( ctx, config ); this._renderContent( ctx, config, data ); this._mount( ctx, container ); queueMicrotask( ()=>{ this._collectNodes( ctx ); ctx.apply( 'afterRender', config, data ); }); return ctx; } if( ctx.filter( 'shouldRebuild', true, config, data ) ) { ctx.apply( 'beforeRebuild', config, data ); this._applyConfig( ctx, config ); // Config kann sich geändert haben this._renderContent( ctx, config, data ); // Neuer Content queueMicrotask( ()=> { this._collectNodes( ctx ); ctx.apply( 'afterRebuild', config, data ); }); return ctx; } return ctx; }, _registerHooks : function( template, ctx ) { for( const [ hookKey, hookDef ] of Object.entries( ctx.hooks ) ) { const hookArray = Array.isArray( hookDef ) ? hookDef : [ hookDef ]; hookArray.forEach( def => { // Parse Hook-Definition const hooks = this._parseHookDefinition( hookKey, def, template ); // Registriere alle geparsten Hooks hooks.forEach( hookConfig => { const originalCallback = hookConfig.callback; const wrappedCallback = function( ...args ) { this.owner = ctx; return originalCallback.call( this, ...args ); }; wrappedCallback._originalCallback = originalCallback; if( !ARUNOX.Hooks.exists( `${hookConfig.template}->${hookConfig.hook}`, originalCallback ) ) { if( hookConfig.once ) { ARUNOX.Hooks.once( `${hookConfig.template}->${hookConfig.hook}`, wrappedCallback, hookConfig.priority ); } else { ARUNOX.Hooks.register( `${hookConfig.template}->${hookConfig.hook}`, wrappedCallback, hookConfig.priority ); } } }); }); } }, _parseHookDefinition : function( hookKey, hookDef, currentTemplate ) { // Function → Standard Hook für eigenes Template if( typeof hookDef === 'function' ) { return [{ template : currentTemplate, hook : hookKey, callback : hookDef, priority : 9, once : false }]; } // Object → Vollständige Definition let templates, hookName; // Templates ermitteln if( hookDef.templates ) { templates = hookDef.templates; } else if( hookDef.template ) { templates = [ hookDef.template ]; } else if( hookKey.includes('->') ) { templates = [ hookKey.split('->')[0] ]; } else { templates = [ currentTemplate ]; } // Hook-Name ermitteln if( hookDef.hook ) { hookName = hookDef.hook; } else if( hookKey.includes('->') ) { hookName = hookKey.split('->')[1]; } else { hookName = hookKey; } // Für jedes Template eine Hook-Config erstellen return templates.map( templateName => ({ template : templateName, hook : hookName, callback : hookDef.callback, priority : hookDef.priority || 9, once : hookDef.once || false })); }, _createContext : function( template, config, parent = null, container = null, definition ) { const prototype = Object.create( this._componentProto ); const context = Object.assign( prototype, definition ); for( const key in this._componentData ) { if( typeof context[ key ] !== 'undefined' ) continue; const val = this._componentData[ key ]; context[ key ] = typeof val === 'function' ? val() : val; } if( definition.keepOnReplace || config.keepOnReplace ) { context.keepOnReplace = this._mergeReplaceRules( definition.keepOnReplace || {}, config.keepOnReplace || {} ); } if( definition.takeOnReplace || config.takeOnReplace ) { context.takeOnReplace = this._mergeReplaceRules( definition.takeOnReplace || {}, config.takeOnReplace || {} ); } context.extends = definition.extends; context.template = template; context.parent = parent; context.container = container || null; context.config = config || {}; // console.log( definition ); // console.log( template, context ); return context; }, _mergeReplaceRules : function( templateRules, configRules ) { return { classes: [ ...(templateRules.classes || []), ...(configRules.classes || []) ], attributes: [ ...(templateRules.attributes || []), ...(configRules.attributes || []) ], datasets: [ ...(templateRules.datasets || []), ...(configRules.datasets || []) ], structure: configRules.structure || templateRules.structure || null }; }, _createElement : function( tagResolver, config, data, ctx ) { // Tag ermitteln (String oder Function(config, data, ctx) -> String) const tag = ( typeof tagResolver === 'function' ) ? tagResolver( config, data, ctx ) : ( tagResolver || 'div' ); return document.createElement( String(tag || 'div') ); }, _buildRoot : function( definition, ctx, config, data ) { ctx.apply( 'buildRootBefore' ); // 1) Root vorab erstellen (Definition darf optional `tag` liefern: string | fn(cfg,data,ctx) -> string) if( !ctx.root ) { ctx.root = this._createElement( definition.htmlTag, config, data, ctx ); } // 2) Definition ausführen – nur wenn render existiert (permissive) if( typeof definition.builder === 'function' ) { const out = definition.builder.call( ctx, config, data ); if( out instanceof HTMLElement ) { ctx.root = out; } else if( typeof out === 'string' ) { const tpl = document.createElement('template'); tpl.innerHTML = out.trim(); ctx.root = tpl.content.firstElementChild || ctx.root; } } // 3) Inputs persistieren ctx.config = config; ctx.data = data; ctx.root.ARX = ctx; ctx.apply( 'buildRootAfter' ); return ctx.root; }, _collectNodes : function( ctx ) { ctx.apply( 'collectNodesBefore' ); if( !ctx.root ) return; // console.log( ctx.root.querySelectorAll('[data-arx-node]') ); ctx.root.querySelectorAll('[data-arx-node]').forEach( element => { const nodeName = element.getAttribute('data-arx-node'); if( nodeName && element.ARX && !ctx.nodes[ nodeName ] ) { ctx.nodes[ nodeName ] = element.ARX; } }); ctx.apply( 'collectNodesAfter' ); }, _applyConfig : function( ctx, config ) { if( !ctx.root || !config ) return; const el = ctx.root; ctx.apply( 'applyConfigBefore', el, config ); if( config.classes ) { if( typeof config.classes === 'string' ) el.classList.add( ...config.classes.split(/\s+/).filter( Boolean ) ); else if( Array.isArray( config.classes ) ) el.classList.add( ...config.classes ); else if( typeof config.classes === 'object' ) { const { add = [], remove = [] } = config.classes; if( add.length ) el.classList.add( ...add ); if( remove.length ) el.classList.remove( ...remove ); } } if( typeof config === 'object' ) { for( const key of Object.keys( config ) ) { const match = key.match( /^on([a-z].*)/i ); if( !match ) continue; config.events ??= {}; config.events[ match[1].toLowerCase() ] = config[ key ]; } } if( typeof config.events === 'object' ) { for( const [ name, callback ] of Object.entries( config.events ) ) el.addEventListener( name, callback ); } if( typeof config.styles === 'object' ) { for (const [k, v] of Object.entries( config.styles )) el.style[k] = v; } if( typeof config.datasets === 'object' ) { const applyDataset = ( obj, prefix = '' ) => { for( const [ k, v ] of Object.entries(obj)) { const key = prefix ? `${prefix}-${k}` : k; if( v && typeof v === 'object' && !Array.isArray( v ) ) { applyDataset( v, key ); // rekursiv } else { el.setAttribute( `data-${key}`, String( v ) ); } } }; applyDataset( config.datasets ); } if( typeof config.attributes === 'object' ) { for (const [k, v] of Object.entries( config.attributes )) { if (v === false || v == null) el.removeAttribute(k); else el.setAttribute(k, String(v)); } } if( typeof config.apply === 'object' ) ctx.apply( ...config.apply ); if( typeof config.innerHTML === 'string' ) ctx.root.innerHTML = config.innerHTML; if( typeof config.innerText === 'string' ) ctx.root.innerText = config.innerText; ctx.apply( 'applyConfigAfter', el, config ); }, _renderContent : function( ctx, config, data ) { const content = ctx.filter( 'content', config?.content, config, data ); if( typeof content === 'string' ) { ctx.root.innerText = content; return; } if( Array.isArray( content ) ) { content.filter( Boolean ).forEach( item => { ctx.render( item.template || item.generator, item.config, item.data ); }); return; } if( typeof content === 'object' ) { return ctx.render( content.template || content.generator, content.config, content.data ); } }, _mount : function( ctx, container ) { if( !ctx || !ctx.root ) return ctx; ctx.apply( 'mount' ); // Ziel ermitteln: explizites container-Arg → sonst ctx.container let target = ( container !== undefined ? container : ctx.container ); if( !target ) return ctx; // Selector-String erlauben if( typeof target === 'string' ) target = document.querySelector( target ); // ARX als Container erlauben else if( target && typeof target === 'object' && target.root instanceof HTMLElement ) target = target.root; if( !( target instanceof HTMLElement ) ) return ctx; target.appendChild( ctx.root ); ctx.container = target; ctx.mounted = true; ctx.apply( 'mounted' ); return ctx; }, }; /** Datei: assets/js/arunox.OLD/utilities.js **/ /** * /arunox/core/utilities.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ window.ARUNOX ??= {}; ARUNOX.Utils = { device: { get isRotatable() { return window.orientation > -1; }, get isMobile() { return this.isRotatable || screen.availHeight <= 800 || screen.availWidth <= 800; }, get screenSize() { return { width: screen.availWidth, height: screen.availHeight }; } }, // Weitere Utils können hier hinzugefügt werden dom: { ready(callback) { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', callback); } else { callback(); } } } }; /** Datei: assets/js/arunox.OLD/prototypes.js **/ /** * /arunox/core/prototypes.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ Object.defineProperty( DOMTokenList.prototype, 'removeRexEx', { value: function( regex ) { if( !( regex instanceof RegExp ) ) { throw new TypeError('DOMTokenList.removeRegex erwartet ein RegExp-Objekt.'); } const toRemove = [ ...this ].filter( cls => regex.test( cls ) ); if( toRemove.length ) this.remove( ...toRemove ); }, enumerable : false, writable : true, configurable: true }); Object.defineProperty( DOMTokenList.prototype, 'match', { value: function( regex, returnMatchIndex = 1 ) { if( !( regex instanceof RegExp ) ) { throw new TypeError('DOMTokenList.match erwartet ein RegExp-Objekt.'); } return [ ...this ] .map( cls => regex.exec( cls ) ) .filter( Boolean ) .map( m => m[ returnMatchIndex ] ); }, enumerable : false, writable : true, configurable: true }); Object.defineProperty( Object.prototype, 'assignRecursive', { value: function( target, ...sources ) { for( const source of sources ) { for( const [ key, item ] of Object.entries( source ) ) { if( item === null ) { delete target[ key ]; continue; } if( Array.isArray( item ) ) { target[ key ] = [].concat( item, target[ key ] || [] ); continue; } if( typeof item === 'object' && item !== null ) { if( typeof target[ key ] !== 'object' || target[ key ] === null ) { target[ key ] = target[ key ] === undefined ? {} : { [key] : target[ key ] }; } target[ key ] = Object.assignRecursive( target[ key ] || {}, item ); continue; } target[ key ] = item; } } return target; }, enumerable : false, writable : true, configurable: true }); Object.defineProperty( String.prototype, 'toUpperCaseFirst', { value: function() { return String( this ).charAt(0).toUpperCase() + String( this ).slice(1); }, enumerable : false, writable : true, configurable: true }); /** Datei: assets/js/arunox.OLD/loader.js **/ /** * /arunox/core/loader.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ window.ARUNOX ??= {}; ARUNOX.Loader = { _loaded : new Set(), _loading : new Map(), load : async function( path, options = {} ) { const { force = false, cache = true, type = 'module' } = options; if( !force && this._loaded.has( path ) ) return Promise.resolve(); // Already loaded? if( this._loading.has( path ) ) return this._loading.get( path ); // Currently loading? const loadPromise = this._loadModule( path, type, cache ); this._loading.set( path, loadPromise ); try { await loadPromise; this._loaded.add( path ); } catch( error ) { console.error( `Failed to load module: ${path}`, error ); throw error; } finally { this._loading.delete( path ); } return loadPromise; }, loadAll : async function( modules ) { const promises = modules.map( module => { if( typeof module === 'string' ) { return this.load( module ); } return this.load( module.path, module.options || {} ); }); return Promise.all( promises ); }, loadIf : async function( condition, path, options = {} ) { if( typeof condition === 'function' ) { condition = condition(); } if( condition ) return this.load( path, options ); return Promise.resolve(); }, loadSync : function( path ) { if( this._loaded.has( path ) || this._loaded.has(path + ':404' ) ) return this._loaded.has( path ); try { const xhr = new XMLHttpRequest(); xhr.open('GET', path, false); // SYNCHRON! xhr.send(); if( xhr.status === 200 ) { const script = document.createElement('script'); // script.type = 'module'; script.textContent = xhr.responseText; document.head.appendChild( script ); document.head.removeChild( script ); this._loaded.add( path ); return true; } if( xhr.status === 404 ) { // 404 merken, damit wir es nicht nochmal versuchen this._loaded.add( path + ':404' ); return false; } return false; } catch( e ) { return false; } }, isLoaded : function( path ) { return this._loaded.has( path ); }, unload : function( path ) { this._loaded.delete( path ); }, _loadModule : function( path, type, cache ) { return new Promise(( resolve, reject ) => { const script = document.createElement('script'); script.onload = () => { document.head.removeChild( script ); resolve(); }; script.onerror = () => { document.head.removeChild( script ); reject( new Error( `Failed to load script: ${path}` ) ); }; if( type === 'module' ) { script.type = 'module'; } if( !cache ) { const url = new URL( path, window.location.origin ); url.searchParams.set( '_t', Date.now() ); script.src = url.toString(); } else { script.src = path; } document.head.appendChild( script ); }); }, }; /** Datei: assets/js/arunox.OLD/hooks.js **/ /** * /arunox/core/hooks.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ window.ARUNOX = window.ARUNOX || {}; ARUNOX.Hooks = { _type : 'hooks', register : function( name, callback, priority ) { return ARUNOX.Registry.register( this._type, name, callback, priority ); }, once : function( name, callback, priority = 10 ) { return ARUNOX.Registry.once( this._type, name, callback, priority ); }, remove : function( name, callback ) { return ARUNOX.Registry.remove( this._type , name, callback ); }, exists : function( name, callback ) { return ARUNOX.Registry.exists( this._type, name, callback ); }, resolve : function( name ) { return ARUNOX.Registry.resolve( this._type, name ); }, all : function( name ) { return [ ...ARUNOX.Registry.all( this._type, name ) ].reverse(); }, filter : function( name, value, ...args ) { let hookNames = Array.isArray( name ) ? name : name.split(' '); let callbacks = hookNames.map( n => ARUNOX.Hooks.all( n ) ).flat(2).filter(Boolean); let stopped = false; let context = { stop : function() { stopped = true; }, instance : this }; if( this.hooks?.hooks ) { callbacks = callbacks.concat( hookNames.map( n => this.hooks.hooks[ n ] ).filter(Boolean).flat() ); } // if( callbacks.length ) console.log( callbacks, context.instance ); for( const fn of callbacks ) { if( stopped ) break; if( fn.ARX?.once && fn.ARX?.done ) continue; value = fn.call( context, value, ...args ); if( fn.ARX?.once ) fn.ARX.done = true; } return value; }, apply : function( name, ...args ) { let hookNames = Array.isArray( name ) ? name : name.split(' '); let callbacks = hookNames.map( n => ARUNOX.Hooks.all( n ) ).flat(2).filter(Boolean); let stopped = false; let context = { stop : function() { stopped = true; }, instance : this }; if( this.hooks?.hooks ) { callbacks = callbacks.concat( hookNames.map( n => this.hooks.hooks[ n ] ).filter(Boolean).flat() ); } for( const fn of callbacks ) { if( stopped ) break; if( fn.ARX?.once && fn.ARX?.done ) continue; fn.call( context, ...args ); if( fn.ARX?.once ) fn.ARX.done = true; } }, Instance : class { constructor( owner, hooks ) { this.owner = owner; this.hooks = hooks || {}; } _hooks( name ) { return [...new Set([ name, this.owner.constructor?.extends ? `${this.owner.constructor.extends.type}->${name}` : null, `${this.owner.type}->${name}`, this.owner.constructor?.extends ? `${this.owner.constructor.extends.hook}->${name}` : null, `${this.owner.hook}->${name}`, ].filter(Boolean))]; } register( name, callback ) { this.hooks[ name ] ??= []; this.hooks[ name ].push( callback ); return this; } exists( name ) { return ARUNOX.Hooks.exists( name ); } filter( name, value, ...args ) { return ARUNOX.Hooks.filter.call( this.owner, this._hooks( name ), value, ...args ); } apply( name, ...args ) { ARUNOX.Hooks.apply.call( this.owner, this._hooks( name ), ...args ); } } }; /** Datei: assets/js/arunox.OLD/validator.js **/ window.ARUNOX = window.ARUNOX || {}; ARUNOX.Validator = { _type : 'validator', register : function( name, callback, priority ) { return ARUNOX.Registry.register( this._type, name, callback, priority ); }, once : function( name, callback, priority = 10 ) { return ARUNOX.Registry.once( this._type, name, callback, priority ); }, remove : function( name, callback ) { return ARUNOX.Registry.remove( this._type , name, callback ); }, resolve : function( name ) { return ARUNOX.Registry.resolve( this._type, name ); }, exists : function( name ) { return ARUNOX.Registry.exists( this._type, name ); }, all : function( name ) { return ARUNOX.Registry.all( this._type, name ); }, validate : function( name, ...args ) { const validator = this._validators[name]; if (typeof validator !== 'function') throw new Error('Validator nicht gefunden: ' + name); return validator( ...args); }, Instance : class { constructor( owner ) { this.owner = owner; this._validators = {}; } exists( name ) { return ARUNOX.Validator.exists( name ); } register( name, fn ) { if( typeof fn !== 'function' ) { fn = ARUNOX.Validator.resolve( name ); } this._validators[ name ] = fn; } validate( name, ...args ) { const validator = this._validators[ name ]; if( typeof validator !== 'function' ) return true; return validator( this.owner, ...args) ; } validateAll( ...args ) { for( const validator of Object.values( this._validators ) ) { if( typeof validator !== 'function' ) continue; const globalResult = validator( this.owner, ...args ); if( globalResult !== true ) return globalResult; } return true; } has( name ) { return typeof this._validators[ name ] === 'function'; } all() { return Object.keys( this._validators ); } on( listener, config, data ) { this.owner.element.addEventListener( listener, event => { const result = this.validateAll( config, data ); const name = result === false ? 'invalid' : 'valid' this.owner.apply( `validation.${name}`, config, result ); this.owner.element.dispatchEvent( new Event('validationComplete', { bubbles: true }) ); }); } } }; /** Datei: assets/js/arunox.OLD/theme.js **/ window.ARUNOX ??= {}; ARUNOX.Theme = { register : function( type, name, callback, priority, once = false ) { return ARUNOX.Registry.register( type, name, callback, priority, once ); }, once : function( type, name, callback, priority = 10 ) { return ARUNOX.Registry.once( type, name, callback, priority ); }, remove : function( type, name, callback ) { return ARUNOX.Registry.remove( type , name, callback ); }, resolve : function( type, name ) { return ARUNOX.Registry.resolve( type, name ); }, exists : function( type, name ) { return ARUNOX.Registry.exists( type, name ); }, all : function( type, name ) { return ARUNOX.Registry.all( type, name ); }, template : { _type : 'template', register : function( name, callback, priority ) { return ARUNOX.Theme.register( this._type, callback, priority ); }, remove : function( name, callback ) { return ARUNOX.Theme.remove( this._type , name, callback ); }, resolve : function( name ) { return ARUNOX.Theme.resolve( this._type, name ); }, exists : function( name ) { return ARUNOX.Theme.exists( this._type, name ); }, all : function( name ) { return ARUNOX.Theme.all( this._type, name ); }, config : function( name, ...args ) { return ARUNOX.Theme.config( this._type, name, ...args ); }, render : function( name, ...args ) { return ARUNOX.Theme.create( this._type, name, ...args ); }, renderAll : function( configs, data, container ) { return ARUNOX.Theme.createAll( this._type, configs, data, container ); }, }, wrapper : { _type : 'wrapper', register : function( name, callback, priority ) { return ARUNOX.Theme.register( this._type, callback, priority ); }, remove : function( name, callback ) { return ARUNOX.Theme.remove( this._type , name, callback ); }, resolve : function( name ) { return ARUNOX.Theme.resolve( this._type, name ); }, exists : function( name ) { return ARUNOX.Theme.exists( this._type, name ); }, all : function( name ) { return ARUNOX.Theme.all( this._type, name ); }, config : function( name, ...args ) { return ARUNOX.Theme.config( this._type, name, ...args ); }, render : function( name, ...args ) { return ARUNOX.Theme.create( this._type, name, ...args ); }, renderAll : function( configs, data, container ) { return ARUNOX.Theme.createAll( this._type, configs, data, container ); }, }, generator : { _type : 'generator', register : function( name, callback, priority ) { return ARUNOX.Theme.register( this._type, callback, priority ); }, remove : function( name, callback ) { return ARUNOX.Theme.remove( this._type , name, callback ); }, resolve : function( name ) { return ARUNOX.Theme.resolve( this._type, name ); }, exists : function( name ) { return ARUNOX.Theme.exists( this._type, name ); }, all : function( name ) { return ARUNOX.Theme.all( this._type, name ); }, config : function( name, ...args ) { return ARUNOX.Theme.config( this._type, name, ...args ); }, render : function( name, ...args ) { return ARUNOX.Theme.create( this._type, name, ...args ); }, renderAll : function( configs, data, container ) { return ARUNOX.Theme.createAll( this._type, configs, data, container ); }, }, htmlTag : { _type : 'htmltag', register : function( name, callback, priority ) { return ARUNOX.Theme.register( this._type, callback, priority ); }, remove : function( name, callback ) { return ARUNOX.Theme.remove( this._type , name, callback ); }, resolve : function( name ) { return ARUNOX.Theme.resolve( this._type, name ); }, exists : function( name ) { return ARUNOX.Theme.exists( this._type, name ); }, all : function( name ) { return ARUNOX.Theme.all( this._type, name ); }, config : function( name, ...args ) { return ARUNOX.Theme.config( this._type, name, ...args ); }, render : function( name, ...args ) { return ARUNOX.Theme.create( this._type, name, ...args ); }, renderAll : function( configs, data, container ) { return ARUNOX.Theme.createAll( this._type, configs, data, container ); }, }, config : function( type, name, config ) { return { type, generator: name, config }; }, create : function( type, name, config = {}, data, container ) { if( container instanceof HTMLElement ) { container = new ARUNOX.Theme.Component( container ); } let hook = `${type}.${name}`; let storage = container?.storage || {}; // if( name == 'components/button' ) console.log( name, `${hook}->config` ); [ type, name, hook, config, data ] = ARUNOX.Hooks.filter( `create`, [ type, name, hook, config, data ] ); [ type, name, hook, config, data ] = ARUNOX.Hooks.filter( `${type}->create`, [ type, name, hook, config, data ] ); [ type, name, hook, config, data ] = ARUNOX.Hooks.filter( `${type}.${name}->create`, [ type, name, hook, config, data ] ); // if( name == 'tabs.button' ) console.log( name, `${hook}->config` ); const typeTitle = type.charAt(0).toUpperCase() + type.slice(1); const typeClass = ARUNOX.Theme.resolve( type, name ) || class extends ARUNOX.Theme.Component {}; // if( typeof typeClass !== 'function' ) return; if( typeof typeClass !== 'function' ) throw new Error( `${typeTitle}-Klasse nicht gefunden: ${name}` ); // if( name == 'tabs.button' ) console.log( name, typeClass ); ARUNOX.Theme.bindHooks( type, name, hook ); let filteredData = data; filteredData = ARUNOX.Hooks.filter( `data`, filteredData, config, type, name, storage ); filteredData = ARUNOX.Hooks.filter( `${type}->data`, filteredData, config, type, name, storage ); filteredData = ARUNOX.Hooks.filter( `${hook}->data`, filteredData, config, type, name, storage ); let filteredConfig = config; filteredConfig = ARUNOX.Hooks.filter( `config`, filteredConfig, type, name, filteredData, storage ); filteredConfig = ARUNOX.Hooks.filter( `${type}->config`, filteredConfig, type, name, filteredData, storage ); filteredConfig = ARUNOX.Hooks.filter( `${hook}->config`, filteredConfig, type, name, filteredData, storage ); // if( name == 'button' ) console.log( name, type, hook, filteredConfig, typeClass ); if( typeClass.shouldCreate( filteredConfig, filteredData ) ) { const element = typeClass.createElement( filteredConfig, filteredData ); container?.append( element ); const typeInstance = new typeClass( element, container, { config: filteredConfig, data: filteredData } ); typeInstance.type = type; typeInstance.hook = hook; typeInstance.root = typeClass.rootName || null; typeInstance.apply( 'beforeRender' ); const created = typeInstance.create( typeInstance.storage.config, typeInstance.storage.data ); typeInstance.setData( { [type] : name }); typeInstance.apply( 'afterRender' ); return created?.ARX || null || typeInstance; } const typeInstance = typeClass.shouldRebuild( filteredConfig, filteredData ); if( typeInstance ) { const rebuilt = typeInstance?.ARX?.apply( 'rebuild', filteredConfig, filteredData ); return rebuilt?.ARX || null || typeInstance; } }, createAll : function( configs, data, container ) { if( configs && Array.isArray( configs ) ) { for( const contentElement of configs ) { if( contentElement.type && contentElement.generator && !contentElement.element ) { contentElement.element = ARUNOX.Theme.create( contentElement.type, contentElement.generator, contentElement.config, data, container ); } } return configs; } return false; }, bindHooks : function( type, name, hook ) { if( ARUNOX.Theme.exists( type, name ) ) { const typeClass = ARUNOX.Theme.resolve( type, name ); const typeHooks = [ 'create', 'config', 'data' ]; for( const typeHook of typeHooks ) { if( typeClass.extends ) { const extendClass = typeClass.extends.class; const extendHooks = ARUNOX.Hooks.all( `${typeClass.extends.hook}->${typeHook}` ); if( extendClass?.hooks?.[ typeHook ] && !typeClass.hooks[ `${hook}->${typeHook}` ]?.registered ) { // console.log( 'bindHooks', `${hook}->(${typeClass.extends.hook})->${typeHook}`, extendHooks ); for( const extendHook of extendHooks ) { ARUNOX.Hooks.register( `${hook}->${typeHook}`, extendHook, 8 ); } // ARUNOX.Hooks.register( `${hook}->${typeHook}`, extendClass.hooks[ typeHook ], 9 ); typeClass.hooks[ `${hook}->${typeHook}` ] = extendClass.hooks[ typeHook ]; typeClass.hooks[ `${hook}->${typeHook}` ].registered = true; } } if( typeClass?.hooks?.[ typeHook ] && !typeClass.hooks[ typeHook ]?.registered ) { // console.log( 'bindHooks', `${hook}->${typeHook}` ); ARUNOX.Hooks.register( `${hook}->${typeHook}`, typeClass.hooks[ typeHook ], 9 ); typeClass.hooks[ typeHook ].registered = true; } } return typeClass; } return false; }, Component : class { static htmlTag = 'div'; constructor( element, parent, storage ) { this.element = element; this.parent = parent; this.storage = storage; this.elements = this.storage?.config?.elements || {}; this.hooks = new ARUNOX.Hooks.Instance( this ); this.validators = new ARUNOX.Validator.Instance( this ); if( this.constructor.extends ) { for( const [ name, callback ] of Object.entries( this.constructor.extends.class.hooks ) ) { this.hooks.register( name, callback ); } } if( this.constructor.hooks ) { for( const [ name, callback ] of Object.entries( this.constructor.hooks ) ) { this.hooks.register( name, callback ); } } if( storage?.config?.hooks ) { for( const [ name, callback ] of Object.entries( storage.config.hooks ) ) { this.hooks.register( name, callback ); } delete storage.config.hooks; } // if( this.constructor.extends ) { // console.log( this.hooks.hooks ); // } element.ARX = this; return this; } clear() { if( this.elements.content ) this.elements.content.innerHTML = ''; else this.element.innerHTML = ''; return this; } reset() { const origin = this.origin || this.element; const fresh = origin.cloneNode(true ); fresh.className = origin.className; fresh.innerHTML = origin.innerHTML; this.element.replaceWith( fresh ); fresh.ARX = this; this.element = fresh; return this; } findRoot( rootName ) { const element = this.element.closest( rootName ? `[data-arx-root="${rootName}"]` : '[data-arx-root]'); return element?.ARX || element || null; } setOrigin( origin ) { return this.origin = origin; } replace( newElement, ...args ) { if( typeof newElement === 'string' ) { newElement = this.render( newElement, ...args ); } if( newElement instanceof ARUNOX.Theme.Component ) { newElement = newElement.element; } this.apply( 'beforeReplace', newElement ); for( const [ key, entry ] of Object.entries( this.element.dataset ) ) { newElement.dataset[ key ] = entry; } newElement.ARX.setOrigin( this.element ); this.origin = this.element; this.element.replaceWith( newElement ); // newElement.ARX = this; this.element = newElement; // console.log( newElement ); newElement.ARX.apply( 'afterReplace' ); // return this; return newElement.ARX; } insertBefore( parent ) { if( parent instanceof ARUNOX.Theme.Component ) { parent = parent.content || parent.element } parent.before( this.element ); return this; } before( ...children ) { for( let child of children ) { if( child instanceof ARUNOX.Theme.Component ) { child = child.element } if( this.elements?.content ) { this.elements.content.before( child ); } else this.element.before( child ); } return children[0]; } insertAfter( parent ) { if( parent instanceof ARUNOX.Theme.Component ) { parent = parent.content || parent.element } parent.after( this.element ); return this; } after( ...children ) { for( let child of children ) { if( child instanceof ARUNOX.Theme.Component ) { child = child.element } if( this.elements?.content ) { this.elements.content.after( child ); } else this.element.after( child ); } return children[0]; } appendTo( parent ) { if( parent instanceof ARUNOX.Theme.Component ) { parent = parent.content || parent.element } parent.append( this.element ); return this; } append( ...children ) { for( let child of children ) { if( child instanceof ARUNOX.Theme.Component ) { child = child.element; } if( this.elements?.content ) { this.elements.content.append( child ); } else this.element.append( child ); } return children[0]; } prependTo( parent ) { if( parent instanceof ARUNOX.Theme.Component ) { parent = parent.content || parent.element } parent.prepend( this.element ); return this; } prepend( ...children ) { for( let child of children ) { if( child instanceof ARUNOX.Theme.Component ) { child = child.element } if( this.elements?.content ) { this.elements.content.prepend( child ); } else this.element.prepend( child ); } return children[0]; } wrap( container ) { container.parent.append( this ); container.appendTo( this ); return this; } remove( onlyIfEmpty = false) { if( onlyIfEmpty ) { if( this.element.childElementCount ) return; } this.element.remove(); } render( name, config, data, alternate ) { if( alternate && !ARUNOX.Theme.generator.exists( name ) ) { return ARUNOX.Theme.generator.render( alternate, config, data, this ); } if( ARUNOX.Theme.htmlTag.exists( name ) ) { return ARUNOX.Theme.htmlTag.render( name, config, data, this ); } return ARUNOX.Theme.generator.render( name, config, data, this ); } renderFirst( name, config, data, alternate ) { const rendered = this.render( name, config, data, alternate ); this.prepend( rendered ); return rendered; } renderWrapper( name, config, data ) { return ARUNOX.Theme.wrapper.render( name, config, data, this ); } renderTemplate( name, config, data ) { return ARUNOX.Theme.template.render( name, config, data, this ); } renderAll( configs, data ) { return ARUNOX.Theme.createAll( configs, data, this ); } create( config, data ) { return this.element; } static createElement( config, data ) { return document.createElement( config?.htmlTag || this.htmlTag || 'div' ); } static shouldCreate( config, data ) { return !!config; } static shouldRebuild( config, data ) { return !!config; } filter( name, value, ...args ) { return this.hooks.filter( name, value, ...args ); } apply( name, ...args ) { this.hooks.apply( name, ...args ); } validate( name, value, ...args ) { return this.validators.validate( name, value, ...args ); } setData( dataset, name = 'arx' ) { dataset = typeof dataset === 'string' ? { [dataset] : true } : dataset; for( const [ key, value ] of Object.entries( dataset ) ) { this.element.setAttribute( `data-${name}-${key}`, Array.isArray( value ) ? value.join(',') : value ); } } } }; ARUNOX.Registry.generator.register( '#HTMLElement', class extends ARUNOX.Theme.Component {}); ARUNOX.Hooks.register( 'create.OLD', function( [ type, name, hook, config, data ] ) { if( type === 'generator' ) { try { if( !( document.createElement( name ) instanceof HTMLUnknownElement ) ) { type = 'htmltag'; hook = `${type}.${name}`; } } catch {} } if( config?.hook ) hook = config?.hook; return [ type, name, hook, config, data ]; }, 0 ); ARUNOX.Hooks.register( 'afterRender.OLD', function() { this.instance.element.querySelectorAll('[data-arx-component]').forEach(element => { const name = element.dataset.arxComponent; if( element instanceof HTMLElement ) { element = new ARUNOX.Theme.Component( element ); } this.instance.elements[ name ] = element; }); }); /** Datei: assets/js/arunox.OLD/router.js **/ /** * /arunox/core/router.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ window.ARUNOX = ARUNOX || {}; ARUNOX.Router = new ( class { constructor() { this._routes = {}; this._cache = new Map(); this._lastCacheKey = null; this._isReloaded = false; this._CACHE_TIMEOUT = 500; } async handle( config, sendData = {}, parseData = {}, mode = 'data' ) { const debug = false; let template = document.body; if( !config ) { console.warn("⚠️ Ungültige Konfiguration für handleAction()."); return; } if( !config.resolved ) { config = this.parseRoute( config, parseData, true, mode ); } if( debug ) console.groupCollapsed( config.resolved ); // console.log( config ); let response = { click : [], route : [], dialog : [], updater : [], wrapper : [], template : [], generator : [], javascript : [], }; for( const item of await this.fetch( config, sendData, {}, mode ) ) { response[ item.type ].push( item ); } // console.log( config, response ); ARUNOX.Router.history = config.history; if( response.dialog ) { for( const item of response.dialog ) { let dialog = null; if( item.name === '#ALL' ) { document.querySelectorAll('dialog').forEach( dialog => dialog.closeDialog() ); continue; } if( dialog = document.querySelector(`dialog[data-id="${item.name}"]`) ) { dialog.closeDialog(); continue; } if( dialog = document.querySelector('dialog') ) { dialog.closeDialog(); } } } if( response.route.length ) { for( const item of response.route ) { await ARUNOX.Router.handle( item, {}, {}, 'rest' ); } ARUNOX.Router.history = config.history; } if( response.template.length && !response.route.length ) { document.body.innerHTML = ''; // template = ARUNOX.Templates.render( response.template[0].template, Object.assign( {}, config ), response.template[0], document.body ); ARUNOX.Templates.render( response.template[0].template, response.template[0], Object.assign( {}, config ), document.body ); } // if( response.template.length && !response.route.length ) { // document.body.innerHTML = ''; // template = ARUNOX.Theme.template.render( response.template[0].template, Object.assign( {}, config ), response.template[0], document.body ); // } // // if( response.wrapper.length && !response.route.length ) { // for( const item of response.wrapper ) { // ARUNOX.Theme.wrapper.render( item.generator, item.config, item.list || item.data || {}, template ); // // if( item.history ) { // if( location.pathname != item.history ) { // window.history.pushState({}, "", item.history ); // } // } // } // } // // if( response.generator.length && !response.route.length ) { // for( const item of response.generator ) { // ARUNOX.Theme.generator.render( item.generator, config, item, template ); // } // } // // if( response.updater.length && !response.route.length ) { // for( const item of response.updater ) { // document.querySelectorAll(`[data-arx-uuid="${item.updater}"]`).forEach( element => element.ARX.apply( 'update', item.data, item.status ) ); // } // } // // if( response.click.length ) { // for( const item of response.click ) { // const element = document.querySelector(`[data-uuid="${item.parent}"] ${item.selector ?? ''}` ); // // element.click(); // } // } if( response.javascript.length ) { for( const item of response.javascript ) { eval( item.script ); } } if( debug ) console.groupEnd(); ARUNOX.Router.history = config.history; } async routes() { if( !this._isLoaded() ) { await this._loadList(); } return this._routes; } async fetch(config, params = {}, options = {}, mode = 'data') { try { let cached = this.getCache(!config, {}, mode); if (cached !== false) return cached; cached = this.getCache(config, params, mode); if (cached !== false) return cached; if (!config.resolved) config = this.parseRoute(config, params, true, mode); if (!config.resolved) throw new Error(`❌ Fehlerhafte API-URL: ${config.resloved.data}`); const cacheKey = `${config.method}:${config.cachekey}`; const defaultOptions = { method: config.method, headers: {}, ...options }; if (config.method === "GET" || config.method === "DELETE") { const queryString = new URLSearchParams(params).toString(); config.resolved += queryString ? `?${queryString}` : ""; } else { if (params instanceof FormData) { defaultOptions.body = params; } else { defaultOptions.body = JSON.stringify(params); defaultOptions.headers["Content-Type"] = "application/json"; } } const response = await fetch(config.resolved, defaultOptions); const data = await response.json(); const fullcache = data.caching || false; delete data.caching; this._cache.set(cacheKey, { response: data, fullcache: false, timestamp: Date.now() }); this._lastCacheKey = cacheKey; return data; } catch (error) { console.error('❌ Fehler beim Abrufen der API-Daten:', error); return null; } } async fetchData(config, params = {}, options = {}) { const response = await this.fetch(config, params, options); return response.data; } async fetchList(config, params = {}, options = {}) { const response = await this.fetch(config, params, options); return response.list; } async fetchConfig(config, params = {}, options = {}) { const response = await this.fetch(config, params, options); return response.config; } async fetchGenerator(config, params = {}, options = {}) { const response = await this.fetch(config, params, options); return response.generator; } async fetchStream(config, params = {}, options = {}, onChunk) { try { if (!config.resolved) config = this.parseRoute(config, params, true); if (!config.resolved) throw new Error(`❌ Fehlerhafte API-URL: ${config.resloved.data}`); const defaultOptions = { method: config.method, headers: {}, ...options }; const response = await fetch(config.resolved, defaultOptions); if (!response.body) return; const decoder = new TextDecoder(); const reader = response.body.getReader(); let buffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; if (onChunk) { buffer += decoder.decode(value, { stream: true }); let lines = buffer.split('\n'); buffer = lines.pop(); for (const line of lines) { if (!line.trim()) continue; try { onChunk(JSON.parse(line)); } catch { onChunk(line); } } } } if (onChunk && buffer.trim()) { try { onChunk(JSON.parse(buffer)); } catch { onChunk(buffer); } } } catch (error) { console.error('❌ Fehler beim Abrufen des API-Stream:', error); return null; } } async find(path, includeHidden = false) { await this._loadList(); return this._routes.find(route => { if (!includeHidden && route.visibility === "hidden") return false; const routePattern = new RegExp(`^${route.path.replace(/{\w+}/g, "([^/]+)")}$`); return routePattern.test(path); }); } getCache(config, params = {}, mode = 'data') { if (config === true && this._lastCacheKey) { const cached = this._cache.get(this._lastCacheKey); if (cached && Date.now() - cached.timestamp < this._CACHE_TIMEOUT) { cached.timestamp = Date.now(); return cached.response; } return null; } if (typeof config === 'object' || typeof config === 'string') { if (!config.resolved) config = this.parseRoute(config, params, {}, mode); const cacheKey = `${config.method}:${config.cachekey}`; const cached = this._cache.get(cacheKey); if (cached && Date.now() - cached.timestamp < this._CACHE_TIMEOUT) { this._lastCacheKey = cacheKey; return cached.response; } if (cached && cached.fullcache && location.pathname == config.cachekey) { return cached.response; } } return false; } getBreadcrumbSegments(path) { let segments = path.split('/').filter(Boolean); let breadcrumbs = []; let currentPath = ""; if (!segments.length) { segments = ['']; } segments.forEach(segment => { currentPath += `/${segment}`; const route = this._routes.find(r => r.path && new RegExp(`^${r.path.replace(/{\w+}/g, "([^/]+)")}$`).test(currentPath)); const group = this._routes.find(r => r.name && (new RegExp(`^${r.name.replace(/{\w+}/g, "([^/]+)")}$`).test(currentPath) || r.name == segment)); if (route) { breadcrumbs.push({ label: route.breadcrumb || route.label || segment, path: currentPath, }); } else if (group) { breadcrumbs.push({ label: group.breadcrumb || group.label || segment, path: false, }); } }); return breadcrumbs; } getDataRoute(config, params = {}, queryIncludes = true) { config = this.parseRoute(config ?? location.pathname, params, queryIncludes, 'data'); return config.resolved; } parseRoute( config, params = {}, queryIncludes = true, mode = 'data' ) { let route = typeof config === "string" ? config : config.route; let query = new URLSearchParams( config?.query || [] ); let connector = /\?/.test( route ) ? '&' : '?'; let resolved = null; let cachekey = null; if( query.size ) route += connector + query; if( !queryIncludes ) route = route.replace(/\?.*/, ''); switch (mode) { case 'rest': resolved = this._resolveApiUrl(route, params, '/rest'); cachekey = this._resolveApiUrl(route, params, '/rest', false); break; case 'data': resolved = this._resolveApiUrl(route, params, true); cachekey = this._resolveApiUrl(route, params, false, false); break; } return { route : route, resolved: resolved, cachekey: cachekey, history : this._resolveApiUrl(route, params, false), method : config?.method || "GET", query : config?.query || {}, }; } parseUrl(route, params) { const missingKeys = []; const resolvedUrl = route.replace(/{(\w+)}/g, (_, key) => { if (params[key] === 'undefined') { missingKeys.push(key); return `{${key}}`; } return params[key]; }).replace(/\/$/, ''); if (missingKeys.length > 0) { console.error(`Nicht ersetzte Platzhalter: ${missingKeys.join(', ')}`, `URL: ${route}`); return null; } return resolvedUrl; } _isLoaded() { return !!Object.entries( this._routes ).length; } async _loadList() { this._routes = await this.fetchList("/rest/routes"); } _resolveApiUrl(route, params = {}, prefix = true, hash = true) { const API_PREFIX = '/api/v1'; if (typeof prefix === 'string') { route = this._stripApiPrefix(route, API_PREFIX); route = route.startsWith(prefix) ? route : `${prefix}${route}`; } const mergedParams = params; const prefixedUrl = route.startsWith(API_PREFIX) ? route : `${API_PREFIX}${route}`; const resolvedUrl = this.parseUrl(prefixedUrl, params); if (!hash) { const withoutHash = resolvedUrl.replace(/#.*/, ''); return prefix ? withoutHash : this._stripApiPrefix(withoutHash, API_PREFIX); } return prefix ? resolvedUrl : this._stripApiPrefix(resolvedUrl, API_PREFIX); } _stripApiPrefix(route, api_prefix = '/api/v1') { if (route.startsWith(api_prefix)) { return route.replace(api_prefix, ''); } return route; } })(); ARUNOX.Router.history = null; /** Datei: assets/js/arunox.OLD/registry.js **/ /** * /arunox/core/regestry.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Creation of new ctx methods */ window.ARUNOX = window.ARUNOX || {}; ARUNOX.Registry = { _store : {}, register : function( type, name, callback, priority = 10, once = false ) { if( !this._store[ type ] ) this._store[ type ] = {}; if( !this._store[ type ][ name ]) this._store[ type ][ name ] = []; // callback.name = name; callback.once = once; callback.priority = priority; this._store[ type ][ name ].unshift( callback ); this._store[ type ][ name ].sort( ( a, b ) => b.priority - a.priority ); }, once : function( type, name, callback, priority = 10 ) { return this.register( type, name, callback, priority, true ); }, remove : function( type, name, callback ) { const arr = this._store[ type ]?.[ name ]; if (!arr) return; this._store[type][name] = arr.filter(e => e !== callback); }, resolve : function( type, name, extendsSupportCallback ) { const resolved = this._store[ type ]?.[ name ]?.[0]; if( !resolved ) return null; if( resolved?.extends && typeof resolved?.extends === 'string' ) { return extendsSupportCallback.call( resolved, name ); } return resolved; }, exists : function( type, name, callback, originCallback ) { if( this._store[ type ]?.[ name ] ) { return !!( this._store[ type ]?.[ name ]?.filter( item => item == callback || item._originalCallback == callback ).length ); } return false; }, all : function( type, name ) { if( !type ) return this._store; if( !name ) return Object.keys(this._store[ type ] || {} ); return this._store[ type ]?.[ name ] || []; }, template : { _type : 'template', register : function( name, callback, priority ) { return ARUNOX.Registry.register( this._type, name, callback, priority); }, remove : function( name, callback ) { return ARUNOX.Registry.remove( this._type, name, callback ); }, resolve : function( name ) { return ARUNOX.Registry.resolve( this._type, name ); }, exists : function( name ) { return ARUNOX.Registry.exists( this._type, name ); }, all : function( name ) { return ARUNOX.Registry.all( this._type, name ); }, }, wrapper : { _type : 'wrapper', register : function( name, callback, priority ) { return ARUNOX.Registry.register( this._type, name, callback, priority); }, remove : function( name, callback ) { return ARUNOX.Registry.remove( this._type, name, callback ); }, resolve : function( name ) { return ARUNOX.Registry.resolve( this._type, name ); }, exists : function( name ) { return ARUNOX.Registry.exists( this._type, name ); }, all : function( name ) { return ARUNOX.Registry.all( this._type, name ); }, }, generator : { _type : 'generator', register : function( name, callback, priority ) { return ARUNOX.Registry.register( this._type, name, callback, priority); }, remove : function( name, callback ) { return ARUNOX.Registry.remove( this._type, name, callback ); }, resolve : function( name ) { return ARUNOX.Registry.resolve( this._type, name ); }, exists : function( name ) { return ARUNOX.Registry.exists( this._type, name ); }, all : function( name ) { return ARUNOX.Registry.all( this._type, name ); }, }, htmlTag : { _type : 'htmltag', register : function( name, callback, priority ) { return ARUNOX.Registry.register( this._type, name, callback, priority); }, remove : function( name, callback ) { return ARUNOX.Registry.remove( this._type, name, callback ); }, resolve : function( name ) { return ARUNOX.Registry.resolve( this._type, name ); }, exists : function( name ) { return ARUNOX.Registry.exists( this._type, name ); }, all : function( name ) { return ARUNOX.Registry.all( this._type, name ); }, }, _mergeHooks : function( baseHooks = {}, childHooks = {} ) { const merged = {...baseHooks}; for (const [name, hookFn] of Object.entries(childHooks)) { if (name.startsWith('!')) { // explizites Überschreiben: "!config" const realName = name.slice(1); merged[realName] = hookFn; } else { // beide behalten → Liste draus machen if (merged[name]) { merged[name] = [].concat(merged[name], hookFn); } else { merged[name] = hookFn; } } } return merged; } };