class WebService{ static base_url = '/api'; static host = 'app.ultracards.co'; static async call( route, data, method, token, success, error ){ let formData = false; if( !(data instanceof FormData) ){ formData = new FormData(); for( let key in data ){ formData.append( key, data[key] ); } } else { formData = data; } let headers = { "Accept": "application/json", // "Content-Type": "application/json", "Host": WebService.host }; if( token ){ headers[ 'Authorization' ] = 'Bearer '+token } let url = WebService.base_url + route; let errorFunc = WebService.onError( error ); let successFunc = WebService.onSuccess( success, errorFunc ); fetch( url, { "method": method, "body": formData, "headers": headers } ) .then( (response) => response.json() ) .then( successFunc ) .catch( errorFunc ); } static onSuccess( successFunc, errorFunc ){ return function( response ){ if( Tools.exists(response.success) ){ if( response.success ){ if( Tools.isFunction(successFunc) ){ successFunc( response ); } } else { errorFunc( response ); } } else { errorFunc( {"succes":false, "message":"Server error, retry later.", "error":"server-error"} ); } } } static onError( errorFunc ){ return function( error ){ if( Tools.isFunction(errorFunc) ){ errorFunc( error ); } } } }class Auth{ static profile = false; static tokens = {}; static setProfile( profile ){ Auth.profile = profile; } static getProfile(){ return Auth.profile; } static loadProfiles(){ let tokens = Auth.getLocalSession(); if( tokens && !Tools.empty(tokens) ){ Auth.tokens = tokens; let token = Auth.getCurrentToken(); if( token ){ Auth.validateToken( token ); } else { EventDispatcher.trigger( 'auth_error' ); } } else { EventDispatcher.trigger( 'auth_error' ); } } static validateToken( token ){ WebService.call( '/me', {}, 'POST', token, Auth.handleTokenSuccess, Auth.handleTokenError ); } static handleTokenSuccess( response ){ if( response.success && response.me ){ Context.setProfile( Auth.getProfile() ); Context.setToken( response.token ); Context.setMe( response.me ); EventDispatcher.trigger( 'auth_success' ); } else { EventDispatcher.trigger( 'auth_error' ); } } static handleTokenError( error ){ EventDispatcher.trigger( 'auth_error' ); } static setToken( token, type ){ if( token && type ){ Auth.tokens[ type ] = token; Auth.updateLocalSession(); EventDispatcher.trigger( 'auth_success' ); } } static getCurrentToken(){ let token = false; if( Tools.exists(Auth.tokens[ Auth.getProfile() ]) ){ token = Auth.tokens[ Auth.getProfile() ]; } return token; } static getToken( type ){ let token = false; if( Auth.tokens[ type ] ){ token = Auth.tokens[ type ]; } return token; } static async logout(){ if( Context.getToken() ){ let type = Auth.getProfile(); WebService.call( '/logout', {}, 'POST', Context.getToken() ); delete(Auth.tokens[ type ]); Context.setMe( false ); Context.setToken( false ); Auth.updateLocalSession(); EventDispatcher.trigger( 'auth_logout' ); } } static getLocalSession(){ let tokens = localStorage.getItem("tokens"); try{ tokens = JSON.parse(tokens); } catch( error ){ tokens = false; } return tokens; } static async updateLocalSession(){ localStorage.setItem("tokens", JSON.stringify(Auth.tokens)); } static removeToken( token ){ for( let userType in Auth.tokens ){ if( Auth.tokens[userType].token == token ){ delete( Auth.tokens[userType] ); localStorage.setItem("tokens", Auth.tokens); } } } static checkACL(){ let profile = Auth.getProfile(); return Auth.getTokenFromACL( profile ); } static getTokenFromACL(){ let token = false; if( Context.getMe() ){ let profile = Auth.getProfile(); if( Tools.exists(Auth.tokens[ profile ]) && Auth.tokens[ profile ] ){ token = Auth.tokens[ profile ]; } } return token; } }class Colors{ static getTextColor( type ){ let textColor = 'text-gray-900'; switch( type ){ case 'info': textColor = 'text-cyan-500'; break; case 'success': textColor = 'text-emerald-500'; break; case 'warning': textColor = 'text-amber-500'; break; case 'error': textColor = 'text-rose-500'; break; } return textColor; } static getBgColor( type ){ let bgColor = 'bg-gray-100'; switch( type ){ case 'info': bgColor = 'bg-sky-50'; break; case 'success': bgColor = 'bg-emerald-50'; break; case 'warning': bgColor = 'bg-amber-50'; break; case 'error': bgColor = 'bg-rose-50'; break; } return bgColor; } }class Context{ static me = false; static token = false; static profile = false; static setMe( me ){ Context.me = me; } static getMe(){ return Context.me; } static setProfile( profile ){ Context.profile = profile; } static getProfile(){ return Context.profile; } static setToken( token ){ Context.token = token; } static getToken(){ return Context.token; } }class DynamicLoader{ static loaded = []; static loadScript( src, success, error ){ if( !Tools.inArray( src, DynamicLoader.loaded ) ){ var script = document.createElement('script'); script.onload = DynamicLoader.onLoadSuccess( src, success ); script.onerror = DynamicLoader.onLoadError( src, error ); script.src = src; document.head.appendChild( script ); } else if( Tools.isFunction(success) ){ success(); } } static loadStyle( src, success, error ){ if( !Tools.inArray( src, DynamicLoader.loaded ) ){ var style = document.createElement('link'); style.onload = DynamicLoader.onLoadSuccess( src, success ); style.onerror = DynamicLoader.onLoadError( src, error ); style.rel = "stylesheet"; style.type = "text/css"; style.href = src; document.head.appendChild( style ); } else if( Tools.isFunction(success) ){ success(); } } static onLoadSuccess( src, successFunc ){ return function(){ DynamicLoader.loaded.push( src ); if( Tools.isFunction(successFunc) ){ successFunc(); } } } static onLoadError( src, errorFunc ){ return function(){ if( Tools.isFunction(errorFunc) ){ errorFunc(); } } } }class EventDispatcher{ static events = {}; static on( eventName, func ){ if( !Tools.isset( EventDispatcher.events[eventName] ) ){ EventDispatcher.events[eventName] = []; } if( Tools.isFunction(func) ){ EventDispatcher.events[eventName].push( func ); } } static trigger( eventName, params ){ if( Tools.isset( EventDispatcher.events[eventName] ) ){ for( let func of EventDispatcher.events[eventName] ){ func( params ); } } } }class Lang{ static current = false; static dictionaries = {}; static loadDictionary( lang, dictionary ){ if( !Tools.empty( dictionary ) ){ Lang.dictionaries[lang] = dictionary; } } static setDictionary( lang ){ if( !Tools.empty(Lang.dictionaries[lang]) ){ Lang.current = lang; } } static t( str ){ if( Lang.current && Tools.exists(Lang.dictionaries[Lang.current]) && Tools.exists( Lang.dictionaries[Lang.current][str] ) ){ str = Lang.dictionaries[Lang.current][str]; } return str; } }class Tools{ static inArray( value, mixed ){ return mixed.indexOf( value ) !== -1; } static empty( mixed ){ return !!mixed && mixed!='' && (typeof( mixed )!='object' || Object.entries(mixed).lenght>0); } static isFunction( mixed ){ return Tools.exists(mixed) && typeof( mixed ) == 'function'; } static isset( mixed ){ return typeof( mixed )!='undefined' && !!mixed; } static exists( mixed ){ let exists = false; try{ exists = typeof( mixed )!='undefined'; } catch(error) {}; return exists; } }class Component{ tpl = ''; styles = ''; view = false; node = false; parameters = {}; static render( target, parameters ){ let me = new this(); if( parameters ){ me.parameters = Object.assign( me.parameters, parameters ); } if( target instanceof View ){ me.view = target; target = target.node; if( me.view.parameters ){ me.parameters = Object.assign( me.parameters, me.view.parameters ); } } me.beforeParseTemplate(); let template = me.parseTemplate(); let componentSlug = me.constructor.name.replace('component','').toLocaleLowerCase(); me.node = $('
',{'class':'component '+componentSlug}).html( template ); me.beforeRender(); $( target ).append( me.node ); me.afterRender(); me.setEvents(); } beforeParseTemplate(){ } parseTemplate(){ let template = this.tpl; if( this.styles && this.styles!='' ){ template += ''; } return template; } beforeRender(){ } afterRender(){ } setEvents(){ } getParameter( name ){ let value = false; if( Tools.exists(this.parameters[name]) ){ value = this.parameters[name]; } return value; } }class FormComponent extends Component{ afterRender(){ super.afterRender(); this.setRoutes(); } setRoutes(){ if( Tools.exists(this.parameters.route) ){ $( this.node ).find( 'form' ).attr( 'action', this.parameters.route ); } } setEvents(){ super.setEvents(); $( this.node ).submit( this.handleSubmit( this ) ); } handleSubmit( inst ){ return (event) => { event.preventDefault(); event.stopImmediatePropagation(); inst.submit(); return false; }; } submit(){ let form = $( this.node ).find('form').get(0); let formData = new FormData( form ); let route = $( form ).attr('action'); let method = $( form ).attr('method'); let onSuccess = $( form ).data('onSuccess'); let onError = $( form ).data('onError'); WebService.call( route, formData, method, false, onSuccess, onError ); } }class View extends Component{ route = false; menu = true; sidebar = true; components = {}; static render( parameters ){ let me = new this(); if( parameters ){ me.parameters = Object.assign( me.parameters, parameters ); } me.beforeParseTemplate(); let template = me.parseTemplate(); let viewSlug = me.constructor.name.replace('view','').toLocaleLowerCase(); me.node = $( '
', {'class':'view '+viewSlug }).html( template ); me.beforeRender(); $( Viewport.content ).html( me.node ); me.afterRender(); me.setEvents(); if( me.menu ){ Viewport.showMenu(); } else { Viewport.hideMenu(); } if( me.sidebar ){ Viewport.showSidebar(); } else { Viewport.hideSidebar(); } return me; } }class Viewport{ static menu = false; static sidebar = false; static content = false; static current_route = false; static routes = {}; static node = false; static components = {}; static tpl = `
`; static styles = ` :root{ --menu-height: 50px; --sidebar-width: 250px; --transition-duration: 0.3s; } body{ overflow:hidden; } .viewport{ width:100vw; height:100vh; } .viewport-menu{ position:absolute; top:calc(var(--menu-height) * -1); left:0; width:100vw; height:var(--menu-height); } body.menu-visible .viewport-menu{ top:0; } .viewport-sidebar{ position:absolute; top:0; left:calc(var(--sidebar-width) * -1); width:var(--sidebar-width); height:100vh; overflow-y:auto; } body.sidebar-visible .viewport-sidebar{ left:0; } body.menu-visible .viewport-sidebar{ top:var(--menu-height); height:calc(100vh - var(--menu-height)); } .viewport-content{ position:absolute; top:0px; left:0px; width:100vw; height:100vh; overflow-y:auto; } body.menu-visible .viewport-content{ top:var(--menu-height); height:calc(100vh - var(--menu-height)); } body.sidebar-visible .viewport-content{ left:var(--sidebar-width); width:calc(100vw - var(--sidebar-width)); } `; static init(){ Viewport.node = $('
', {'class':'viewport'}).html( Viewport.tpl ); $('body').html( Viewport.node ); let styles = $('