/** Datei: themes/lcars/assets/js/components/dynamic.js **/ /** * /lcars/components/dynamic.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'dynamic', { extends : 'layout/section', hooks : { config : { priority : 5, callback : function( config, data ) { return { name : 'main_section_content', color : 'primary', backlink : config.backlink || {}, header : { html : 'h2', after : config.title, }, aside : 'right', content : ARUNOX.Templates.config( 'div', { classes : [ 'frame__content' ], datasets : { arx : { node : 'content', }, }, content : config?.content, }), footer : { after : config.copyright, }, }; }, }, afterRender : function( config, data ) { if( config.backlink?.route ) { this.instance.nodes.header.nodes.right.replace( 'button', { color : 'default', action : config.backlink, icon : 'bi-arrow-left', title : 'Zurück zur Übersicht', }); } }, }, }); /** Datei: themes/lcars/assets/js/hooks/button.js **/ /** * /lcars/hooks/button.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ import '/plugins/rest/assets/js/components/button.js'; ARUNOX.Hooks.register( 'button->config', function( config ) { return Object.assignRecursive( config, { block : 'frame', color : Object.assign( { color : 'primary', prefix : 'theme', }, config.color ), classes : [ config.classes, [ 'frame__element', 'frame__element--button', ]].flat().filter(Boolean), }); }, 5 ); if( 'vibrate' in navigator ) { ARUNOX.Hooks.register( 'button->afterRender', function() { this.instance.element.addEventListener( 'touchstart', event => { navigator.vibrate(50); }); }); } /** Datei: themes/lcars/assets/js/hooks/tabs.js **/ /** * /lcars/hooks/tabs.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Hooks.register( 'tabs->afterRender', function( config ) { const section = this.instance.closest( 'layout/section' ); const tabs = this.instance.closest( 'tabs' ); const buttons = this.instance.nodes.buttons; const actions = buttons.root.querySelectorAll( '.tabs__item--action' ); if( buttons ) { section.nodes.header.nodes.after.render(); section.nodes.header.nodes.after.root.append( buttons.root ); } if( actions.length ) { section.nodes.header.nodes.before.render(); section.nodes.header.nodes.before.nodes.inner.root.append( ...actions ); } }); /** Datei: themes/lcars/assets/js/hooks/menus.js **/ /** * /lcars/hooks/_general.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ import '/themes/lcars/assets/js/hooks/button.js'; ARUNOX.Hooks.register( 'addMenus', async function( anchor ) { const wrapper = this.instance.replace( 'div' ); const routes = ( await ARUNOX.Router.routes() ).map( item => { if( !Array.isArray( item.menu ) ) { item.menu = [ item.menu ]; } return item; }); const filtered = routes.filter( item => item.menu.includes( anchor ) ); for( const route of filtered ) { if( route.path ) { wrapper.apply( 'addMenus->item', route ); continue; } wrapper.apply( 'addMenus->category', route, routes ); } }); ARUNOX.Hooks.register( 'addMenus->category', async function( route, routes ) { const filtered = routes.filter( item => item.menu.includes( route.name ) ); const wrapper = this.instance.render( 'div', { classes : [ 'frame__wrapper' ], content : ARUNOX.Templates.config( 'div', { classes : [ 'frame__element', 'frame__element--label' ], content : ARUNOX.Templates.config( 'span', { classes : [ 'label' ], content : route.label, }), styles : { order : 0, } }), styles : { order : route?.order ?? 1, } }); for( const route of filtered ) { wrapper.apply( 'addMenus->item', route ); } }); ARUNOX.Hooks.register( 'addMenus->item', async function( route ) { this.instance.render( 'button', { label : route.label, icon : route.icon, action : route.path, styles : { order : route?.order ?? 1, } }); }); /** Datei: themes/lcars/assets/js/hooks/table.js **/ /** * /lcars/hooks/table.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Hooks.register( 'table->afterRender', function( config ) { const section = this.instance.closest( 'layout/section' ); const toolbar = this.instance.nodes.toolbar; const search = this.instance.nodes.searchContainer; const filter = this.instance.nodes.filterContainer; const buttons = this.instance.nodes.buttons; const wrapper = this.instance.nodes.wrapper; if( search ) { section.nodes.footer.nodes.after.render(); section.nodes.footer.nodes.after.nodes.inner.replace( search ); section.nodes.aside.nodes.before.render(); section.nodes.aside.nodes.before.replace( filter ); } if( buttons ) { section.nodes.header.nodes.before.render(); section.nodes.header.nodes.before.replace( buttons ); } toolbar.root.remove(); }); ARUNOX.Hooks.register( 'table->config', function( config ) { if( config.buttons ) { for( const button of config.buttons ) { button.config.color = 'secondary'; } } return config; }); ARUNOX.Hooks.register( 'button->config.node->filterReset', function( config ) { config.styles.order = 1; config.color = 'primary'; return config; }); ARUNOX.Hooks.register( 'button->active', function( active ) { const config = this.instance.config; const nodeName = config?.nodeName; // console.log( nodeName, active ); if( nodeName == 'searchReset' ) { this.instance.root.style.display = !active ? 'none' : null; return; } if( nodeName == 'filterReset' ) { this.instance.root.disabled = !active; return; } if( nodeName.match( /^filter[A-Z][a-zA-Z]*-.*/ ) ) { // if( active ) this.instance.root.classList.remove( 'inactive' ); // else this.instance.root.classList.add( 'inactive' ); if( active ) this.instance.root.classList.add( 'active' ); else this.instance.root.classList.remove( 'active' ); return; } }); /** Datei: themes/lcars/assets/js/hooks/notifications.js **/ /** * /lcars/hooks/notifications.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ import '/plugins/rest/assets/js/components/notifications.js'; ARUNOX.Hooks.register( 'notifications->afterRender', function() { const section = this.instance.closest( 'layout/section' ); if( section ) { section.nodes.header.nodes.before.render(); section.nodes.header.nodes.before.replace( this.instance ); this.instance.addNotification({ duration : 0, icon : 'bi-bell', title : 'Benachrichtigungen', message : 'Keine Benachrichtigungen', style : { order : -1, }, }); } }); ARUNOX.Hooks.register( 'notification->afterRender', function( config ) { if( config.duration === 0 ) return; config.delay ??= 2000; config.animate ??= config.delay <= 500 ? 100 : 500; if( typeof ARUNOX.Tools?.Beep === 'function' ) { if( config.type == 'danger' ) ARUNOX.Tools.Beep.alert(); else if( config.type == 'success' ) ARUNOX.Tools.Beep.short(); else if( config.type == 'warning' ) ARUNOX.Tools.Beep.long(); } window.setTimeout( () => { this.instance.parent.root.classList.add( 'hover' ); window.setTimeout( () => { this.instance.parent.root.classList.remove('hover'); // if( config.redirect !== false && config.redirect.url !== false ) { // if( config.redirect === true ) location.reload(); // else location.href = config.redirect.url; // } window.setTimeout( () => { this.instance.root.classList.add( 'fade-out' ); window.setTimeout( () => { this.instance.root.remove(); }, 250 ); }, config.message ? 2000 : 0 ); }, config.delay || 2000 ); }, config.animate || 500 ); }); /** Datei: themes/lcars/assets/js/templates/default.js **/ import '/themes/lcars/assets/js/hooks/menus.js'; ARUNOX.Templates.register( 'default', { extends : 'layout/section', hooks : { config : { priority : 5, callback : function( config, data ) { return { name : 'main_section', header : { html : 'h1', size : ARUNOX.Utils.device.isMobile ? 'sm' : 'md', after : config.title, }, aside : { size : 'xl', }, content : ARUNOX.Templates.config( 'div', { classes : [ 'frame__content' ], datasets : { arx : { node : 'content', }, }, content : config?.content, }), footer : { after : config.copyright, }, }; }, }, afterRender : function( config, data ) { this.instance.container.classList.removeRexEx( /template--.*/ ); this.instance.container.classList.add( 'theme' ); this.instance.nodes.footer.nodes.before.apply( 'addMenus', 'tools' ); this.instance.nodes.aside.nodes.before.apply( 'addMenus', 'sidebar' ); this.instance.render( 'notifications' ); }, }, }); /** Datei: themes/lcars/assets/js/templates/dialog.js **/ /** * /components/dialog.js * * Dialog-Component für LCARS - schiebt sich von rechts rein * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File created */ ARUNOX.Templates.register('dialog', { extends : 'layout/section', hooks : { config : { priority : 5, callback : function( config, data ) { return { name : 'dialog_section', color : 'secondary', header : { html : 'h3', size : 'sm', after : config.title, right : config.close ? { content : [ ARUNOX.Templates.config('button', { icon : 'bi bi-x-lg', color : 'danger', title : 'Schließen', datasets : { arx : { action : 'close-dialog' } } }) ] } : undefined }, aside : true, content : ARUNOX.Templates.config('div', { classes : ['frame__content'], content : config?.content, }), // footer : false, }; }, }, afterRender1 : function(config, data) { const mainSection = document.querySelector('[data-arx-node="main_section_content"]'); // Dialog-Wrapper erstellen falls nicht vorhanden let dialogWrapper = document.querySelector('.dialog__wrapper'); if (!dialogWrapper) { dialogWrapper = document.createElement('div'); dialogWrapper.classList.add('dialog__wrapper'); // Nach der main_section_content einfügen if (mainSection) { mainSection.parentElement.appendChild(dialogWrapper); } } // Dialog in Wrapper verschieben dialogWrapper.appendChild(this.instance.root); // Size-Klasse setzen const size = config.size || 'md'; this.instance.root.classList.add('dialog', `dialog--size-${size}`); // Animation triggern requestAnimationFrame(() => { this.instance.root.classList.add('dialog--open'); }); // Backdrop hinzufügen wenn gewünscht if (config.backdrop) { const backdrop = document.createElement('div'); backdrop.classList.add('dialog__backdrop'); dialogWrapper.appendChild(backdrop); // Backdrop verschieben damit es hinter dem Dialog ist dialogWrapper.insertBefore(backdrop, this.instance.root); requestAnimationFrame(() => { backdrop.classList.add('dialog__backdrop--visible'); }); } // Content-Section Größe anpassen if (mainSection) { mainSection.classList.add(`dialog-active--${size}`); } // Close-Handler this.instance.root.addEventListener('click', (event) => { const closeBtn = event.target.closest('[data-arx-action="close-dialog"]'); if (closeBtn || (config.backdrop && event.target.classList.contains('dialog__backdrop'))) { this.instance.apply('close'); } }); }, close : function() { const mainSection = document.querySelector('[data-arx-node="main_section_content"]'); const dialogWrapper = this.instance.root.closest('.dialog__wrapper'); const backdrop = dialogWrapper?.querySelector('.dialog__backdrop'); // Animation rückwärts this.instance.root.classList.remove('dialog--open'); if (backdrop) backdrop.classList.remove('dialog__backdrop--visible'); // Content-Section zurücksetzen if (mainSection) { mainSection.classList.removeRexEx(/dialog-active--.*/); } // Nach Animation entfernen setTimeout(() => { if (backdrop) backdrop.remove(); this.instance.root.remove(); // Wrapper entfernen wenn leer if (dialogWrapper && !dialogWrapper.querySelector('.dialog')) { dialogWrapper.remove(); } }, 300); } }, }); /** Datei: themes/lcars/assets/js/layouts/section/navbar.js **/ /** * /lcars/components/section/navbar.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Adaptation to new ctx method * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'layout/section/navbar', { htmlTag : 'nav', hooks : { config : function( config ) { config.datasets.arx.node = this.instance.htmlTag; config.classes.push( 'frame__navbar', `frame__navbar--${this.instance.htmlTag}` ); return config; }, corner : function( config, position ) { return Object.assign( { id : `${this.instance.parent.config.name}_${position}`, classes : [ 'frame__wrapper', 'frame__element', `frame__navbar--${position}` ], keepOnReplace : { classes : [ 'frame__*' ], }, content : [ ARUNOX.Templates.config( 'div', { classes : [ 'frame__wrapper__inner' ], }), ], }, config || {} ); }, toolbar : function( config, position ) { return Object.assign( { id : `${this.instance.parent.config.name}_${position}`, classes : [ 'frame__wrapper', `frame__navbar--${position}` ], keepOnReplace : { classes : [ 'frame__*' ], }, content : [ ARUNOX.Templates.config( 'div', { nodeName : 'inner', classes : [ 'frame__wrapper__inner' ], content : typeof config.textNode === 'string' ? [ ARUNOX.Templates.config( this.instance.config.html || 'div', { classes : [ 'frame__title' ], content : config.textNode, }) ] : config.content, }), ], }, config || {} ); }, nodeRight : function( config ) { return this.instance.filter( 'corner', config || {}, 'right' ); }, nodeBefore : function( config ) { return this.instance.filter( 'toolbar', config || {}, 'before' ); }, nodeCenter : function( config ) { return this.instance.filter( 'toolbar', config || {}, 'center' ); }, nodeAfter : function( config ) { return this.instance.filter( 'toolbar', config || {}, 'after' ); }, nodeLeft : function( config ) { if( this.instance.parent.config.aside.visibility ) { config = Object.assignRecursive( {}, config || {}, { classes : [ 'frame__element', 'frame__element--corner' ], datasets : { arx : { node : 'corner', }, }, }); } return this.instance.filter( 'corner', config || {}, 'left' ); }, }, builder : function( config, data ) { const fillBefore= this.render( 'div', { classes : [ 'frame__element', 'frame__element--fill' ], datasets : { arx : { node : 'fillBefore', }, } }); const fillAfter = this.render( 'div', { classes : [ 'frame__element', 'frame__element--fill' ], datasets : { arx : { node : 'fillAfter', }, } }); this.parent.root.classList.add( `frame--${this.htmlTag}-${config.size}` ); this.node( 'left', config.left || {}, 'prepend' ).render(); this.node( 'before', config.before, 'before', fillBefore ); this.node( 'center', config.center, 'before', fillAfter ); this.node( 'after', config.after, 'after', fillAfter ); this.node( 'right', config.right || {}, 'append' ).render(); if( config.before ) this.nodes.before.render(); if( config.center ) this.nodes.center.render(); if( config.after ) this.nodes.after.render(); }, }); ARUNOX.Templates.register( 'layout/section/header', { htmlTag : 'header', extends : 'layout/section/navbar', }); ARUNOX.Templates.register( 'layout/section/footer', { htmlTag : 'footer', extends : 'layout/section/navbar', }); /** Datei: themes/lcars/assets/js/layouts/section/sidebar.js **/ /** * /lcars/components/section/sidebar.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 1.0.0 * @since 1.0.0 File is created */ ARUNOX.Templates.register( 'layout/section/sidebar', { htmlTag : 'aside', hooks : { config : function( config ) { config.datasets.arx.node = this.instance.htmlTag; config.classes.push( 'frame__sidebar' ); return config; }, toolbar : function( config, position ) { return Object.assign( { id : `${this.instance.parent.config.name}_${position}`, classes : [ 'frame__wrapper', `frame__sidebar--${position}` ], keepOnReplace : { classes : [ 'frame__*' ], }, content : [ ARUNOX.Templates.config( 'div', { nodeName : 'inner', classes : [ 'frame__wrapper__inner' ], content : config.content, }), ], }, config || {} ); }, nodeBefore : function( config ) { return this.instance.filter( 'toolbar', config || {}, 'before' ); }, nodeCenter : function( config ) { return this.instance.filter( 'toolbar', config || {}, 'center' ); }, nodeAfter : function( config ) { return this.instance.filter( 'toolbar', config || {}, 'after' ); }, shouldRender : function( render, config, data ) { return this.instance.parent.config.aside.visibility; }, }, builder : function( config, data ) { const fillBefore= this.render( 'div', { classes : [ 'frame__element', 'frame__element--fill' ], datasets : { arx : { node : 'fillBefore', }, } }); const fillAfter = this.render( 'div', { classes : [ 'frame__element', 'frame__element--fill' ], datasets : { arx : { node : 'fillAfter', }, } }); this.parent.root.classList.add( `frame--sidebar-${config.size}`, `frame--sidebar-${config.side}` ); this.node( 'before', config.before, 'before', fillBefore ); this.node( 'center', config.center, 'before', fillAfter ); this.node( 'after', config.after, 'after', fillAfter ); if( config.before ) this.nodes.before.render(); if( config.center ) this.nodes.center.render(); if( config.after ) this.nodes.after.render(); }, }); /** Datei: themes/lcars/assets/js/layouts/section.js **/ /** * /lcars/components/section.js * * Some Javascript codes without further description * * @author Sparky * @copyright 2025 ARUNOX * @version 2.0.0 * @since 2.0.0 Adaptation to new ctx method * @since 1.0.0 File is created */ import '/themes/lcars/assets/js/layouts/section/navbar.js'; import '/themes/lcars/assets/js/layouts/section/sidebar.js'; ARUNOX.Templates.register( 'layout/section', { htmlTag : 'section', hooks : { config : function( config, data ) { // console.log( 'SECTION', config ); config.size ??= 'sm'; config.aside ??= false; config.header ??= {}; config.footer ??= {}; config.content ??= true; config.color ??= 'default'; if( typeof config.aside !== 'undefined' ) { if( typeof config.aside === 'string' ) config.aside = { side : config.aside }; if( typeof config.aside === 'boolean' ) config.aside = { visibility : config.aside }; config.aside.visibility ??= true; config.aside.side ??= config.side || 'left'; config.aside.size ??= config?.size; } if( config.header ) { if( typeof config.header === 'string' ) config.header = { after : config.header }; if( typeof config.header === 'boolean' ) config.header = { visibility : config.header }; config.header.visibility ??= true; config.header.size ??= config?.size; } if( config.footer ) { if( typeof config.footer === 'string' ) config.footer = { after : config.footer }; if( typeof config.footer === 'boolean' ) config.footer = { visibility : config.footer }; config.footer.visibility ??= true; config.footer.size ??= config?.size; } // if( config.content ) { // if( typeof config.content === 'string' ) config.content = { textNode : config.content }; // // config.content.visibility = true; // } return config; }, content : function( content ) { if( content.length > 1 ) { return ARUNOX.Templates.config( 'div', { classes : [ 'frame__content' ], content : content, }); } if( content.length == 1 ) { content[0].config.classes ??= []; content[0].config.classes.push( 'frame__content' ); } return content; }, }, builder : function( config, data ) { config.name = config.name || 'section'; this.root.id = config.name; this.root.classList.add( 'frame__section' ); this.render( 'layout/section/header', config.header, data ); this.render( 'layout/section/sidebar', config.aside, data ); this.render( 'layout/section/footer', config.footer, data ); } }); /** Datei: themes/lcars/assets/js/app.js **/ ARUNOX.Utils.dom.ready(async () => { try { await ARUNOX.Loader.loadAll([ '/plugins/rest/assets/js/hooks/_general.js', ]); ARUNOX.currentTheme = 'lcars'; ARUNOX.themeHookPath = `/themes/${ARUNOX.currentTheme}/assets/js/hooks`; ARUNOX.themeComponentPath = `/plugins/rest/assets/js/components`; await ARUNOX.Router.handle( window.location.pathname + window.location.hash, {}, {}, 'rest' ); } catch( error ) { console.error( 'Failed to initialize app:', error ); } });