farts
BIN
src/assets/.DS_Store
vendored
Normal file
BIN
src/assets/backgrounds/bg1.jpg
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
3
src/assets/backgrounds/bgs.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[
|
||||||
|
"assets/backgrounds/bg1.jpg"
|
||||||
|
]
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"settings": {
|
||||||
|
"title": "My Startpage",
|
||||||
|
"backgroundImgPath": "/src/backgrounds/bg1.jpg",
|
||||||
|
"headerBgColor": "rgba(0, 0, 0, 0.5)",
|
||||||
|
"headerOutlineColor": "rgba(255, 255, 255, 0.7)",
|
||||||
|
"headerOutlineThickness": 2,
|
||||||
|
"cardBgColor": "rgba(255, 255, 255, 0.8)",
|
||||||
|
"cornerRadius": 10,
|
||||||
|
"openLinksInNewTab": true,
|
||||||
|
"searchEngine": {
|
||||||
|
"name": "DuckDuckGo",
|
||||||
|
"url": "https://duckduckgo.com/?q="
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"title": "Category One",
|
||||||
|
"iconPath": "/src/icons/category1.png",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"name": "Example Link 1",
|
||||||
|
"url": "https://example1.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Example Link 2",
|
||||||
|
"url": "https://example2.com"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Category Two",
|
||||||
|
"iconPath": "/src/icons/category2.png",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"name": "Example Link A",
|
||||||
|
"url": "https://exampleA.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Example Link B",
|
||||||
|
"url": "https://exampleB.com"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Category Three",
|
||||||
|
"iconPath": "/src/icons/category3.png",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"name": "Example Link X",
|
||||||
|
"url": "https://exampleX.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Example Link Y",
|
||||||
|
"url": "https://exampleY.com"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
src/assets/fonts/iosevka_ttf/iosevka-bold.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-bolditalic.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-boldoblique.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-extrabold.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-extrabolditalic.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-extraboldoblique.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-extralight.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-extralightitalic.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-extralightoblique.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-heavy.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-heavyitalic.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-heavyoblique.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-italic.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-light.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-lightitalic.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-lightoblique.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-medium.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-mediumitalic.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-mediumoblique.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-oblique.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-regular.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-semibold.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-semibolditalic.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-semiboldoblique.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-thin.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-thinitalic.ttf
Normal file
BIN
src/assets/fonts/iosevka_ttf/iosevka-thinoblique.ttf
Normal file
|
Before Width: | Height: | Size: 0 B After Width: | Height: | Size: 304 KiB |
55
src/assets/images/fox.svg
Normal file
|
After Width: | Height: | Size: 14 KiB |
100
src/assets/images/google.svg
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
|
||||||
|
sodipodi:docname="google.svg"
|
||||||
|
id="svg16"
|
||||||
|
version="1.1"
|
||||||
|
height="52"
|
||||||
|
width="52"
|
||||||
|
viewBox="0 0 5.2 5.2">
|
||||||
|
<metadata
|
||||||
|
id="metadata22">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs20">
|
||||||
|
<inkscape:path-effect
|
||||||
|
only_selected="false"
|
||||||
|
apply_with_weight="true"
|
||||||
|
apply_no_weight="true"
|
||||||
|
helper_size="0"
|
||||||
|
steps="2"
|
||||||
|
weight="33.333333"
|
||||||
|
lpeversion="1"
|
||||||
|
is_visible="true"
|
||||||
|
id="path-effect1925"
|
||||||
|
effect="bspline" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
only_selected="false"
|
||||||
|
apply_with_weight="true"
|
||||||
|
apply_no_weight="true"
|
||||||
|
helper_size="0"
|
||||||
|
steps="2"
|
||||||
|
weight="33.333333"
|
||||||
|
lpeversion="1"
|
||||||
|
is_visible="true"
|
||||||
|
id="path-effect885"
|
||||||
|
effect="bspline" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
inkscape:current-layer="svg16"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:cy="24.110555"
|
||||||
|
inkscape:cx="46.005708"
|
||||||
|
inkscape:zoom="7.989887"
|
||||||
|
showgrid="false"
|
||||||
|
id="namedview18"
|
||||||
|
inkscape:window-height="704"
|
||||||
|
inkscape:window-width="1366"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
guidetolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
objecttolerance="10"
|
||||||
|
borderopacity="1"
|
||||||
|
bordercolor="#666666"
|
||||||
|
pagecolor="#232929"
|
||||||
|
inkscape:document-rotation="0" />
|
||||||
|
<g
|
||||||
|
transform="matrix(0.92846546,0,0,0.92479909,0.17492131,0.19753118)"
|
||||||
|
style="stroke:#6c8dac;stroke-width:0.48541465;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="g1929">
|
||||||
|
<path
|
||||||
|
style="fill:#ff8080;fill-opacity:0;stroke:#6c8dac;stroke-width:0.48541465;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke markers fill"
|
||||||
|
id="path1911"
|
||||||
|
sodipodi:type="arc"
|
||||||
|
sodipodi:cx="2.4996181"
|
||||||
|
sodipodi:cy="2.6108518"
|
||||||
|
sodipodi:rx="2.1740921"
|
||||||
|
sodipodi:ry="2.1773813"
|
||||||
|
sodipodi:start="6.2744429"
|
||||||
|
sodipodi:end="5.33678"
|
||||||
|
sodipodi:open="true"
|
||||||
|
sodipodi:arc-type="arc"
|
||||||
|
d="M 4.673627,2.5918165 A 2.1740921,2.1773813 0 0 1 3.0230721,4.7241798 2.1740921,2.1773813 0 0 1 0.56877939,3.6116325 2.1740921,2.1773813 0 0 1 1.0791939,0.96243588 2.1740921,2.1773813 0 0 1 3.7705995,0.84430035" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#6c8dac;stroke-width:0.48541465;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2.5940139,2.6155787 c 0,0 2.0796131,-0.023762 2.0796131,-0.023762"
|
||||||
|
id="path1923"
|
||||||
|
inkscape:path-effect="#path-effect1925"
|
||||||
|
inkscape:original-d="M 2.5940139,2.6155787 4.673627,2.5918165"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.4 KiB |
89
src/assets/images/search.svg
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
|
||||||
|
sodipodi:docname="search.svg"
|
||||||
|
id="svg12"
|
||||||
|
version="1.1"
|
||||||
|
class="feather feather-crosshair"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke="#1789D0"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
height="48"
|
||||||
|
width="48">
|
||||||
|
<metadata
|
||||||
|
id="metadata18">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs16" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
inkscape:current-layer="svg12"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:window-y="112"
|
||||||
|
inkscape:window-x="317"
|
||||||
|
inkscape:cy="24"
|
||||||
|
inkscape:cx="24"
|
||||||
|
inkscape:zoom="5.6041667"
|
||||||
|
showgrid="false"
|
||||||
|
id="namedview14"
|
||||||
|
inkscape:window-height="480"
|
||||||
|
inkscape:window-width="733"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
guidetolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
objecttolerance="10"
|
||||||
|
borderopacity="1"
|
||||||
|
bordercolor="#666666"
|
||||||
|
pagecolor="#ffffff" />
|
||||||
|
<g
|
||||||
|
transform="matrix(1.0346466,0,0,1.0346466,-0.79687178,-0.0346466)"
|
||||||
|
id="g25">
|
||||||
|
<circle
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
id="circle2" />
|
||||||
|
<line
|
||||||
|
x1="22"
|
||||||
|
y1="12"
|
||||||
|
x2="18"
|
||||||
|
y2="12"
|
||||||
|
id="line4" />
|
||||||
|
<line
|
||||||
|
x1="6"
|
||||||
|
y1="12"
|
||||||
|
x2="2"
|
||||||
|
y2="12"
|
||||||
|
id="line6" />
|
||||||
|
<line
|
||||||
|
x1="12"
|
||||||
|
y1="6"
|
||||||
|
x2="12"
|
||||||
|
y2="2"
|
||||||
|
id="line8" />
|
||||||
|
<line
|
||||||
|
x1="12"
|
||||||
|
y1="22"
|
||||||
|
x2="12"
|
||||||
|
y2="18"
|
||||||
|
id="line10" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
1
src/assets/images/shuttle-space-solid-full.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M32 432L32 496C32 522.5 53.5 544 80 544L130 544C170.6 544 210.4 533 245.2 512.1L378.7 432L285.4 432L220.5 470.9C206.5 479.3 191.6 485.6 176 489.9L176 423.5C185.7 417.9 193.8 409.8 199.4 400.1L398.2 400.1C465.3 400.1 526 379.5 568.5 329.5C573.1 324.1 573.1 316.2 568.5 310.7C526 260.6 465.3 240.1 398.2 240.1L199.4 240.1C193.8 230.4 185.7 222.3 176 216.7L176 150.3C191.6 154.5 206.6 160.9 220.5 169.3L285.4 208.2L378.7 208.2L245.2 128.1C210.4 107 170.6 96 130 96L80 96C53.5 96 32 117.5 32 144L32 432zM128 432L128 496L80 496L80 432L128 432zM128 144L128 208L80 208L80 144L128 144zM448 288C456.8 288 464 295.2 464 304L464 336C464 344.8 456.8 352 448 352C439.2 352 432 344.8 432 336L432 304C432 295.2 439.2 288 448 288z"/></svg>
|
||||||
|
After Width: | Height: | Size: 945 B |
@ -0,0 +1,211 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: iosevka-light;
|
||||||
|
src: url('fonts/iosevka_ttf/iosevka-light.ttf');
|
||||||
|
}
|
||||||
|
|
||||||
|
html{
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background:#1b1b1b;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
/* background: #263238; */
|
||||||
|
background-image: url('/src/backgrounds/bg1.jpg');
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
color: #ffa31a;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.head {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 20%;
|
||||||
|
font-family: iosevka-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #292929;
|
||||||
|
color: #ffa31a;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: 0px 4px 5px #171D20;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
border: #ffa31a 1px;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmark {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookmarks {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: #191C1E;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: 0px 4px 5px #171D20;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
background: rgba(41, 41, 41, .6);
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
align-self: center;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
align-self: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.input_box {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
background: none;
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-left: 15px;
|
||||||
|
width: 100%;
|
||||||
|
color: #ffa31a;
|
||||||
|
font-size: 22px;
|
||||||
|
border-radius: 0px 0px 0px 0px;
|
||||||
|
height: 70px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
transition-duration: 0.6s;
|
||||||
|
position: fixed;
|
||||||
|
padding-left: 11px;
|
||||||
|
padding-right: 12px;
|
||||||
|
height: 70px;
|
||||||
|
opacity: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine {
|
||||||
|
background: none;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 11px;
|
||||||
|
border: none;
|
||||||
|
transition-duration: 0.6s;
|
||||||
|
position: relative;
|
||||||
|
opacity: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search_box {
|
||||||
|
display: flex;
|
||||||
|
background: rgba(41, 41, 41, .6);
|
||||||
|
width: 600px;
|
||||||
|
padding: 0px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
border-radius: 3px 3px 3px 3px;
|
||||||
|
box-shadow: 0px 4px 5px #171D20;
|
||||||
|
align-self: center;
|
||||||
|
height: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine:hover {
|
||||||
|
filter: saturate(150%) brightness(120%) !important;
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover {
|
||||||
|
filter: saturate(150%) brightness(120%) !important;
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
font-size: 16px;
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 5px
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #F8BE4D;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: #F8BE4D;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #B5CCE8;
|
||||||
|
}
|
||||||
|
|
||||||
|
#credit {
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
font-family: iosevka-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gSearch {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@media screen and (max-width: 600px) {
|
||||||
|
.container {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.bookmark {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.category {
|
||||||
|
width: 90%;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
.search_box {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
.input_box {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
.engine {
|
||||||
|
padding: 0px 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
305
src/assets/scripts/VoidPage.js
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
/**
|
||||||
|
* VoidPage - Main application controller
|
||||||
|
* Coordinates configuration loading, styling, and page initialization
|
||||||
|
*/
|
||||||
|
|
||||||
|
class VoidPage {
|
||||||
|
constructor() {
|
||||||
|
this.config = null;
|
||||||
|
this.configLoader = null;
|
||||||
|
this.styleParser = null;
|
||||||
|
this.isInitialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the VoidPage application
|
||||||
|
*/
|
||||||
|
async init() {
|
||||||
|
try {
|
||||||
|
console.log('Initializing VoidPage...');
|
||||||
|
|
||||||
|
// Import required modules
|
||||||
|
await this.loadModules();
|
||||||
|
|
||||||
|
// Load configuration
|
||||||
|
await this.loadConfiguration();
|
||||||
|
|
||||||
|
// Apply styles based on configuration
|
||||||
|
await this.applyStyles();
|
||||||
|
|
||||||
|
// Initialize page content
|
||||||
|
this.initializePageContent();
|
||||||
|
|
||||||
|
// Set up event listeners
|
||||||
|
this.setupEventListeners();
|
||||||
|
|
||||||
|
this.isInitialized = true;
|
||||||
|
console.log('VoidPage initialized successfully');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize VoidPage:', error);
|
||||||
|
this.handleInitializationError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load required modules dynamically
|
||||||
|
*/
|
||||||
|
async loadModules() {
|
||||||
|
try {
|
||||||
|
// Import configLoader module
|
||||||
|
const configModule = await import('./configLoader.js');
|
||||||
|
this.configLoader = configModule.configLoader;
|
||||||
|
|
||||||
|
// Import styleParser module
|
||||||
|
const styleModule = await import('./styleParser.js');
|
||||||
|
this.styleParser = styleModule.styleParser;
|
||||||
|
|
||||||
|
console.log('Modules loaded successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load modules:', error);
|
||||||
|
throw new Error('Module loading failed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load and parse configuration
|
||||||
|
*/
|
||||||
|
async loadConfiguration() {
|
||||||
|
try {
|
||||||
|
this.config = await this.configLoader.loadConfig();
|
||||||
|
|
||||||
|
// Make config globally available for other scripts
|
||||||
|
window.VOID_CONFIG = this.config;
|
||||||
|
|
||||||
|
console.log('Configuration loaded:', this.config);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load configuration:', error);
|
||||||
|
throw new Error('Configuration loading failed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply styles based on configuration
|
||||||
|
*/
|
||||||
|
async applyStyles() {
|
||||||
|
try {
|
||||||
|
if (!this.config) {
|
||||||
|
throw new Error('Configuration not loaded');
|
||||||
|
}
|
||||||
|
|
||||||
|
const success = this.styleParser.applyConfigStyles(this.config);
|
||||||
|
if (!success) {
|
||||||
|
throw new Error('Style application failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Styles applied successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to apply styles:', error);
|
||||||
|
// Don't throw - allow page to continue with default styles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize page content based on configuration
|
||||||
|
*/
|
||||||
|
initializePageContent() {
|
||||||
|
try {
|
||||||
|
const settings = this.config.settings;
|
||||||
|
const cards = this.config.cards;
|
||||||
|
|
||||||
|
// Set page title
|
||||||
|
document.title = settings.title;
|
||||||
|
|
||||||
|
// Create or update page header
|
||||||
|
this.createPageHeader(settings);
|
||||||
|
|
||||||
|
// Generate cards
|
||||||
|
this.generateCards(cards);
|
||||||
|
|
||||||
|
// Set up search functionality
|
||||||
|
this.setupSearch(settings.searchEngine);
|
||||||
|
|
||||||
|
console.log('Page content initialized');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize page content:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create page header
|
||||||
|
*/
|
||||||
|
createPageHeader(settings) {
|
||||||
|
let header = document.querySelector('.header');
|
||||||
|
if (!header) {
|
||||||
|
header = document.createElement('header');
|
||||||
|
header.className = 'header';
|
||||||
|
document.body.insertBefore(header, document.body.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
header.innerHTML = `
|
||||||
|
<h1>${settings.title}</h1>
|
||||||
|
<div class="search-container">
|
||||||
|
<input type="text" id="search-input" placeholder="Search ${settings.searchEngine.name}...">
|
||||||
|
<button id="search-button">Search</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate cards from configuration
|
||||||
|
*/
|
||||||
|
generateCards(cards) {
|
||||||
|
let container = document.querySelector('.cards-container');
|
||||||
|
if (!container) {
|
||||||
|
container = document.createElement('div');
|
||||||
|
container.className = 'cards-container';
|
||||||
|
document.body.appendChild(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear existing cards
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
cards.forEach(cardData => {
|
||||||
|
const cardElement = this.createCardElement(cardData);
|
||||||
|
container.appendChild(cardElement);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create individual card element
|
||||||
|
*/
|
||||||
|
createCardElement(cardData) {
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'card';
|
||||||
|
|
||||||
|
const openInNewTab = this.config.settings.openLinksInNewTab;
|
||||||
|
const targetAttr = openInNewTab ? 'target="_blank" rel="noopener noreferrer"' : '';
|
||||||
|
|
||||||
|
card.innerHTML = `
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="card-icon" style="background-image: url('${cardData.iconPath}')"></div>
|
||||||
|
<h3 class="card-title">${cardData.title}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-links">
|
||||||
|
${cardData.links.map(link =>
|
||||||
|
`<a href="${link.url}" class="card-link" ${targetAttr}>${link.name}</a>`
|
||||||
|
).join('')}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up search functionality
|
||||||
|
*/
|
||||||
|
setupSearch(searchEngine) {
|
||||||
|
const searchInput = document.getElementById('search-input');
|
||||||
|
const searchButton = document.getElementById('search-button');
|
||||||
|
|
||||||
|
if (!searchInput || !searchButton) return;
|
||||||
|
|
||||||
|
const performSearch = () => {
|
||||||
|
const query = searchInput.value.trim();
|
||||||
|
if (query) {
|
||||||
|
const searchUrl = searchEngine.url + encodeURIComponent(query);
|
||||||
|
if (this.config.settings.openLinksInNewTab) {
|
||||||
|
window.open(searchUrl, '_blank', 'noopener,noreferrer');
|
||||||
|
} else {
|
||||||
|
window.location.href = searchUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
searchButton.addEventListener('click', performSearch);
|
||||||
|
searchInput.addEventListener('keypress', (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
performSearch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up additional event listeners
|
||||||
|
*/
|
||||||
|
setupEventListeners() {
|
||||||
|
// Handle configuration updates
|
||||||
|
window.addEventListener('configUpdated', (event) => {
|
||||||
|
this.handleConfigUpdate(event.detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle window resize for responsive behavior
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
this.handleResize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle configuration updates
|
||||||
|
*/
|
||||||
|
handleConfigUpdate(newConfig) {
|
||||||
|
console.log('Configuration updated, refreshing page...');
|
||||||
|
this.config = newConfig;
|
||||||
|
window.VOID_CONFIG = newConfig;
|
||||||
|
|
||||||
|
// Reapply styles and content
|
||||||
|
this.applyStyles();
|
||||||
|
this.initializePageContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle window resize
|
||||||
|
*/
|
||||||
|
handleResize() {
|
||||||
|
// Add any responsive behavior here
|
||||||
|
console.log('Window resized');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle initialization errors
|
||||||
|
*/
|
||||||
|
handleInitializationError(error) {
|
||||||
|
console.error('VoidPage initialization failed:', error);
|
||||||
|
|
||||||
|
// Create error message for user
|
||||||
|
const errorDiv = document.createElement('div');
|
||||||
|
errorDiv.className = 'error-message';
|
||||||
|
errorDiv.innerHTML = `
|
||||||
|
<h2>Loading Error</h2>
|
||||||
|
<p>Failed to load the page configuration. Please check the console for details.</p>
|
||||||
|
<button onclick="location.reload()">Retry</button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(errorDiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current configuration
|
||||||
|
*/
|
||||||
|
getConfig() {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if page is initialized
|
||||||
|
*/
|
||||||
|
isReady() {
|
||||||
|
return this.isInitialized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create global VoidPage instance
|
||||||
|
const voidPage = new VoidPage();
|
||||||
|
|
||||||
|
// Initialize when DOM is ready
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
voidPage.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make VoidPage globally available
|
||||||
|
window.VoidPage = VoidPage;
|
||||||
|
window.voidPage = voidPage;
|
||||||
|
|
||||||
|
// Export for module usage
|
||||||
|
// export { VoidPage, voidPage };
|
||||||
94
src/assets/scripts/configLoader.js
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/**
|
||||||
|
* Configuration loader utility
|
||||||
|
* Loads and parses the config.json file from the server
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ConfigLoader {
|
||||||
|
constructor() {
|
||||||
|
this.config = null;
|
||||||
|
this.configPath = '/src/config.json';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load configuration from the server
|
||||||
|
* @returns {Promise<Object>} The parsed configuration object
|
||||||
|
*/
|
||||||
|
async loadConfig() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(this.configPath);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to load config: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const configText = await response.text();
|
||||||
|
this.config = JSON.parse(configText);
|
||||||
|
|
||||||
|
console.log('Configuration loaded successfully:', this.config);
|
||||||
|
return this.config;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading configuration:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the loaded configuration
|
||||||
|
* @returns {Object|null} The configuration object or null if not loaded
|
||||||
|
*/
|
||||||
|
getConfig() {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a specific configuration value by key
|
||||||
|
* @param {string} key - The configuration key
|
||||||
|
* @param {*} defaultValue - Default value if key is not found
|
||||||
|
* @returns {*} The configuration value
|
||||||
|
*/
|
||||||
|
get(key, defaultValue = null) {
|
||||||
|
if (!this.config) {
|
||||||
|
console.warn('Configuration not loaded yet. Call loadConfig() first.');
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.config.hasOwnProperty(key) ? this.config[key] : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if configuration is loaded
|
||||||
|
* @returns {boolean} True if config is loaded
|
||||||
|
*/
|
||||||
|
isLoaded() {
|
||||||
|
return this.config !== null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a global instance for easy access
|
||||||
|
const configLoader = new ConfigLoader();
|
||||||
|
|
||||||
|
// Alternative function-based approach for simpler usage
|
||||||
|
async function loadConfiguration() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/src/config.json');
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = await response.json();
|
||||||
|
console.log('Configuration loaded:', config);
|
||||||
|
return config;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load configuration:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export for module usage (if using ES6 modules)
|
||||||
|
// export { ConfigLoader, configLoader, loadConfiguration };
|
||||||
|
|
||||||
|
// For immediate use in browser without modules
|
||||||
|
window.ConfigLoader = ConfigLoader;
|
||||||
|
window.configLoader = configLoader;
|
||||||
|
window.loadConfiguration = loadConfiguration;
|
||||||
9
src/assets/scripts/randomBackgrounds.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
fetch('/assets/backgrounds/bgs.json')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(images => {
|
||||||
|
const randomImage = images[Math.floor(Math.random() * images.length)];
|
||||||
|
document.body.style.backgroundImage = `url(${randomImage})`;
|
||||||
|
})
|
||||||
|
.catch(error => console.error('Error fetching image list:', error));
|
||||||
|
});
|
||||||
270
src/assets/scripts/styleParser.js
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
/**
|
||||||
|
* Style Parser and Applicator
|
||||||
|
* Builds and applies CSS stylesheets based on configuration values
|
||||||
|
*/
|
||||||
|
|
||||||
|
class StyleParser {
|
||||||
|
constructor() {
|
||||||
|
this.dynamicStyleElement = null;
|
||||||
|
this.cssVariablesApplied = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build CSS stylesheet text from configuration
|
||||||
|
* @param {Object} config - The parsed configuration object
|
||||||
|
* @returns {string} Complete CSS stylesheet text
|
||||||
|
*/
|
||||||
|
buildStylesheet(config) {
|
||||||
|
const { settings, cards } = config;
|
||||||
|
|
||||||
|
let css = `
|
||||||
|
/* Dynamic stylesheet generated from config.json */
|
||||||
|
|
||||||
|
/* Root CSS Variables */
|
||||||
|
:root {
|
||||||
|
--bg-image: url('${settings.backgroundImgPath}');
|
||||||
|
--header-bg-color: ${settings.headerBgColor};
|
||||||
|
--header-outline-color: ${settings.headerOutlineColor};
|
||||||
|
--header-outline-thickness: ${settings.headerOutlineThickness}px;
|
||||||
|
--card-bg-color: ${settings.cardBgColor};
|
||||||
|
--corner-radius: ${settings.cornerRadius}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Body and Background */
|
||||||
|
body {
|
||||||
|
background-image: var(--bg-image);
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-attachment: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header Styles */
|
||||||
|
.header,
|
||||||
|
header {
|
||||||
|
background-color: var(--header-bg-color);
|
||||||
|
border: var(--header-outline-thickness) solid var(--header-outline-color);
|
||||||
|
border-radius: var(--corner-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card Styles */
|
||||||
|
.card {
|
||||||
|
background-color: var(--card-bg-color);
|
||||||
|
border-radius: var(--corner-radius);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card Container */
|
||||||
|
.cards-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Link Target Behavior */
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Add link target behavior based on config
|
||||||
|
if (settings.openLinksInNewTab) {
|
||||||
|
css += `
|
||||||
|
.card a {
|
||||||
|
target: _blank;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add card-specific styles if needed
|
||||||
|
cards.forEach((card, index) => {
|
||||||
|
if (card.iconPath) {
|
||||||
|
css += `
|
||||||
|
.card:nth-child(${index + 1}) .card-icon {
|
||||||
|
background-image: url('${card.iconPath}');
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
css += `
|
||||||
|
/* Card Icons */
|
||||||
|
.card-icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.cards-container {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
return css;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply CSS variables to document root
|
||||||
|
* @param {Object} settings - Configuration settings object
|
||||||
|
*/
|
||||||
|
applyCSSVariables(settings) {
|
||||||
|
const root = document.documentElement;
|
||||||
|
|
||||||
|
root.style.setProperty('--bg-image', `url('${settings.backgroundImgPath}')`);
|
||||||
|
root.style.setProperty('--header-bg-color', settings.headerBgColor);
|
||||||
|
root.style.setProperty('--header-outline-color', settings.headerOutlineColor);
|
||||||
|
root.style.setProperty('--header-outline-thickness', `${settings.headerOutlineThickness}px`);
|
||||||
|
root.style.setProperty('--card-bg-color', settings.cardBgColor);
|
||||||
|
root.style.setProperty('--corner-radius', `${settings.cornerRadius}px`);
|
||||||
|
|
||||||
|
this.cssVariablesApplied = true;
|
||||||
|
console.log('CSS variables applied to document root');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and inject dynamic stylesheet into document head
|
||||||
|
* @param {string} cssText - CSS stylesheet text
|
||||||
|
* @returns {HTMLStyleElement} The created style element
|
||||||
|
*/
|
||||||
|
injectStylesheet(cssText) {
|
||||||
|
// Remove existing dynamic stylesheet if present
|
||||||
|
this.removeDynamicStylesheet();
|
||||||
|
|
||||||
|
// Create new style element
|
||||||
|
this.dynamicStyleElement = document.createElement('style');
|
||||||
|
this.dynamicStyleElement.type = 'text/css';
|
||||||
|
this.dynamicStyleElement.id = 'dynamic-config-styles';
|
||||||
|
this.dynamicStyleElement.textContent = cssText;
|
||||||
|
|
||||||
|
// Inject into document head
|
||||||
|
document.head.appendChild(this.dynamicStyleElement);
|
||||||
|
|
||||||
|
console.log('Dynamic stylesheet injected');
|
||||||
|
return this.dynamicStyleElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the dynamic stylesheet from document
|
||||||
|
*/
|
||||||
|
removeDynamicStylesheet() {
|
||||||
|
if (this.dynamicStyleElement) {
|
||||||
|
this.dynamicStyleElement.remove();
|
||||||
|
this.dynamicStyleElement = null;
|
||||||
|
console.log('Dynamic stylesheet removed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply link target behavior to existing links
|
||||||
|
* @param {boolean} openInNewTab - Whether to open links in new tab
|
||||||
|
*/
|
||||||
|
applyLinkTargetBehavior(openInNewTab) {
|
||||||
|
const links = document.querySelectorAll('.card a, .card-link');
|
||||||
|
|
||||||
|
links.forEach(link => {
|
||||||
|
if (openInNewTab) {
|
||||||
|
link.setAttribute('target', '_blank');
|
||||||
|
link.setAttribute('rel', 'noopener noreferrer');
|
||||||
|
} else {
|
||||||
|
link.removeAttribute('target');
|
||||||
|
link.removeAttribute('rel');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Link target behavior applied: ${openInNewTab ? 'new tab' : 'same tab'}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete style application from configuration
|
||||||
|
* @param {Object} config - The complete configuration object
|
||||||
|
*/
|
||||||
|
applyConfigStyles(config) {
|
||||||
|
try {
|
||||||
|
// Build and inject stylesheet
|
||||||
|
const cssText = this.buildStylesheet(config);
|
||||||
|
this.injectStylesheet(cssText);
|
||||||
|
|
||||||
|
// Apply CSS variables
|
||||||
|
this.applyCSSVariables(config.settings);
|
||||||
|
|
||||||
|
// Apply link behavior
|
||||||
|
this.applyLinkTargetBehavior(config.settings.openLinksInNewTab);
|
||||||
|
|
||||||
|
console.log('All configuration styles applied successfully');
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error applying configuration styles:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update specific style property
|
||||||
|
* @param {string} property - CSS property name
|
||||||
|
* @param {string} value - CSS property value
|
||||||
|
*/
|
||||||
|
updateStyleProperty(property, value) {
|
||||||
|
const root = document.documentElement;
|
||||||
|
const cssVarName = `--${property.replace(/([A-Z])/g, '-$1').toLowerCase()}`;
|
||||||
|
root.style.setProperty(cssVarName, value);
|
||||||
|
console.log(`Updated ${cssVarName}: ${value}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current dynamic stylesheet text
|
||||||
|
* @returns {string|null} Current stylesheet text or null if not applied
|
||||||
|
*/
|
||||||
|
getCurrentStylesheet() {
|
||||||
|
return this.dynamicStyleElement ? this.dynamicStyleElement.textContent : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if styles are currently applied
|
||||||
|
* @returns {boolean} True if dynamic styles are active
|
||||||
|
*/
|
||||||
|
isStylesApplied() {
|
||||||
|
return this.dynamicStyleElement !== null && this.cssVariablesApplied;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create global instance
|
||||||
|
const styleParser = new StyleParser();
|
||||||
|
|
||||||
|
// Utility functions for external access
|
||||||
|
function applyConfigurationStyles(config) {
|
||||||
|
return styleParser.applyConfigStyles(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildStylesheetFromConfig(config) {
|
||||||
|
return styleParser.buildStylesheet(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStyleProperty(property, value) {
|
||||||
|
return styleParser.updateStyleProperty(property, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeConfigurationStyles() {
|
||||||
|
return styleParser.removeDynamicStylesheet();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export for module usage
|
||||||
|
// export { StyleParser, styleParser, applyConfigurationStyles, buildStylesheetFromConfig };
|
||||||
|
|
||||||
|
// Global access
|
||||||
|
window.StyleParser = StyleParser;
|
||||||
|
window.styleParser = styleParser;
|
||||||
|
window.applyConfigurationStyles = applyConfigurationStyles;
|
||||||
|
window.buildStylesheetFromConfig = buildStylesheetFromConfig;
|
||||||
|
window.updateStyleProperty = updateStyleProperty;
|
||||||
|
window.removeConfigurationStyles = removeConfigurationStyles;
|
||||||
@ -1,16 +1,49 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>VoidPage</title>
|
<title>VoidPage</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="assets/main.css">
|
||||||
|
<script src="https://kit.fontawesome.com/a81a0c775a.js" crossorigin="anonymous"></script>
|
||||||
|
<!-- Background randomizer -->
|
||||||
|
<script defer src="assets/scripts/randomBackgrounds.js"></script>
|
||||||
|
<link rel="icon" type="image/x-icon" href="assets/images/favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Logo Container -->
|
<div class="container">
|
||||||
<div id="logo-container">
|
<div class="head">
|
||||||
|
<!-- Above-Search Logo -->
|
||||||
|
<div class="logo"><img src="assets/images/fox.svg" /></div>
|
||||||
|
<div class="search_box">
|
||||||
|
<!-- Google Search -->
|
||||||
|
<div id="gSearch" style="display:block;">
|
||||||
|
<form id="textField" class="google" action="https://google.com/search" method="get">
|
||||||
|
<input class="input_box" type="text" name="q" placeholder="Search" autofocus>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bookmark">
|
||||||
|
<div class="category">
|
||||||
|
<p class="title">Category One</p>
|
||||||
|
<div class="bookmarks">
|
||||||
|
<li><a class="bm" href="https://google.com">Google</a></li>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="category">
|
||||||
|
<p class="title">Category Two</p>
|
||||||
|
<div class="bookmarks">
|
||||||
|
<li><a class="bm" href="https://steampowered.com">Steam</a></li>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="category">
|
||||||
|
<p class="title">Category Three</p>
|
||||||
|
<div class="bookmarks">
|
||||||
|
<li><a class="bm" href="https://mail.google.com">GMail</a></li>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Search Bar -->
|
|
||||||
|
|
||||||
<!-- Categories -->
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||