1 line
8.4 KiB
JavaScript
1 line
8.4 KiB
JavaScript
(function(window){'use strict';const MYPCore ={version:'1.0.0',csrf:{_token:null,getToken(){if(!this._token){this._token = document.querySelector('meta[name="csrf-token"]')?.content || document.querySelector('input[name="csrf_token"]')?.value || this._getFromCookie('csrf_token')|| '';}return this._token;},_getFromCookie(name){const match = document.cookie.match(new RegExp('(^|)' + name + '=([^;]+)'));return match ? match[2]:null;},clearCache(){this._token = null;}},dom:{_cache:new Map(),get(selector,parent = document){const key = `${parent === document ? 'doc':'el'}_${selector}`;if(!this._cache.has(key)){this._cache.set(key,parent.querySelector(selector));}return this._cache.get(key);},getAll(selector,parent = document){return parent.querySelectorAll(selector);},escapeHtml(text){const div = this._escapeDiv ||(this._escapeDiv = document.createElement('div'));div.textContent = text;return div.innerHTML;},clearCache(){this._cache.clear();},create(tag,attrs ={},children = []){const el = document.createElement(tag);Object.entries(attrs).forEach(([key,value])=>{if(key === 'class'){el.className = value;}else if(key === 'style' && typeof value === 'object'){Object.assign(el.style,value);}else if(key.startsWith('data-')){el.dataset[key.slice(5)] = value;}else{el.setAttribute(key,value);}});children.forEach(child =>{if(typeof child === 'string'){el.appendChild(document.createTextNode(child));}else{el.appendChild(child);}});return el;}},notify:{_container:null,_queue:[],_activeToasts:new Map(),_toastId:0,show(message,type = 'info',duration = 5000,options ={}){if(window.dndManager?.isEnabled && !options.force){console.log(`[DND] Suppressed ${type}notification:`,message);return null;}const id = `toast-${++this._toastId}`;const toast = this._createToast(id,message,type,duration,options);this._ensureContainer();this._container.appendChild(toast);requestAnimationFrame(()=>{toast.classList.add('show');});if(duration > 0 && !options.persistent){const timeout = setTimeout(()=> this.close(id),duration);this._activeToasts.set(id,{element:toast,timeout});}return id;},_createToast(id,message,type,duration,options){const toast = MYPCore.dom.create('div',{id,class:`myp-toast myp-toast-${type}`,role:'alert','aria-live':'polite'});const icon = this._getIcon(type);const content = MYPCore.dom.create('div',{class:'toast-content'},[ MYPCore.dom.create('span',{class:'toast-icon'},[icon]),MYPCore.dom.create('span',{class:'toast-message'},[message])]);const closeBtn = MYPCore.dom.create('button',{class:'toast-close','aria-label':'Close notification',onclick:()=> this.close(id)},['×']);toast.appendChild(content);toast.appendChild(closeBtn);if(duration > 0 && !options.persistent){const progress = MYPCore.dom.create('div',{class:'toast-progress'});const bar = MYPCore.dom.create('div',{class:'toast-progress-bar',style:{animationDuration:`${duration}ms`}});progress.appendChild(bar);toast.appendChild(progress);}return toast;},_getIcon(type){const icons ={success:'✓',error:'✗',warning:'⚠',info:'ℹ'};return icons[type] || icons.info;},_ensureContainer(){if(!this._container){this._container = MYPCore.dom.get('#myp-toast-container')|| MYPCore.dom.create('div',{id:'myp-toast-container',class:'myp-toast-container'});if(!this._container.parentNode){document.body.appendChild(this._container);}}},close(id){const toast = this._activeToasts.get(id);if(toast){clearTimeout(toast.timeout);toast.element.classList.remove('show');setTimeout(()=>{toast.element.remove();this._activeToasts.delete(id);},300);}},closeAll(){this._activeToasts.forEach((_,id)=> this.close(id));}},api:{_cache:new Map(),_pendingRequests:new Map(),async request(url,options ={}){const config ={...options,headers:{'Content-Type':'application/json','X-CSRFToken':MYPCore.csrf.getToken(),...options.headers}};if(options.method === 'GET' || !options.method){const cacheKey = this._getCacheKey(url,options);const cached = this._cache.get(cacheKey);if(cached && !options.noCache){const age = Date.now()- cached.timestamp;const maxAge = options.cacheTime || 300000;if(age < maxAge){return cached.data;}}if(this._pendingRequests.has(cacheKey)){return this._pendingRequests.get(cacheKey);}}const requestPromise = fetch(url,config).then(async response =>{if(!response.ok){throw new Error(`HTTP ${response.status}:${response.statusText}`);}const data = await response.json();if(config.method === 'GET' || !config.method){const cacheKey = this._getCacheKey(url,options);this._cache.set(cacheKey,{data,timestamp:Date.now()});this._pendingRequests.delete(cacheKey);}return data;}).catch(error =>{if(config.method === 'GET' || !config.method){this._pendingRequests.delete(this._getCacheKey(url,options));}throw error;});if(config.method === 'GET' || !config.method){this._pendingRequests.set(this._getCacheKey(url,options),requestPromise);}return requestPromise;},_getCacheKey(url,options){return `${url}_${JSON.stringify(options.params ||{})}`;},clearCache(pattern){if(pattern){for(const [key] of this._cache){if(key.includes(pattern)){this._cache.delete(key);}}}else{this._cache.clear();}}},time:{formatAgo(timestamp){const date = timestamp instanceof Date ? timestamp:new Date(timestamp);const now = new Date();const seconds = Math.floor((now - date)/ 1000);const intervals = [{label:'Jahr',seconds:31536000,plural:'Jahre'},{label:'Monat',seconds:2592000,plural:'Monate'},{label:'Woche',seconds:604800,plural:'Wochen'},{label:'Tag',seconds:86400,plural:'Tage'},{label:'Stunde',seconds:3600,plural:'Stunden'},{label:'Minute',seconds:60,plural:'Minuten'}];for(const interval of intervals){const count = Math.floor(seconds / interval.seconds);if(count >= 1){const label = count === 1 ? interval.label:interval.plural;return `vor ${count}${label}`;}}return 'Gerade eben';},formatDuration(seconds){const hours = Math.floor(seconds / 3600);const minutes = Math.floor((seconds % 3600)/ 60);const secs = seconds % 60;const parts = [];if(hours > 0)parts.push(`${hours}h`);if(minutes > 0)parts.push(`${minutes}m`);if(secs > 0 || parts.length === 0)parts.push(`${secs}s`);return parts.join(' ');}},perf:{debounce(func,wait){let timeout;return function debounced(...args){const context = this;clearTimeout(timeout);timeout = setTimeout(()=> func.apply(context,args),wait);};},throttle(func,limit){let inThrottle;return function throttled(...args){const context = this;if(!inThrottle){func.apply(context,args);inThrottle = true;setTimeout(()=> inThrottle = false,limit);}};},memoize(func,resolver){const cache = new Map();return function memoized(...args){const key = resolver ? resolver(...args):args[0];if(cache.has(key)){return cache.get(key);}const result = func.apply(this,args);cache.set(key,result);return result;};}},events:{_listeners:new Map(),on(element,event,handler,options ={}){const key = `${element}_${event}`;if(!this._listeners.has(key)){this._listeners.set(key,new Set());}this._listeners.get(key).add(handler);element.addEventListener(event,handler,options);return()=> this.off(element,event,handler);},off(element,event,handler){const key = `${element}_${event}`;const handlers = this._listeners.get(key);if(handlers){handlers.delete(handler);if(handlers.size === 0){this._listeners.delete(key);}}element.removeEventListener(event,handler);},once(element,event,handler,options ={}){const wrappedHandler =(e)=>{handler(e);this.off(element,event,wrappedHandler);};return this.on(element,event,wrappedHandler,options);}},storage:{get(key,defaultValue = null){try{const item = localStorage.getItem(`myp_${key}`);return item ? JSON.parse(item):defaultValue;}catch(e){console.error('Storage get error:',e);return defaultValue;}},set(key,value){try{localStorage.setItem(`myp_${key}`,JSON.stringify(value));return true;}catch(e){console.error('Storage set error:',e);return false;}},remove(key){localStorage.removeItem(`myp_${key}`);},clear(prefix = 'myp_'){const keys = Object.keys(localStorage);keys.forEach(key =>{if(key.startsWith(prefix)){localStorage.removeItem(key);}});}}};window.MYPCore = MYPCore;window.showToast =(msg,type,duration)=> MYPCore.notify.show(msg,type,duration);window.showNotification = window.showToast;window.showFlashMessage = window.showToast;window.showSuccessMessage =(msg)=> MYPCore.notify.show(msg,'success');window.showErrorMessage =(msg)=> MYPCore.notify.show(msg,'error');window.showWarningMessage =(msg)=> MYPCore.notify.show(msg,'warning');window.showInfoMessage =(msg)=> MYPCore.notify.show(msg,'info');if(document.readyState === 'loading'){document.addEventListener('DOMContentLoaded',()=>{console.log('✅ MYP Core Utilities initialized');});}else{console.log('✅ MYP Core Utilities initialized');}})(window); |