-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfabr.min.js
185 lines (185 loc) · 41.7 KB
/
fabr.min.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
fbr={};fbr.FabrCore=class{fetchContent(sourceUrl){return fetch(sourceUrl).then((response)=>response.text()).then((html)=>{const parser=new DOMParser();const doc=parser.parseFromString(html,"text/html");const sourceElement=doc.querySelector("html");return sourceElement.innerHTML;}).catch((error)=>{console.error("Error:",error);});}
fetchJSON(sourceUrl){return fetch(sourceUrl).then((response)=>{if(!response.ok){throw new Error("Network response was not OK");}
const contentType=response.headers.get("content-type");if(!contentType||!contentType.includes("application/json")){throw new TypeError("Expected JSON response");}
return response.json();}).then((data)=>{if(!Array.isArray(data)||!data.every((item)=>typeof item==="object")){throw new TypeError("Invalid JSON data");}
return data;}).catch((error)=>{console.error("Error:",error);});}
fetchJsScript(sourceUrl){return fetch(sourceUrl).then((response)=>response.text()).catch((error)=>{console.error("Error:",error);});}
renderContent(html,container){if(container.innerHTML===html){return;}
const scripts=container.querySelectorAll("script[fabr-lib]");for(const script of scripts){script.remove();}
if(container.tagName==="HTML"){const title=container.querySelector("title");if(title){document.title=title.innerHTML;}}
container.innerHTML=html;fbr.fabr.reload();}
renderUrl(url,container){this.fetchContent(url).then((html)=>{return this.renderContent(html,container);}).catch((error)=>console.error("Error:",error));}};fbr.FabrHelper=class{constructor(){this.helperUID=Math.random().toString(36).substr(2,9);this.helperName="Generic";this.component=null;}
init(component){this.component=component;}};fbr.FabrHelperAnimate=class extends fbr.FabrHelper{constructor(){super();this.helperName="Animate";}
animate(element,animation,duration=200){switch(animation){case"fadeIn":this.fadeIn(element,duration);break;case"fadeOut":this.fadeOut(element,duration);break;case"slideUp":this.slideUp(element,duration);break;case"slideDown":this.slideDown(element,duration);break;case"slideLeft":this.slideLeft(element,duration);break;case"slideRight":this.slideRight(element,duration);break;default:break;}}
fade(element,duration,from,to,display,remove=false){element.style.opacity=from;element.style.display=display;let start=null;const step=(timestamp)=>{if(!start)start=timestamp;const progress=timestamp-start;const opacity=from+(to-from)*(progress/duration);element.style.opacity=opacity;if(progress<duration){window.requestAnimationFrame(step);}else{element.style.opacity=to;if(to===0){element.style.display="none";if(remove){element.remove();}}}};window.requestAnimationFrame(step);}
fadeIn(element,duration=200){this.fade(element,duration,0,1,"block");}
fadeOut(element,remove=false,duration=200){this.fade(element,duration,1,0,"block",remove);}
slide(element,duration,from,to,display,remove=false){element.style.transform=`translateX(${from}%)`;element.style.display=display;let start=null;const step=(timestamp)=>{if(!start)start=timestamp;const progress=timestamp-start;const translate=from+(to-from)*(progress/duration);element.style.transform=`translateX(${translate}%)`;if(progress<duration){window.requestAnimationFrame(step);}else{element.style.transform=`translateX(${to}%)`;if(to===0){element.style.display="block";if(remove){element.remove();}}}};window.requestAnimationFrame(step);}
slideUp(element,duration=200){this.slide(element,duration,0,-100,"block");}
slideDown(element,duration=200){this.slide(element,duration,-100,0,"block");}
slideLeft(element,duration=200){this.slide(element,duration,0,-100,"block");}
slideRight(element,duration=200){this.slide(element,duration,-100,0,"block");}};fbr.FabrHelperIcon=class extends fbr.FabrHelper{constructor(){super();this.helperName="Icon";this.iconsLibrary="";this.iconDOMStructure=null;}
setIconsLibraryFontAwesome(){this.iconsLibrary="fontawesome";this.iconDOMStructure=document.createElement("i");this.iconDOMStructure.classList.add("fas","fa-{{icon}}");}
setIconsLibraryMaterial(){this.iconsLibrary="material";this.iconDOMStructure=document.createElement("i");this.iconDOMStructure.classList.add("material-icons");this.iconDOMStructure.innerHTML="{{icon}}";}
setIconsLibraryBootstrap(){this.iconsLibrary="bootstrap";this.iconDOMStructure=document.createElement("i");this.iconDOMStructure.classList.add("bi","bi-{{icon}}");}
new(icon){if(this.iconDOMStructure===null){this.setIconsLibraryMaterial();}
const iconElement=this.iconDOMStructure.cloneNode(true);iconElement.previousIcon="{{icon}}";iconElement.changeIcon=(icon)=>{iconElement.innerHTML=iconElement.innerHTML.replace(iconElement.previousIcon,icon);iconElement.previousIcon=icon;};iconElement.changeIcon(icon);return iconElement;}};fbr.FabrHelperToast=class extends fbr.FabrHelper{constructor(){super();this.helperName="Toast";}
showToast(message,type="info",duration=3000,deletable=false){const toast=document.createElement("div");toast.className=`fabr-toast fabr-toast-${type}`;toast.innerHTML=message;document.body.appendChild(toast);if(deletable){toast.addEventListener("click",()=>{toast.remove();});}
if(duration===0){return toast;}
setTimeout(()=>{toast.remove();},duration);}};fbr.FabrHelperLocalStorage=class extends fbr.FabrHelper{constructor(){super();this.helperName="LocalStorage";}
setItem(key,value){try{localStorage.setItem(key,JSON.stringify(value));}catch(e){console.error("Failed to save data to Local Storage: ",e);}}
getItem(key){try{const item=localStorage.getItem(key);if(item){return JSON.parse(item);}
return null;}catch(e){console.error("Failed to retrieve data from Local Storage: ",e);return null;}}
removeItem(key){try{localStorage.removeItem(key);}catch(e){console.error("Failed to remove data from Local Storage: ",e);}}
initComponentStorage(){this.setItem(this.component.componentUID,{});}
getComponentStorage(){const storage=this.getItem(this.component.componentUID);if(storage===null){this.initComponentStorage();return{};}
return storage;}
setCItem(key,value){const storage=this.getComponentStorage();storage[key]=value;this.setItem(this.component.componentUID,storage);}
getCItem(key){const storage=this.getComponentStorage();return storage[key];}
removeCItem(key){const storage=this.getComponentStorage();delete storage[key];this.setItem(this.component.componentUID,storage);}
clearComponentStorage(){this.removeItem(this.component.componentUID);}};fbr.FabrHelperSharedMemory=class extends fbr.FabrHelper{constructor(){super();this.helperName="SharedMemory";}
init(component){super.init(component);this.sharedMemory=this.#getSharedMemory();this.restrictedKeys=this.#getRestrictedKeys();}
#getSharedMemory(){if(fbr._fabrSharedMemory){return fbr._fabrSharedMemory;}
fbr._fabrSharedMemory={};return fbr._fabrSharedMemory;}
#getRestrictedKeys(){if(fbr._fabrSharedMemoryRestrictedKeys){return fbr._fabrSharedMemoryRestrictedKeys;}
fbr._fabrSharedMemoryRestrictedKeys=[];return fbr._fabrSharedMemoryRestrictedKeys;}
set(key,value,replace=false,restricted=false){if(this.sharedMemory[key]&&!replace){return false;}
if(this.restrictedKeys.includes(key)){return false;}
if(restricted){this.restrictedKeys.push(key);}
this.sharedMemory[key]=value;return true;}
get(key){if(this.sharedMemory[key]){return this.sharedMemory[key];}
return false;}
remove(key){if(this.restrictedKeys.includes(key)){return false;}
if(this.sharedMemory[key]){delete this.sharedMemory[key];return true;}
return false;}};fbr.FabrHelperWatcher=class extends fbr.FabrHelper{constructor(){super();this.helperName="Watcher";this.watchers={};}
watch(variable,func){this.watchers[variable]=setInterval(()=>{if(this.watchers[variable]!==variable){this.watchers[variable]=variable;func(variable);}},100);}
unwatch(variable){if(this.watchers[variable]){clearInterval(this.watchers[variable]);delete this.watchers[variable];return true;}
return false;}
unwatchAll(){let result=false;for(let variable of Object.keys(this.watchers)){result=this.unwatch(variable)||result;}
return result;}};fbr.FabrCoreComponent=class extends fbr.FabrCore{constructor(){super();this.componentUID=Math.random().toString(36).substr(2,9);this.componentName="Generic";this.componentType="generic";this.componentStyleClass="fabr-component";this.selector="";this.eventMap={};this.elements=[];this.renderedElements=[];this.signals={};this.observers=new Map();}
init(){this.#initElements();this.#initEventListeners();this.#renderElements();}
update(){this.#initElements();this.#initEventListeners();}
get repr(){return`<${this.componentName}:${this.componentUID}>`;}
get reprX(){const color=this.componentType==="test"?"#ff6c6c":"#6c63ff";return[`<${this.componentName}:${this.componentUID}>`,`background:${color};color:white;border-radius:3px;padding:0 3px;font-weight:bold`,];}
#initElements(){if(!this.selector){return;}
const elements=document.querySelectorAll(this.selector);for(let element of elements){if(this.renderedElements.includes(element)){continue;}
if(element.getAttribute("fabr-com-id")){fbr.comIDs[element.getAttribute("fabr-com-id")]={component:this,element:element,};}
this.elements.push(element);element.classList.add(this.componentStyleClass);this.renderedElements.push(element);}}
#initEventListeners(){if(Object.keys(this.eventMap).length===0){return;}
for(let[event,fn]of Object.entries(this.eventMap)){for(let element of this.elements){element.addEventListener(event,(e)=>{this[fn](e);});}}}
addInternalEventListener(element,event,fn,...args){const listener=element.addEventListener(event,(e)=>{this[fn](e,...args);});return listener;}
removeInternalEventListener(element,event,fn){element.removeEventListener(event,this[fn]);}
#renderElements(){if(this.elements.length===0){return;}
for(let element of this.elements){if(this.render){this.render(element);}}}
emit(signal,element,...args){if(this.observers.has(signal)){const observers=this.observers.get(signal);for(let observer of observers){observer(element,...args);}}}
connect(signal,element,fn){if(!this.observers.has(signal)){this.observers.set(signal,new Set());}
const observers=this.observers.get(signal);const observer=(emittingElement,counterValue)=>{if(emittingElement===element){fn(emittingElement,counterValue);}};observers.add(observer);return observer;}
disconnect(signal,observer){if(this.observers.has(signal)){const observers=this.observers.get(signal);observers.delete(observer);}}
isElementInViewport(el){const rect=el.getBoundingClientRect();const elHeight=el.offsetHeight;const elWidth=el.offsetWidth;return(rect.top>=-elHeight&&rect.left>=-elWidth&&rect.bottom<=(window.innerHeight||document.documentElement.clientHeight)+
elHeight&&rect.right<=(window.innerWidth||document.documentElement.clientWidth)+elWidth);}
getElementSettings(element){const settings=element.getAttribute("fabr-settings");return settings?settings.split("|"):[];}};fbr.FabrForm=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrForm";this.componentStyleClass="fabr-form";this.selector="[fabr-form]";this.eventMap={submit:"onSubmit",};this.toastHelper=new fbr.FabrHelperToast();this.toastHelper.init(this);}
onSubmit(event){event.preventDefault();const formData=new FormData(event.target);const formAction=event.target.action;if(event.target.hasAttribute("fabr-form-response")){const responseUrl=event.target.getAttribute("fabr-form-response");this.#getResponse(responseUrl).then((data)=>{if(data.status===200){if(data.redirect){window.location.href=data.redirect;}else{this.toastHelper.showToast(data.message,"success",3000,true);}}else{this.toastHelper.showToast(data.message,"error",0,true);}}).catch((error)=>console.error("Error:",error));return;}
this.#submitForm(formAction,formData).then((data)=>{if(data.status===200){if(data.redirect){window.location.href=data.redirect;}else{this.toastHelper.showToast(data.message,"success",3000,true);}}else{this.toastHelper.showToast(data.message,"error",0,true);}}).catch((error)=>console.error("Error:",error));}
#getResponse(responseUrl){return fetch(responseUrl).then((response)=>response.json()).then((data)=>{return data;}).catch((error)=>{console.error("Error:",error);});}
#submitForm(formAction,formData){return fetch(formAction,{method:"POST",body:formData,}).then((response)=>response.json()).then((data)=>{return data;}).catch((error)=>{console.error("Error:",error);});}};fbr.FabrLink=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrLink";this.componentStyleClass="fabr-link";this.selector="[fabr-link]";this.eventMap={click:"onClick",};}
onClick(event){event.preventDefault();let source="";let target=event.target.closest("[fabr-link]");if(target){source=target.hasAttribute("href")?target.getAttribute("href"):target.getAttribute("fabr-link-target");}
const containerId=target?target.getAttribute("fabr-link-dom"):null;const container=containerId?document.getElementById(containerId):document.body;this.fetchContent(source).then((html)=>{return this.renderContent(html,container);}).then(()=>{if(!containerId){window.history.pushState(null,"",source);}}).catch((error)=>console.error("Error:",error));}
onMouseOver(event){console.log("Mouse Over on:",event.target);}};fbr.FabrTooltip=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrTooltip";this.componentStyleClass="fabr-has-tooltip";this.selector="[fabr-tooltip]";this.eventMap={mouseenter:"onMouseEnter",mouseleave:"onMouseLeave",};this.animateHelper=new fbr.FabrHelperAnimate();this.animateHelper.init(this);}
onMouseEnter(event){const target=event.target.closest("[fabr-tooltip]");if(target){const tooltipId=target.getAttribute("fabr-tooltip");const tooltip=document.createElement("div");tooltip.setAttribute("id",tooltipId);tooltip.setAttribute("class","fabr-tooltip");tooltip.innerText=target.getAttribute("fabr-tooltip-text");const targetRect=target.getBoundingClientRect();const tooltipRect=tooltip.getBoundingClientRect();tooltip.style.position="fixed";tooltip.style.top=`${targetRect.top-tooltipRect.height-30}px`;tooltip.style.left=`${targetRect.left-tooltipRect.width/2}px`;document.body.appendChild(tooltip);this.animateHelper.fadeIn(tooltip);}}
onMouseLeave(event){const tooltips=document.querySelectorAll(".fabr-tooltip");tooltips.forEach((tooltip)=>{this.animateHelper.fadeOut(tooltip,true);});}};fbr.FabrExpander=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrExpander";this.componentStyleClass="fabr-expander";this.selector="[fabr-expander]";this.eventMap={};this.animateHelper=new fbr.FabrHelperAnimate();this.animateHelper.init(this);this.iconHelper=new fbr.FabrHelperIcon();this.iconHelper.init(this);this.iconHelper.setIconsLibraryMaterial();}
render(expander){const wrapper=document.createElement("div");wrapper.classList.add("fabr-expander-wrapper");const header=document.createElement("div");header.classList.add("fabr-expander-header");wrapper.appendChild(header);const title=document.createElement("div");title.classList.add("fabr-expander-title");title.textContent=expander.getAttribute("fabr-expander-title");header.appendChild(title);const icon=this.iconHelper.new("keyboard_arrow_down");icon.classList.add("fabr-expander-icon");header.appendChild(icon);const content=document.createElement("div");content.classList.add("fabr-expander-content");wrapper.appendChild(content);expander.parentNode.insertBefore(wrapper,expander);content.appendChild(expander);expander.activeElement=wrapper;this.addInternalEventListener(header,"click","onHeaderClick",expander);}
onHeaderClick(event,expander){const wrapper=expander.activeElement;const content=wrapper.querySelector(".fabr-expander-content");const icon=wrapper.querySelector(".fabr-expander-icon");if(content.classList.contains("fabr-expander-content-open")){this.animateHelper.fadeOut(content,false,200);content.classList.remove("fabr-expander-content-open");wrapper.classList.remove("fabr-expander-wrapper-open");icon.textContent="keyboard_arrow_down";return;}
this.animateHelper.fadeIn(content,200);content.classList.add("fabr-expander-content-open");wrapper.classList.add("fabr-expander-wrapper-open");icon.textContent="keyboard_arrow_up";}};fbr.FabrList=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrList";this.componentStyleClass="fabr-list";this.selector="[fabr-list]";this.eventMap={};}
newList(listName,listItems){const list=document.querySelector(`[fabr-list-name="${listName}"]`);if(!list){}
listItems.forEach((listItem)=>{const item=document.createElement("li");item.classList.add("fabr-list-item");item.innerHTML=listItem;list.appendChild(item);});}
newListFromTemplate(listName,listItems,template){const list=document.querySelector(`[fabr-list-name="${listName}"]`);if(!list){}
listItems.forEach((listItem)=>{let item=template;for(const key in listItem){item=item.replace(`{{${key}}}`,listItem[key]);if(item.includes("{{value}}")){item=item.replace("{{value}}",listItem[key]);}}
const li=document.createElement("li");li.classList.add("fabr-list-item");li.innerHTML=item;list.appendChild(li);});}};fbr.FabrSelector=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrSelector";this.componentStyleClass="fabr-selector";this.selector="[fabr-selector]";this.eventMap={};this.animateHelper=new fbr.FabrHelperAnimate();this.animateHelper.init(this);this.iconHelper=new fbr.FabrHelperIcon();this.iconHelper.init(this);this.iconHelper.setIconsLibraryMaterial();}
render(selector){const wrapper=document.createElement("div");wrapper.classList.add("fabr-selector-wrapper");const searchWrapper=document.createElement("div");searchWrapper.classList.add("fabr-selector-search-wrapper");wrapper.appendChild(searchWrapper);const options=selector.querySelectorAll("option");const dropdown=document.createElement("div");dropdown.classList.add("fabr-selector-dropdown");selector.parentNode.insertBefore(wrapper,selector);wrapper.appendChild(selector);const input=document.createElement("input");input.classList.add("fabr-selector-input");input.type="text";input.placeholder="Search...";searchWrapper.appendChild(input);const icon=this.iconHelper.new("arrow_drop_down");icon.classList.add("fabr-selector-icon");searchWrapper.appendChild(icon);const list=document.createElement("ul");list.classList.add("fabr-selector-list");dropdown.appendChild(list);options.forEach((option)=>{const listItem=document.createElement("li");listItem.classList.add("fabr-selector-item");listItem.dataset.value=option.value;listItem.textContent=option.textContent;list.appendChild(listItem);});this.addInternalEventListener(list,"keydown","onKeyNavigationEvent",selector,input);this.addInternalEventListener(input,"keydown","onKeyNavigationEvent",selector,input);this.addInternalEventListener(input,"input","filterOptionsEvent",list);this.addInternalEventListener(searchWrapper,"click","showDropdownEvent",list);this.addInternalEventListener(list,"click","selectOptionEvent",selector,input,list);this.addInternalEventListener(document,"click","hideDropdownEvent",wrapper,list);wrapper.appendChild(dropdown);selector.style.display="none";}
filterOptionsEvent(event,list){const filterText=event.target.value.toLowerCase();const items=list.querySelectorAll(".fabr-selector-item");items.forEach((item)=>{const text=item.textContent.toLowerCase();if(text.indexOf(filterText)!==-1){item.style.display="block";}else{item.style.display="none";}});}
selectOptionEvent(event,selector,input,list){const selectedItem=event.target.closest(".fabr-selector-item");if(!selectedItem){return;}
const value=selectedItem.dataset.value;const text=selectedItem.textContent;selector.value=value;selector.dispatchEvent(new Event("change"));input.value=text;this.animateHelper.fadeOut(list);}
showDropdownEvent(event,list){event.stopPropagation();if(list.style.display==="block"){return;}
this.animateHelper.fadeIn(list);}
hideDropdownEvent(event,wrapper,list){event.stopPropagation();if(!wrapper.contains(event.target)&&list.style.display==="block"){list.classList.remove("active");this.animateHelper.fadeOut(list);}}
onKeyNavigationEvent(event,selector,input){const list=selector.parentNode.querySelector(".fabr-selector-list");const items=list.querySelectorAll(".fabr-selector-item");const activeItem=list.querySelector(".active");let index=-1;if(activeItem){index=Array.from(items).indexOf(activeItem);}
if(event.key==="ArrowDown"){event.preventDefault();index++;if(index>=items.length){index=0;}}else if(event.key==="ArrowUp"){event.preventDefault();index--;if(index<0){index=items.length-1;}}
if(index!==-1){items.forEach((item)=>{item.classList.remove("active");});items[index].classList.add("active");input.value=items[index].textContent;}
if(event.key==="Enter"){event.preventDefault();if(activeItem){activeItem.click();}}}};fbr.FabrSnippet=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="Snippet";this.componentStyleClass="fabr-has-snippet";this.selector="[fabr-snippet]";this.eventMap={};this.iconHelper=new fbr.FabrHelperIcon();this.iconHelper.init(this);this.iconHelper.setIconsLibraryMaterial();this.toastHelper=new fbr.FabrHelperToast();this.toastHelper.init(this);}
init(){super.init();this.#render();}
#render(){this.elements.forEach((element)=>{this.#renderSnippet(element);});}
#renderSnippet(element){const originElement=document.querySelector(element.getAttribute("fabr-snippet-from"));const snippet=document.createElement("div");const pre=document.createElement("pre");const code=document.createElement("code");const icon=this.iconHelper.new("content_copy");snippet.classList.add("fabr-snippet");code.classList.add("fabr-snippet-code");pre.classList.add("fabr-snippet-pre");icon.classList.add("fabr-snippet-icon");code.innerText=this.#cleanUpHtml(originElement.innerHTML);this.addInternalEventListener(icon,"click","onIconClick",element);pre.appendChild(code);snippet.appendChild(pre);snippet.appendChild(icon);element.appendChild(snippet);}
#cleanUpHtml(unsafe){let res=unsafe.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'");return res;}
onIconClick(event,element){const codeElement=element.querySelector(".fabr-snippet-code");const codeSnippet=codeElement.innerText;navigator.clipboard.writeText(codeSnippet).then(()=>{this.toastHelper.showToast("Code snippet copied to clipboard!","info");}).catch((error)=>{this.toastHelper.showToast("Code snippet copied to clipboard!","error");});}};fbr.FabrTable=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="Table";this.componentStyleClass="fabr-table";this.selector="[fabr-table]";this.eventMap={};this.iconHelper=new fbr.FabrHelperIcon();this.iconHelper.init(this);this.iconHelper.setIconsLibraryMaterial();}
render(table){const wrapper=document.createElement("div");const settings=this.getElementSettings(table);wrapper.classList.add("fabr-table-wrapper");table.parentNode.insertBefore(wrapper,table);wrapper.appendChild(table);table.activeElement=wrapper;if(settings.includes("search")){this.#renderSearch(table);}
if(settings.includes("sort")){this.#renderSort(table);}}
#renderSearch(table){const search=document.createElement("input");search.setAttribute("type","search");search.setAttribute("placeholder","Search...");search.setAttribute("fabr-table-search","");search.classList.add("fabr-table-search");table.activeElement.insertBefore(search,table);this.addInternalEventListener(search,"keyup","onKeyup",table);}
#renderSort(table){const ths=table.querySelectorAll("th");ths.forEach((th)=>{const icon=this.iconHelper.new("unfold_more");th.sortIcon=icon;th.appendChild(icon);this.addInternalEventListener(th,"click","onClick",table);th.setAttribute("fabr-table-sort","");});}
#sortTable(th,table){const isAsc=th.getAttribute("fabr-table-sort")==="asc";const ths=table.querySelectorAll("[fabr-table-sort]");ths.forEach((th)=>{th.sortIcon.changeIcon("unfold_more");th.setAttribute("fabr-table-sort","");});const tbody=table.querySelector("tbody");const rows=tbody.querySelectorAll("tr");const index=th.cellIndex;const sortedRows=Array.from(rows).sort((rowA,rowB)=>{const cellA=rowA.cells[index].textContent;const cellB=rowB.cells[index].textContent;return isAsc?cellA.localeCompare(cellB):cellB.localeCompare(cellA);});th.setAttribute("fabr-table-sort",isAsc?"desc":"asc");th.sortIcon.changeIcon(isAsc?"keyboard_arrow_down":"keyboard_arrow_up");while(tbody.firstChild){tbody.removeChild(tbody.firstChild);}
tbody.append(...sortedRows);}
#searchTable(input,table){const value=input.value.toLowerCase();const rows=table.querySelectorAll("tbody tr");rows.forEach((row)=>{const text=row.textContent.toLowerCase();if(text.includes(value)){row.style.display="";}else{row.style.display="none";}});}
onClick(event,table){const target=event.target.closest("[fabr-table-sort]");if(target){this.#sortTable(target,table);}}
onKeyup(event,table){const target=event.target.closest("[fabr-table-search]");if(target){this.#searchTable(target,table);}}};fbr.FabrCounter=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrCounter";this.componentStyleClass="fabr-counter";this.selector="[fabr-counter]";this.eventMap={click:"onClick",};this.counters={};}
onClick(event){event.preventDefault();let target=event.target;const virtualTarget=target.getAttribute("fabr-counter-target");if(target.getAttribute("fabr-counter-target")){target=virtualTarget;}
if(target){const counterId=target.getAttribute("fabr-counter");const counter=this.counters[counterId];if(counter){counter.value++;target.innerText=counter.value;}else{const initialValue=parseInt(target.getAttribute("fabr-counter-initial-value"))||0;this.counters[counterId]={value:initialValue+1};target.innerText=this.counters[counterId].value;}
this.emit("fabr-counter:incremented",event.target,target.innerText);}}};fbr.FabrNotebook=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrNotebook";this.componentStyleClass="fabr-notebook";this.selector="[fabr-notebook]";this.eventMap={};this.animateHelper=new fbr.FabrHelperAnimate();this.animateHelper.init(this);}
render(notebook){const tabItems=notebook.querySelectorAll("[fabr-notebook-item]");tabItems.forEach((tabItem)=>{const tabId=tabItem.getAttribute("fabr-notebook-item");const tabContent=notebook.querySelector(`[fabr-notebook-content="${tabId}"]`);tabItem.classList.add("fabr-notebook-item");tabContent.classList.add("fabr-notebook-content");tabContent.style.display="none";this.addInternalEventListener(tabItem,"click","activateTabEvent",tabItem,tabContent,notebook);});const firstTabItem=notebook.querySelector("[fabr-notebook-item]");const firstTabItemId=firstTabItem.getAttribute("fabr-notebook-item");const firstTabContent=notebook.querySelector(`[fabr-notebook-content="${firstTabItemId}"]`);firstTabItem.classList.add("active");firstTabContent.style.display="block";}
activateTabEvent(_,tabItem,tabContent,notebook){const tabItems=notebook.querySelectorAll("[fabr-notebook-item]");const tabContents=notebook.querySelectorAll("[fabr-notebook-content]");if(tabItem.classList.contains("active")){return;}
tabItems.forEach((tabItem)=>{tabItem.classList.remove("active");});tabContents.forEach((tabContent)=>{tabContent.style.display="none";});tabItem.classList.add("active");this.animateHelper.fadeIn(tabContent,500);}};fbr.FabrImage=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrImage";this.componentStyleClass="fabr-image";this.selector="[fabr-image]";this.eventMap={};this.animateHelper=new fbr.FabrHelperAnimate();this.animateHelper.init(this);}
render(image){const settings=this.getElementSettings(image);image.width=image.width;image.height=image.height;image.realSrc=image.src;if(settings.length===0){return;}
if(settings.includes("managed")){this.addInternalEventListener(document,"scroll","manageImageOnScroll",image);this.addInternalEventListener(window,"resize","manageImage",image);this.addInternalEventListener(window,"orientationchange","manageImage",image);}}
manageImageOnScroll(event,image){clearTimeout(image.scrollTimeout);image.scrollTimeout=setTimeout(()=>{this.manageImage(event,image);},100);}
manageImage(_,image){let process=false;if(!this.isElementInViewport(image.parentNode)){process=true;}
if(process){image.src="";image.style.visibility="hidden";}else{image.src=image.realSrc;image.style.visibility="visible";}}};fbr.FabrCarousel=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="Carousel";this.componentStyleClass="fabr-carousel";this.selector="[fabr-carousel]";this.eventMap={};this.iconHelper=new fbr.FabrHelperIcon();this.iconHelper.init(this);this.iconHelper.setIconsLibraryMaterial();this.animateHelper=new fbr.FabrHelperAnimate();this.animateHelper.init(this);}
render(carousel){const delay=carousel.getAttribute("fabr-carousel-delay");const wrapper=document.createElement("div");const slides=carousel.querySelectorAll("[fabr-carousel-slide]");carousel.style.height=`${carousel.offsetHeight}px`;carousel.delay=delay?parseInt(delay):5000;wrapper.classList.add("fabr-carousel-wrapper");carousel.parentNode.insertBefore(wrapper,carousel);wrapper.appendChild(carousel);slides.forEach((slide)=>{slide.classList.add("fabr-carousel-slide");});this.#renderControls(carousel);this.#renderIndicators(carousel);carousel.activeSlide=0;this.#rotateSlides(carousel);}
#renderControls(carousel){const controls=document.createElement("div");const prev=document.createElement("div");const next=document.createElement("div");controls.classList.add("fabr-carousel-controls");prev.classList.add("fabr-carousel-control-prev");next.classList.add("fabr-carousel-control-next");const prevIcon=this.iconHelper.new("chevron_left");const nextIcon=this.iconHelper.new("chevron_right");prev.appendChild(prevIcon);next.appendChild(nextIcon);controls.appendChild(prev);controls.appendChild(next);carousel.appendChild(controls);this.addInternalEventListener(prev,"click","setSlideEvent",carousel,-2);this.addInternalEventListener(next,"click","setSlideEvent",carousel,-1);}
#renderIndicators(carousel){const indicators=document.createElement("div");const slides=carousel.querySelectorAll("[fabr-carousel-slide]");indicators.classList.add("fabr-carousel-indicators");slides.forEach((_,index)=>{const indicator=document.createElement("div");indicator.classList.add("fabr-carousel-indicator");indicator.setAttribute("fabr-carousel-indicator","");indicator.setAttribute("fabr-carousel-indicator-index",index);indicators.appendChild(indicator);this.addInternalEventListener(indicator,"click","setSlideEvent",carousel,index);});carousel.appendChild(indicators);}
#rotateSlides(carousel){const slides=carousel.querySelectorAll("[fabr-carousel-slide]");const indicators=carousel.querySelectorAll("[fabr-carousel-indicator]");slides.forEach((slide)=>{slide.classList.remove("fabr-carousel-slide-active");});indicators.forEach((indicator)=>{indicator.classList.remove("fabr-carousel-indicator-active");});slides[carousel.activeSlide].classList.add("fabr-carousel-slide-active");indicators[carousel.activeSlide].classList.add("fabr-carousel-indicator-active");carousel.activeSlideTimeout=setTimeout(()=>{this.setSlide(carousel,-1);},carousel.delay);}
setSlideEvent(_,carousel,index){this.setSlide(carousel,index);}
setSlide(carousel,index){const slides=carousel.querySelectorAll("[fabr-carousel-slide]");if(index===-1){index=carousel.activeSlide+1;}else if(index===-2){index=carousel.activeSlide-1;}
if(index>=slides.length){index=0;}else if(index<0){index=slides.length-1;}
clearTimeout(carousel.activeSlideTimeout);carousel.activeSlide=index;this.#rotateSlides(carousel);}};fbr.FabrSummary=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="Summary";this.componentStyleClass="fabr-summary";this.selector="[fabr-summary]";this.eventMap={};this.iconHelper=new fbr.FabrHelperIcon();this.iconHelper.init(this);this.iconHelper.setIconsLibraryMaterial();this.animateHelper=new fbr.FabrHelperAnimate();this.animateHelper.init(this);}
render(summary){const headings=document.querySelectorAll("[fabr-summary-title]");const wrapper=document.createElement("div");const list=document.createElement("ul");wrapper.classList.add("fabr-summary-wrapper");list.classList.add("fabr-summary-list");summary.parentNode.insertBefore(wrapper,summary);wrapper.appendChild(summary);wrapper.appendChild(list);headings.forEach((heading)=>{let headingId=heading.id;if(!headingId){headingId=heading.innerText.replace(/ /g,"-").toLowerCase();heading.id=headingId;}
const item=document.createElement("li");const link=document.createElement("a");const text=document.createTextNode(heading.innerText);const level=heading.tagName.replace("H","");item.classList.add("fabr-summary-item",`fabr-summary-item-level-${level}`);link.classList.add("fabr-summary-link");link.href=`#${headingId}`;link.appendChild(text);item.appendChild(link);list.appendChild(item);this.addInternalEventListener(link,"click","scrollToHeading",heading,item,list);});this.#renderNestedHeadings(list);}
#renderNestedHeadings(list){const items=list.querySelectorAll(".fabr-summary-item");const levels=[];items.forEach((item)=>{const level=item.classList[1].replace("fabr-summary-item-level-","");const parentLevel=parseInt(level)-1;if(levels[parentLevel]){const parent=levels[parentLevel];const wrapper=document.createElement("ul");const arrow=this.iconHelper.new("arrow_drop_down");parent.classList.add("fabr-summary-item-has-nested");wrapper.classList.add("fabr-summary-item-wrapper");arrow.classList.add("fabr-summary-arrow");parent.appendChild(arrow);parent.appendChild(wrapper);wrapper.appendChild(item);wrapper.style.display="none";this.addInternalEventListener(parent,"click","toggleNestedHeadings",parent);}else{list.appendChild(item);}
levels[level]=item;});}
toggleNestedHeadings(event,parent){const wrapper=parent.querySelector(".fabr-summary-item-wrapper");const arrow=parent.querySelector(".fabr-summary-arrow");if(event.target.closest(".fabr-summary-item-wrapper")){return;}
if(wrapper.style.display==="none"){this.animateHelper.fadeIn(wrapper);arrow.changeIcon("arrow_drop_up");parent.classList.add("fabr-summary-item-nested-open");}else{this.animateHelper.fadeOut(wrapper);arrow.changeIcon("arrow_drop_down");parent.classList.remove("fabr-summary-item-nested-open");}}
scrollToHeading(event,heading,wrapper,list){event.preventDefault();wrapper.classList.add("fabr-summary-item-wrapper-active");heading.scrollIntoView({behavior:"smooth"});const activeWrappers=list.querySelectorAll(".fabr-summary-item-wrapper-active");activeWrappers.forEach((activeWrapper)=>{if(activeWrapper!==wrapper){activeWrapper.classList.remove("fabr-summary-item-wrapper-active");}});}};fbr.FabrAnimator=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrAnimator";this.componentStyleClass="fabr-animator";this.selector="[fabr-animator]";this.eventMap={click:"onAnim",mouseover:"onAnim",mouseout:"onAnim",};this.animateHelper=new fbr.FabrHelperAnimate();this.animateHelper.init(this);}
onAnim(event){let element=event.target;const animationType=element.getAttribute("fabr-animator-type");const animationEvent=element.getAttribute("fabr-animator-event");const animationTarget=element.getAttribute("fabr-animator-target");if(animationTarget){const target=document.querySelector(animationTarget);if(target){element=target;}}
if(animationEvent!==event.type){return;}
if(!animationType){return;}
this.animateHelper.animate(element,animationType);}};fbr.FabrLoader=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrLoader";this.componentStyleClass="fabr-loader";this.selector="[fabr-loader]";this.eventMap={};this.animateHelper=new fbr.FabrHelperAnimate();this.animateHelper.init(this);}
render(loader){this.animateHelper.fadeIn(loader,500);loader.style.visibility="visible";}};fbr.FabrContextMenu=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrContextMenu";this.componentStyleClass="fabr-contextmenu";this.selector="[fabr-contextmenu]";this.eventMap={};this.animateHelper=new fbr.FabrHelperAnimate();this.animateHelper.init(this);this.iconHelper=new fbr.FabrHelperIcon();this.iconHelper.init(this);this.iconHelper.setIconsLibraryMaterial();}
newMenu(menuName,menuItems){const menuTrigger=document.querySelector(`[fabr-contextmenu-name="${menuName}"]`);if(!menuTrigger){}
const menu=document.createElement("div");const menuList=document.createElement("div");menu.style.display="none";menu.classList.add("fabr-contextmenu-body");menu.setAttribute("fabr-contextmenu-name",menuName);menuList.classList.add("fabr-contextmenu-list");menuItems.forEach((menuItem)=>{const item=document.createElement("div");item.classList.add("fabr-contextmenu-item");item.style.position="relative";if(menuItem.icon){const icon=this.iconHelper.new(menuItem.icon);icon.classList.add("fabr-contextmenu-item-icon");item.appendChild(icon);}
const text=document.createElement("span");text.classList.add("fabr-contextmenu-item-text");text.innerHTML=menuItem.text;item.appendChild(text);if(!menuItem.subitems){this.addInternalEventListener(item,"click","runFunction",menuItem.action,menu);}else{const subMenu=document.createElement("div");subMenu.style.display="none";subMenu.classList.add("fabr-contextmenu-submenu");const subMenuList=document.createElement("div");subMenuList.classList.add("fabr-contextmenu-list","fabr-contextmenu-submenu-list");menuItem.subitems.forEach((subMenuItem)=>{const subItem=document.createElement("div");subItem.classList.add("fabr-contextmenu-item");if(subMenuItem.icon){const icon=this.iconHelper.new(subMenuItem.icon);icon.classList.add("fabr-contextmenu-item-icon");subItem.appendChild(icon);}
const text=document.createElement("span");text.classList.add("fabr-contextmenu-item-text");text.innerHTML=subMenuItem.text;subItem.appendChild(text);this.addInternalEventListener(subItem,"click","runFunction",subMenuItem.action,menu);subMenuList.appendChild(subItem);});this.addInternalEventListener(item,"mouseover","showSubMenu",subMenu);this.addInternalEventListener(item,"mouseleave","hideSubMenu",subMenu);subMenu.appendChild(subMenuList);item.appendChild(subMenu);}
menuList.appendChild(item);});menu.appendChild(menuList);document.body.appendChild(menu);this.addInternalEventListener(menuTrigger,"contextmenu","showMenu",menu);this.addInternalEventListener(document,"click","hideMenu",menu);}
showMenu(event,menu){event.preventDefault();this.animateHelper.fadeIn(menu);menu.style.top=`${event.clientY}px`;menu.style.left=`${event.clientX}px`;menu.style.position="fixed";}
hideMenu(event,menu){if(event.target==menu||menu.style.display=="none"){return;}
this.animateHelper.fadeOut(menu);}
runFunction(event,func,menu){this.hideMenu(event,menu);func();}
showSubMenu(_,subMenu){subMenu.style.display="block";subMenu.style.top="0";subMenu.style.left="100%";subMenu.style.position="absolute";}
hideSubMenu(_,subMenu){subMenu.style.display="none";}};fbr.FabrIcon=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrIcon";this.componentStyleClass="fabr-icon";this.selector="[fabr-icon]";this.eventMap={};this.iconHelper=new fbr.FabrHelperIcon();this.iconHelper.init(this);this.iconHelper.setIconsLibraryMaterial();}
render(icon){const iconName=icon.getAttribute("fabr-icon-name");if(!iconName){}
const iconElement=this.iconHelper.new(iconName);iconElement.classList.add("fabr-icon-element");icon.appendChild(iconElement);}};fbr.FabrPreLoad=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrPreLoad";this.componentStyleClass="fabr-preload";this.selector="[fabr-preload]";this.eventMap={};}
render(preload){let source="";if(preload.hasAttribute("fabr-preload-target")){source=preload.getAttribute("fabr-preload-target");}
if(!source){}
this.fetchContent(source).then((html)=>{return this.renderContent(html,preload);});}};fbr.FabrNav=class extends fbr.FabrCoreComponent{constructor(){super();this.componentName="FabrNav";this.componentStyleClass="fabr-nav";this.selector="[fabr-nav]";this.eventMap={click:"toggleNavChild",};}
render(nav){const navActiveClass=nav.getAttribute("fabr-nav-active-class");const navActiveChildren=nav.querySelector("[fabr-nav-active]");if(navActiveClass){nav.activeClass=navActiveClass;}else{nav.activeClass="fabr-nav-active";}
if(navActiveChildren){navActiveChildren.classList.add(nav.activeClass);}}
toggleNavChild(event){const nav=event.target.closest(this.selector);const navChild=event.target.closest("li");if(navChild){navChild.classList.add(nav.activeClass);this.resetNavChildren(nav,navChild);}}
resetNavChildren(nav,navChild){const navChildren=nav.querySelectorAll("li");navChildren.forEach((child)=>{if(child!==navChild){child.classList.remove(nav.activeClass);}});}};fbr.Fabr=class extends fbr.FabrCore{constructor(){super();this.version="0.1.0";console.log(`%cFabr[${this.version}]initializing...`,"color: #00f");this.components={form:new fbr.FabrForm(),link:new fbr.FabrLink(),list:new fbr.FabrList(),carousel:new fbr.FabrCarousel(),contextmenu:new fbr.FabrContextMenu(),counter:new fbr.FabrCounter(),expander:new fbr.FabrExpander(),icon:new fbr.FabrIcon(),image:new fbr.FabrImage(),loader:new fbr.FabrLoader(),tooltip:new fbr.FabrTooltip(),nav:new fbr.FabrNav(),notebook:new fbr.FabrNotebook(),table:new fbr.FabrTable(),animator:new fbr.FabrAnimator(),preload:new fbr.FabrPreLoad(),snippet:new fbr.FabrSnippet(),selector:new fbr.FabrSelector(),summary:new fbr.FabrSummary(),};console.log("%cFabr initialized","color: #00f");}
init(){this.initComponents();this.loadScripts();}
reload(){console.log("%c\n\n\n\n\n\n------\nReloading Fabr at "+
new Date().toLocaleTimeString()+
"\n------\n\n\n\n\n\n","color: #00f");for(const cName in this.components){this.components[cName].update();}
this.loadScripts();}
initComponents(){for(const cName in this.components){this.components[cName].init();}}
getComponent(cName){return this.components[cName];}
getComponents(){return this.components;}
loadScripts(){const scripts=document.querySelectorAll("script[type='text/fabr']");for(const script of scripts){const scriptText=script.innerHTML;const scriptFunc=new Function("fbr",scriptText);scriptFunc(fbr);}
const remoteScripts=document.querySelectorAll("script[type='text/fabr-remote']");for(const script of remoteScripts){const scriptUrl=script.getAttribute("src");this.fetchJsScript(scriptUrl).then((data)=>{const scriptText=data;const scriptFunc=new Function("fbr",scriptText);scriptFunc(fbr);}).catch((error)=>{console.error("Error:",error);});}}
getComponentByComId(comId){if(comId in fbr.comIDs){return fbr.comIDs[comId];}}
loadAppComponents(){if(typeof fbr.appComponents!=="undefined"){for(const cName in fbr.appComponents){fbr.appComponents[cName].init();}}}};fbr.comIDs={};fbr.fabr=new fbr.Fabr();fbr.fabr.init();