├── Dockerfile ├── LICENSE ├── License.md ├── README.md ├── apps.json ├── assets ├── css │ └── styles.css └── js │ ├── data.js │ ├── script.js │ ├── search.js │ └── themer.js ├── favicon.ico ├── index.html ├── links.json ├── providers.json └── start.sh /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:alpine 2 | LABEL maintainer="Jeroen Pardon" 3 | 4 | EXPOSE 80 5 | 6 | RUN mkdir -p /usr/share/holding 7 | COPY . /usr/share/holding 8 | 9 | RUN chmod +x usr/share/holding/start.sh 10 | ENTRYPOINT ["./usr/share/holding/start.sh"] 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Note: this was forked for the purpose of triggering builds at dockerhub for an unRAID docker container template. 2 | I've also now started making slight modifications of this per request of users and some of my own findings as well. 3 | 4 | ## SUI 5 | *a startpage for your server and / or new tab page* 6 | 7 | ![screenshot](https://i.imgur.com/J4d7Q3D.png) 8 | 9 | [More screenshots](https://imgur.com/a/FDVRIyw) 10 | 11 | #### Changing color themes 12 | - Click the options button on the left bottom 13 | 14 | #### Apps 15 | Add your apps by editing apps.json: 16 | 17 | { 18 | "apps" : [ 19 | {"name":"Name of app 1","url":"https://sub1.example.com","icon":"icon-name"}, 20 | {"name":"Name of app 2","url":"https://sub2.example.com","icon":"icon-name"} 21 | ] 22 | } 23 | 24 | Please note: 25 | 26 | - You must start URLs with http:// or https:// 27 | - No `,` at the end of the last app's line 28 | - Find the names of icons to use at [Material Design Icons](https://materialdesignicons.com/) 29 | 30 | #### Bookmarks 31 | Add your bookmarks by editing links.json: 32 | 33 | ``` 34 | { 35 | "bookmarks":[ 36 | { 37 | "category":"Category1", 38 | "links":[ 39 | { 40 | "name":"Link1", 41 | "url":"http://example.com" 42 | }, 43 | { 44 | "name":"Link2", 45 | "url":"http://example.com" 46 | } 47 | ] 48 | }, 49 | { 50 | "category":"Category2", 51 | "links":[ 52 | { 53 | "name":"Link1", 54 | "url":"http://example.com" 55 | }, 56 | { 57 | "name":"Link2", 58 | "url":"http://example.com" 59 | } 60 | ] 61 | } 62 | ] 63 | } 64 | ``` 65 | Add names for the categories you wish to define and add the bookmarks for each category. 66 | 67 | Please note: 68 | 69 | - You must start URLs with http:// or https:// 70 | - No `,` at the end of the last bookmark in a category and at the end of the last category 71 | 72 | #### Search Providers 73 | Add your providers by editing assets/js/search.js starting at line 59: 74 | 75 | Here is an example of one added with /z as the triggeer, and searching a test domain. 76 | You will also want to edit providers.json to show your new provider in the web interface as well. 77 | 78 | ``` 79 | function search(text) { 80 | var option = text.substr(1, text.indexOf(' ') - 1) || text.substr(1); 81 | var subtext = text.substr(2 + option.length); 82 | if (text[0] === '/') { 83 | if (text.indexOf(' ') > -1) { 84 | switch (option) { 85 | case "z": 86 | window.location = "https://www.testdomaon.com/search/all/" + subtext; 87 | break; 88 | case "am": 89 | window.location = "https://www.allmusic.com/search/all/" + subtext; 90 | break; 91 | ``` 92 | Add names for the categories you wish to define and add the bookmarks for each category. 93 | 94 | #### Color themes 95 | These can be added or customized in the themer.js file. When changing the name of a theme or adding one, make sure to edit this section in index.html accordingly: 96 | 97 | ``` 98 |
99 | ``` 100 | 101 | 102 | -------------------------------------------------------------------------------- /apps.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps" : [ 3 | {"name":"Bazarr","url":"http://10.0.0.10:8080","icon":"message-video"}, 4 | {"name":"CloudCMD","url":"http://files.example.com","icon":"folder-multiple-outline"}, 5 | {"name":"Cockpit","url":"http://10.0.0.10:8888","icon":"airplane"}, 6 | {"name":"Feedbin","url":"http://rss.example.com","icon":"rss"}, 7 | {"name":"Filestash","url":"http://cloud.example.com","icon":"package"}, 8 | {"name":"Jackett","url":"http://jackett.example.com","icon":"tshirt-crew-outline"}, 9 | {"name":"Lidarr","url":"http://music.example.com","icon":"music"}, 10 | {"name":"Minio","url":"http://minio.example.com","icon":"server"}, 11 | {"name":"Mylar","url":"http://comics.example.com","icon":"book-open-variant"}, 12 | {"name":"Nextcloud","url":"http://cloud.example.com","icon":"weather-cloudy"}, 13 | {"name":"Ombi","url":"http://request.example.com","icon":"file-find-outline"}, 14 | {"name":"Pi-hole","url":"http://pihole.example.com","icon":"do-not-disturb"}, 15 | {"name":"Plex","url":"http://play.example.com","icon":"plex"}, 16 | {"name":"Portainer","url":"http://port1.example.com","icon":"docker"}, 17 | {"name":"Radarr","url":"http://movies.example.com","icon":"filmstrip"}, 18 | {"name":"Sonarr","url":"http://tv.example.com","icon":"television-box"}, 19 | {"name":"Stackedit","url":"http://md.example.com","icon":"markdown"}, 20 | {"name":"Transmission","url":"http://dl.example.com","icon":"progress-download"}, 21 | {"name":"Ubooquity","url":"http://opds.example.com","icon":"library-shelves"}, 22 | {"name":"Youtube-DL","url":"http://yt.example.com","icon":"youtube"} 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /assets/css/styles.css: -------------------------------------------------------------------------------- 1 | html{ 2 | box-sizing: border-box; 3 | moz-box-sizing: border-box; 4 | webkit-box-sizing: border-box; 5 | webkit-text-size-adjust: none; 6 | } 7 | 8 | html, 9 | body{ 10 | background-color: var(--color-background); 11 | color: var(--color-text-pri); 12 | font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif; 13 | font-size: 14px; 14 | font-weight: 400; 15 | height: auto; 16 | letter-spacing: -.012em; 17 | margin: 0; 18 | padding: 0; 19 | webkit-font-smoothing: antialiased; 20 | width: 100vw; 21 | overflow-x: hidden; /* dirty hack to hide horizontal scrollbars */ 22 | } 23 | 24 | *, 25 | *:before, 26 | *:after{ 27 | box-sizing: inherit; 28 | moz-box-sizing: inherit; 29 | webkit-box-sizing: inherit; 30 | } 31 | 32 | :root{ 33 | module-spacing: 3vh; 34 | } 35 | 36 | 37 | /* TEXT STYLES */ 38 | 39 | h1, h2{ 40 | font-weight: 300; 41 | margin: 0; 42 | padding: 0; 43 | text-align: left; 44 | } 45 | 46 | h2, h3, h4{ 47 | text-transform: uppercase; 48 | } 49 | 50 | h1{ 51 | font-size: 4em; 52 | font-weight: 700; 53 | margin-bottom: 0.5em; 54 | } 55 | 56 | h2{ 57 | font-size: 16px; 58 | height: 30px; 59 | 60 | } 61 | 62 | h3{ 63 | font-size: 20px; 64 | font-weight: 900; 65 | height: 10px; 66 | } 67 | 68 | h4{ 69 | font-size: 1.1em; 70 | font-weight: 400; 71 | height: 10px; 72 | } 73 | 74 | a{ 75 | color: var(--color-text-pri); 76 | text-decoration: none; 77 | } 78 | 79 | a:hover{ 80 | text-decoration: underline; 81 | webkit-text-decoration-color: var(--color-text-acc); 82 | webkit-text-decoration-skip: true; 83 | } 84 | 85 | .icon{ 86 | font-size: 2.5em; 87 | } 88 | 89 | 90 | /* FORMS */ 91 | 92 | input{ 93 | background-color: transparent; 94 | border: 0; 95 | border-bottom: thin solid var(--color-text-acc); 96 | color: var(--color-text-pri); 97 | font-size: 0.8em; 98 | height: 3.5em; 99 | transition: all 0.4s ease; 100 | width: 100%; 101 | } 102 | 103 | input:focus{ 104 | color-border: var(--color-text-pri); 105 | outline: none; 106 | } 107 | 108 | input:focus{ 109 | opacity: 1; 110 | } 111 | 112 | 113 | /* TABLES */ 114 | 115 | table{ 116 | border: thin solid #e4e4e4; 117 | border-collapse: collapse; 118 | border-spacing: 0; 119 | font-size: 1em; 120 | text-align: left; 121 | width: 100%; 122 | } 123 | 124 | table td:nth-of-type(2){ 125 | padding-right: 5em; 126 | } 127 | 128 | table td{ 129 | border: thin solid #e4e4e4; 130 | color: #333333; 131 | font-size: 1em; 132 | overflow: hidden; 133 | padding: 10px 5px; 134 | word-break: normal; 135 | } 136 | 137 | table th{ 138 | border: thin solid #e4e4e4; 139 | color: #333333; 140 | font-weight: bold; 141 | padding: 10px 5px; 142 | } 143 | 144 | table a{ 145 | color: #333333; 146 | } 147 | 148 | 149 | /* ANIMATION */ 150 | 151 | .fade{ 152 | opacity: 0; 153 | } 154 | 155 | @keyframes fadeseq{ 156 | 100% { 157 | opacity: 1; 158 | } 159 | } 160 | 161 | .fade{ 162 | opacity: 0; 163 | } 164 | 165 | .fade{ 166 | animation: fadeseq .3s forwards; 167 | } 168 | 169 | .fade:nth-child(2){ 170 | animation-delay: .4s; 171 | } 172 | 173 | 174 | /* LAYOUT */ 175 | 176 | #container{ 177 | align-items: stretch; 178 | display: grid; 179 | grid-column-gap: 20px; 180 | grid-row-gap: 3vh; 181 | grid-template-columns: 1fr; 182 | grid-template-rows: 8vh auto; 183 | justify-items: stretch; 184 | margin-left: auto; 185 | margin-right: auto; 186 | margin-top: 5vh; 187 | width: 60%; 188 | } 189 | 190 | 191 | 192 | /* SECTIONS */ 193 | 194 | #header{ 195 | border-bottom: 0px solid var(--color-text-acc); 196 | z-index: 1; 197 | } 198 | 199 | #apps_loop{ 200 | border-bottom: 0px solid var(--color-text-acc); 201 | display: grid; 202 | grid-column-gap: 0px; 203 | grid-row-gap: 0px; 204 | grid-template-columns: 1fr 1fr 1fr 1fr; 205 | grid-template-rows: 64px; 206 | padding-bottom: var(--module-spacing); 207 | } 208 | 209 | .apps_icon{ 210 | height: 64px; 211 | margin-right: 1em; 212 | padding-top: 15px; 213 | } 214 | 215 | .apps_icon span{ 216 | font-size: 2.5em; 217 | line-height: 3rem; 218 | } 219 | 220 | .apps_item{ 221 | display: flex; 222 | flex-direction: row; 223 | flex-wrap: wrap; 224 | height: 64px; 225 | margin: 0; 226 | } 227 | 228 | .apps_text{ 229 | display: flex; 230 | flex-direction: column; 231 | justify-content: center; 232 | } 233 | 234 | .apps_text a{ 235 | font-size: 1em; 236 | font-weight: 500; 237 | text-transform: uppercase; 238 | } 239 | 240 | .apps_text span{ 241 | color: var(--color-text-acc); 242 | font-size: 0.8em; 243 | text-transform: uppercase; 244 | } 245 | 246 | 247 | #links_loop{ 248 | display: grid; 249 | flex-wrap: nowrap; 250 | grid-column-gap: 20px; 251 | grid-row-gap: 0px; 252 | grid-template-columns: 1fr 1fr 1fr 1fr; 253 | grid-template-rows: auto; 254 | } 255 | 256 | #links_item{ 257 | line-height: 1.5rem; 258 | margin-bottom: 2em; 259 | webkit-font-smoothing: antialiased; 260 | } 261 | 262 | #links_item h4{ 263 | color: var(--color-text-acc); 264 | } 265 | 266 | #links_item a{ 267 | display: block; 268 | line-height: 2; 269 | } 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | /* MODAL */ 286 | 287 | 288 | #modal{ 289 | bottom: 0; 290 | left: 0; 291 | opacity: 0; 292 | pointer-events: none; 293 | position: fixed; 294 | right: 0; 295 | top: 0; 296 | transition: all 0.3s; 297 | z-index: 20; 298 | } 299 | 300 | #modal:target{ 301 | opacity: 1; 302 | pointer-events: auto; 303 | } 304 | 305 | #modal>div{ 306 | background-color: #ffffff; 307 | box-shadow: 0 14px 28px rgba(0, 0, 0, 0.30), 0 15px 12px rgba(0, 0, 0, 0.25); 308 | margin-left: auto; 309 | margin-right: auto; 310 | padding: 2em; 311 | margin-top: 25vh; 312 | width: 50%; 313 | display: flex; 314 | flex-direction: column; 315 | } 316 | 317 | #modal h1{ 318 | color: #333333; 319 | font-size: 2em; 320 | } 321 | 322 | #modal h2{ 323 | margin-top:1.5em; 324 | } 325 | 326 | #modal-header{ 327 | display:flex; 328 | justify-content: space-between; 329 | } 330 | 331 | #modal-footer{ 332 | display:flex; 333 | font-size:2em; 334 | justify-content: flex-start; 335 | } 336 | 337 | #modal-footer a{ 338 | margin-right:0.25em; 339 | } 340 | 341 | .modal-close{ 342 | color: #000000; 343 | font-size: 1.5em; 344 | text-align: center; 345 | text-decoration: none; 346 | } 347 | 348 | .modal-close:hover{ 349 | color: #000; 350 | } 351 | 352 | #modal_init a{ 353 | bottom: 1vh; 354 | color: var(--color-text-acc); 355 | left: 1vw; 356 | position: fixed; 357 | } 358 | 359 | #modal_init a:hover{ 360 | color: var(--color-text-pri); 361 | } 362 | 363 | #modal-theme{ 364 | border-bottom: 0px solid var(--color-text-acc); 365 | display: flex; 366 | flex-wrap: wrap; 367 | margin-bottom: 2em; 368 | } 369 | 370 | #providers{ 371 | margin-bottom: 2em; 372 | } 373 | 374 | 375 | /* THEMING */ 376 | 377 | .theme-button{ 378 | font-size: 0.8em; 379 | margin: 2px; 380 | width:128px; 381 | line-height: 3em; 382 | text-align: center; 383 | text-transform: uppercase; 384 | } 385 | 386 | .theme-blackboard{ 387 | background-color: #000000; 388 | border: 4px solid #5c5c5c; 389 | color: #FFFDEA; 390 | } 391 | 392 | .theme-gazette{ 393 | background-color: #F2F7FF; 394 | border: 4px solid #5c5c5c; 395 | color: #000000; 396 | } 397 | 398 | .theme-espresso{ 399 | background-color: #21211F; 400 | border: 4px solid #4E4E4E; 401 | color: #D1B59A; 402 | } 403 | 404 | .theme-cab{ 405 | background-color: #FEED01; 406 | border: 4px solid #424242; 407 | color: #1F1F1F; 408 | } 409 | 410 | .theme-cloud{ 411 | background-color: #f1f2f0; 412 | border: 4px solid #35342f; 413 | color: #37bbe4; 414 | } 415 | 416 | .theme-lime{ 417 | background-color: #263238; 418 | border: 4px solid #AABBC3; 419 | color: #aeea00; 420 | } 421 | 422 | .theme-passion{ 423 | background-color: #f5f5f5; 424 | border: 4px solid #8e24aa; 425 | color: #12005e; 426 | } 427 | 428 | .theme-blues{ 429 | background-color: #2B2C56; 430 | border: 4px solid #6677EB; 431 | color: #EFF1FC; 432 | } 433 | 434 | .theme-chalk{ 435 | background-color: #263238; 436 | border: 4px solid #FF869A; 437 | color: #AABBC3; 438 | } 439 | 440 | .theme-tron{ 441 | background-color: #242B33; 442 | border: 4px solid #6EE2FF; 443 | color: #EFFBFF; 444 | } 445 | 446 | .theme-paper{ 447 | background-color: #F8F6F1; 448 | border: 4px solid #F5E1A4; 449 | color: #4C432E; 450 | } 451 | 452 | 453 | /* MEDIA QUERIES */ 454 | 455 | @media screen and (max-width: 1260px) 456 | { 457 | #container 458 | { 459 | align-items: stretch; 460 | display: grid; 461 | grid-column-gap: 10px; 462 | grid-row-gap: 0px; 463 | grid-template-columns: 1fr; 464 | grid-template-rows: 80px auto; 465 | justify-items: stretch; 466 | margin-bottom: 1vh; 467 | margin-left: auto; 468 | margin-right: auto; 469 | width: 90%; 470 | } 471 | 472 | #apps_loop{ 473 | grid-template-columns: 1fr 1fr 1fr; 474 | width: 100vw; 475 | } 476 | 477 | #links_loop { 478 | grid-template-columns: 1fr 1fr 1fr; 479 | } 480 | 481 | #modal>div{ 482 | margin-left: auto; 483 | margin-right: auto; 484 | margin-top: 25vh; 485 | width: 90%; 486 | } 487 | } 488 | 489 | @media screen and (max-width: 667px) 490 | { 491 | html{ 492 | font-size: calc(16px + 6 * ((100vw - 320px) / 680)); 493 | } 494 | 495 | #container{ 496 | align-items: stretch; 497 | display: grid; 498 | grid-column-gap: 20px; 499 | grid-row-gap: 0px; 500 | grid-template-columns: 1fr; 501 | grid-template-rows: 80px auto; 502 | justify-items: stretch; 503 | margin-bottom: 1vh; 504 | width: 90%; 505 | } 506 | 507 | h1{ 508 | font-size: 4em; 509 | height: auto; 510 | margin-bottom: 0em; 511 | } 512 | 513 | h2{ 514 | font-size: 1em; 515 | height: auto; 516 | margin-bottom: 0em; 517 | } 518 | 519 | h3{ 520 | font-size: 1em; 521 | } 522 | 523 | #apps_loop{ 524 | grid-column-gap: 0px; 525 | grid-row-gap: 0px; 526 | grid-template-columns: 1fr 1fr; 527 | width: 100vw; 528 | } 529 | 530 | .apps_icon{ 531 | height: 64px; 532 | margin-right: 0.8em; 533 | padding-top: 14px; 534 | } 535 | 536 | .apps_icon span{ 537 | font-size: 2em; 538 | line-height: 2.5rem; 539 | } 540 | 541 | #links_loop{ 542 | display: grid; 543 | flex-wrap: nowrap; 544 | grid-column-gap: 20px; 545 | grid-row-gap: 0px; 546 | grid-template-columns: 1fr 1fr; 547 | grid-template-rows: auto; 548 | } 549 | } 550 | -------------------------------------------------------------------------------- /assets/js/data.js: -------------------------------------------------------------------------------- 1 | var data_links = "links.json"; 2 | 3 | $(document).ready(function(){ 4 | $.getJSON(data_links, 5 | function (data) { 6 | var mysource = $('#links-template').html(); 7 | var mytemplate = Handlebars.compile(mysource); 8 | var myresult = mytemplate(data) 9 | $('#links').html(myresult); 10 | }); 11 | }); 12 | 13 | var data_apps = "apps.json"; 14 | 15 | $(document).ready(function(){ 16 | $.getJSON(data_apps, 17 | function (data) { 18 | var mysource = $('#apps-template').html(); 19 | var mytemplate = Handlebars.compile(mysource); 20 | var myresult = mytemplate(data) 21 | $('#apps').html(myresult); 22 | }); 23 | }); 24 | 25 | var data_providers = "providers.json"; 26 | 27 | $(document).ready(function(){ 28 | $.getJSON(data_providers, 29 | function (data) { 30 | var mysource = $('#providers-template').html(); 31 | var mytemplate = Handlebars.compile(mysource); 32 | var myresult = mytemplate(data) 33 | $('#providers').html(myresult); 34 | }); 35 | }); -------------------------------------------------------------------------------- /assets/js/script.js: -------------------------------------------------------------------------------- 1 | function date() { 2 | let currentDate = new Date(); 3 | let dateOptions = { 4 | weekday: "long", 5 | year: "numeric", 6 | month: "long", 7 | day: "numeric" 8 | }; 9 | let date = currentDate.toLocaleDateString("en-GB", dateOptions); 10 | document.getElementById("header_date").innerHTML = date; 11 | } 12 | 13 | function greet() { 14 | let currentTime = new Date(); 15 | let greet = Math.floor(currentTime.getHours() / 6); 16 | switch (greet) { 17 | case 0: 18 | document.getElementById("header_greet").innerHTML = "Good night!"; 19 | break; 20 | case 1: 21 | document.getElementById("header_greet").innerHTML = "Good morning!"; 22 | break; 23 | case 2: 24 | document.getElementById("header_greet").innerHTML = "Good afternoon!"; 25 | break; 26 | case 3: 27 | document.getElementById("header_greet").innerHTML = "Good evening!"; 28 | break; 29 | } 30 | } 31 | 32 | function loadFunctions() { 33 | date(); 34 | greet(); 35 | } 36 | 37 | 38 | -------------------------------------------------------------------------------- /assets/js/search.js: -------------------------------------------------------------------------------- 1 | var sindex = 0; 2 | var cycle = false; 3 | 4 | function start() { 5 | var query = getParameterByName('q'); 6 | if (query) search(query.replaceAll("+", "%2B")); 7 | 8 | document.getElementById('keywords').focus(); 9 | 10 | window.setInterval(function () { 11 | updatetime(); 12 | }, 200); 13 | } 14 | 15 | function handleKeyPress(e) { 16 | var key = e.keyCode || e.which; 17 | var text = document.getElementById("keywords").value.replaceAll("+", "%2B"); 18 | var option = text.substr(1, text.indexOf(' ') - 1) || text.substr(1); 19 | var subtext = text.substr(2 + option.length); 20 | if (key == 13) { // Search functions 21 | search(text); 22 | } 23 | if (key == 9) { // Tab Completion Functions 24 | e.preventDefault(); 25 | e.stopPropagation(); 26 | if (text[0] === ';') { 27 | switch (option) { 28 | case 't': 29 | var streamers = ['admiralbahroo', 'moonmoon_ow', 'witwix']; 30 | if (!subtext || cycle) { 31 | cycle = true; 32 | if (sindex > streamers.length - 1) sindex = 0; 33 | document.getElementById("keywords").value = ';t ' + streamers[sindex++]; 34 | return; 35 | } 36 | for (var streamer of streamers) { 37 | if (subtext === streamer.substr(0, subtext.length)) { 38 | document.getElementById("keywords").value = ';t ' + streamer; 39 | return; 40 | } 41 | } 42 | break; 43 | } 44 | } 45 | } 46 | if(key == 32){ //Space to go to search 47 | document.getElementById("keywords").focus(); 48 | } 49 | sindex = 0; 50 | cycle = false; 51 | } 52 | 53 | function search(text) { 54 | var option = text.substr(1, text.indexOf(' ') - 1) || text.substr(1); 55 | var subtext = text.substr(2 + option.length); 56 | if (text[0] === '/') { 57 | if (text.indexOf(' ') > -1) { 58 | switch (option) { 59 | case "am": 60 | window.location = "https://www.allmusic.com/search/all/" + subtext; 61 | break; 62 | case "d": 63 | window.location = "https://duckduckgo.com/?q=" + subtext; 64 | break; 65 | case "di": 66 | window.location = "https://www.discogs.com/search/?q=" + subtext; 67 | break; 68 | case "i": 69 | window.location = "https://www.imdb.com/find?q=" + subtext; 70 | break; 71 | case "m": 72 | window.location = "https://www.themoviedb.org/search?query=" + subtext; 73 | break; 74 | case "r": 75 | window.location = "https://www.reddit.com/search?q=" + subtext; 76 | break; 77 | case "q": 78 | window.location = "https://www.qwant.com/?q=" + subtext; 79 | break; 80 | case "so": 81 | window.location = "https://soundcloud.com/search?q=" + subtext; 82 | break; 83 | case "s": 84 | window.location = "https://open.spotify.com/search/results/" + subtext; 85 | break; 86 | case "t": 87 | window.location = "https://trakt.tv/search?query=" + subtext; 88 | break; 89 | case "tv": 90 | window.location = "https://www.thetvdb.com/search?q=" + subtext; 91 | break; 92 | case "y": 93 | window.location = "https://www.youtube.com/results?search_query=" + subtext; 94 | break; 95 | } 96 | } else { 97 | var option = text.substr(1); 98 | switch (option) { 99 | case "d": 100 | window.location = "https://www.dukduckgo.com"; 101 | break; 102 | case "y": 103 | window.location = "https://www.youtube.com"; 104 | break; 105 | case "r": 106 | window.location = "https://reddit.com"; 107 | break; 108 | case "s": 109 | window.location = "https://open.spotify.com"; 110 | break; 111 | } 112 | } 113 | } else if (validURL(text)) { 114 | if (containsProtocol(text)) 115 | window.location = text; 116 | else 117 | window.location = "https://" + text; 118 | } else { 119 | window.location = "https://www.google.com/search?q=" + text; 120 | } 121 | } 122 | 123 | // Source: https://stackoverflow.com/questions/5717093/check-if-a-javascript-string-is-a-url 124 | function validURL(str) { 125 | var pattern = new RegExp('^(https?:\\/\\/)?' + // protocol 126 | '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name 127 | '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address 128 | '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path 129 | '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string 130 | '(\\#[-a-z\\d_]*)?$', 'i'); // fragment locator 131 | return !!pattern.test(str); 132 | } 133 | 134 | function containsProtocol(str) { 135 | var pattern = new RegExp('^(https?:\\/\\/){1}.*', 'i'); 136 | return !!pattern.test(str); 137 | } 138 | 139 | String.prototype.replaceAll = function(search, replacement) { 140 | var target = this; 141 | return target.split(search).join(replacement); 142 | }; -------------------------------------------------------------------------------- /assets/js/themer.js: -------------------------------------------------------------------------------- 1 | const setValue = (property, value) => { 2 | if (value) { 3 | document.documentElement.style.setProperty(`--${property}`, value); 4 | 5 | const input = document.querySelector(`#${property}`); 6 | if (input) { 7 | value = value.replace('px', ''); 8 | input.value = value; 9 | } 10 | } 11 | }; 12 | 13 | const setValueFromLocalStorage = property => { 14 | let value = localStorage.getItem(property); 15 | setValue(property, value); 16 | }; 17 | 18 | const setTheme = options => { 19 | for (let option of Object.keys(options)) { 20 | const property = option; 21 | const value = options[option]; 22 | 23 | setValue(property, value); 24 | localStorage.setItem(property, value); 25 | } 26 | } 27 | 28 | document.addEventListener('DOMContentLoaded', () => { 29 | setValueFromLocalStorage('color-background'); 30 | setValueFromLocalStorage('color-text-pri'); 31 | setValueFromLocalStorage('color-text-acc'); 32 | }); 33 | 34 | const dataThemeButtons = document.querySelectorAll('[data-theme]'); 35 | 36 | for (let i = 0; i < dataThemeButtons.length; i++) { 37 | dataThemeButtons[i].addEventListener('click', () => { 38 | const theme = dataThemeButtons[i].dataset.theme; 39 | 40 | switch (theme) { 41 | case 'blackboard': 42 | setTheme({ 43 | 'color-background': '#1a1a1a', 44 | 'color-text-pri': '#FFFDEA', 45 | 'color-text-acc': '#5c5c5c' 46 | }); 47 | return; 48 | 49 | case 'gazette': 50 | setTheme({ 51 | 'color-background': '#F2F7FF', 52 | 'color-text-pri': '#000000', 53 | 'color-text-acc': '#5c5c5c' 54 | }); 55 | return; 56 | 57 | case 'espresso': 58 | setTheme({ 59 | 'color-background': '#21211F', 60 | 'color-text-pri': '#D1B59A', 61 | 'color-text-acc': '#4E4E4E' 62 | }); 63 | return; 64 | 65 | case 'cab': 66 | setTheme({ 67 | 'color-background': '#F6D305', 68 | 'color-text-pri': '#1F1F1F', 69 | 'color-text-acc': '#424242' 70 | }); 71 | return; 72 | 73 | case 'cloud': 74 | setTheme({ 75 | 'color-background': '#f1f2f0', 76 | 'color-text-pri': '#35342f', 77 | 'color-text-acc': '#37bbe4' 78 | }); 79 | return; 80 | 81 | case 'lime': 82 | setTheme({ 83 | 'color-background': '#263238', 84 | 'color-text-pri': '#AABBC3', 85 | 'color-text-acc': '#aeea00' 86 | }); 87 | return; 88 | 89 | case 'white': 90 | setTheme({ 91 | 'color-background': '#ffffff', 92 | 'color-text-pri': '#222222', 93 | 'color-text-acc': '#dddddd' 94 | }); 95 | return; 96 | 97 | case 'tron': 98 | setTheme({ 99 | 'color-background': '#242B33', 100 | 'color-text-pri': '#EFFBFF', 101 | 'color-text-acc': '#6EE2FF' 102 | }); 103 | return; 104 | 105 | case 'blues': 106 | setTheme({ 107 | 'color-background': '#2B2C56', 108 | 'color-text-pri': '#EFF1FC', 109 | 'color-text-acc': '#6677EB' 110 | }); 111 | return; 112 | 113 | case 'passion': 114 | setTheme({ 115 | 'color-background': '#f5f5f5', 116 | 'color-text-pri': '#12005e', 117 | 'color-text-acc': '#8e24aa' 118 | }); 119 | return; 120 | 121 | case 'chalk': 122 | setTheme({ 123 | 'color-background': '#263238', 124 | 'color-text-pri': '#AABBC3', 125 | 'color-text-acc': '#FF869A' 126 | }); 127 | return; 128 | 129 | case 'paper': 130 | setTheme({ 131 | 'color-background': '#F8F6F1', 132 | 'color-text-pri': '#4C432E', 133 | 'color-text-acc': '#AA9A73' 134 | }); 135 | return; 136 | 137 | } 138 | }) 139 | } -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CorneliousJD/SUI-Startpage/fb4094a047d25cd21352c0849b5b623171e7974c/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SUI 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 67 | 68 |
69 | 70 | 73 | 74 | 78 | 79 |
80 | 96 |
97 | 98 | 113 |
114 | 115 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /links.json: -------------------------------------------------------------------------------- 1 | { 2 | "bookmarks" : [ 3 | { 4 | "category": "Communicate", 5 | "links": [ 6 | { 7 | "name": "Discord", 8 | "url": "https://discordapp.com/" 9 | }, 10 | { 11 | "name": "Gmail", 12 | "url": "http://gmail.com" 13 | }, 14 | { 15 | "name": "Slack", 16 | "url": "https://slack.com/signin" 17 | } 18 | ] 19 | }, 20 | { 21 | "category": "Cloud", 22 | "links": [ 23 | { 24 | "name": "Box", 25 | "url": "https://box.net" 26 | }, 27 | { 28 | "name": "Dropbox", 29 | "url": "https://dropbox.com" 30 | }, 31 | { 32 | "name": "Drive", 33 | "url": "https://drive.google.com" 34 | } 35 | ] 36 | }, 37 | { 38 | "category": "Design", 39 | "links": [ 40 | { 41 | "name": "Awwwards", 42 | "url": "https://awwwards.com" 43 | }, 44 | { 45 | "name": "Dribbble", 46 | "url": "https://dribbble.com" 47 | }, 48 | { 49 | "name": "Muz.li", 50 | "url": "https://medium.muz.li/" 51 | } 52 | ] 53 | }, 54 | { 55 | "category": "Dev", 56 | "links": [ 57 | { 58 | "name": "Codepen", 59 | "url": "https://codepen.io/" 60 | }, 61 | { 62 | "name": "Devdocs", 63 | "url": "https://devdocs.io" 64 | }, 65 | { 66 | "name": "Devhints", 67 | "url": "https://devhints.io" 68 | } 69 | ] 70 | }, 71 | { 72 | "category": "Lifestyle", 73 | "links": [ 74 | { 75 | "name": "Design Milk", 76 | "url": "https://design-milk.com/category/interior-design/" 77 | }, 78 | { 79 | "name": "Dwell", 80 | "url": "https://www.dwell.com/" 81 | }, 82 | { 83 | "name": "Freshome", 84 | "url": "https://freshome.com/" 85 | } 86 | ] 87 | }, 88 | { 89 | "category": "Media", 90 | "links": [ 91 | { 92 | "name": "Spotify", 93 | "url": "http://browse.spotify.com" 94 | }, 95 | { 96 | "name": "Trakt", 97 | "url": "http://trakt.tv" 98 | }, 99 | { 100 | "name": "YouTube", 101 | "url": "http://youtube.com/subscriptions" 102 | } 103 | ] 104 | }, 105 | { 106 | "category": "Reading", 107 | "links": [ 108 | { 109 | "name": "Instapaper", 110 | "url": "https://www.instapaper.com/u" 111 | }, 112 | { 113 | "name": "Medium", 114 | "url": "http://medium.com" 115 | }, 116 | { 117 | "name": "Reddit", 118 | "url": "http://reddit.com" 119 | } 120 | ] 121 | }, 122 | { 123 | "category": "Tech", 124 | "links": [ 125 | { 126 | "name": "TheNextWeb", 127 | "url": "https://thenextweb.com/" 128 | }, 129 | { 130 | "name": "The Verge", 131 | "url": "https://theverge.com/" 132 | }, 133 | { 134 | "name": "MIT Technology Review", 135 | "url": "https://www.technologyreview.com/" 136 | } 137 | ] 138 | } 139 | ] 140 | } -------------------------------------------------------------------------------- /providers.json: -------------------------------------------------------------------------------- 1 | { 2 | "providers" : [ 3 | {"name":"Allmusic","url":"https://www.allmusic.com/search/all/","prefix":"/a"}, 4 | {"name":"Discogs","url":"https://www.discogs.com/search/?q=","prefix":"/di"}, 5 | {"name":"Duck Duck Go","url":"https://duckduckgo.com/?q=","prefix":"/d"}, 6 | {"name":"iMDB","url":"https://www.imdb.com/find?q=","prefix":"/i"}, 7 | {"name":"TheMovieDB","url":"https://www.themoviedb.org/search?query=","prefix":"/m"}, 8 | {"name":"Reddit","url":"https://www.reddit.com/search?q=","prefix":"/r"}, 9 | {"name":"Qwant","url":"https://www.qwant.com/?q=","prefix":"/q"}, 10 | {"name":"Soundcloud","url":"https://soundcloud.com/search?q=","prefix":"/so"}, 11 | {"name":"Spotify","url":"https://open.spotify.com/search/results/","prefix":"/s"}, 12 | {"name":"TheTVDB","url":"https://www.thetvdb.com/search?q=","prefix":"/tv"}, 13 | {"name":"Trakt","url":"https://trakt.tv/search?query=","prefix":"/t"} 14 | ] 15 | } -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #check for existing files, copy them into place if needed. 4 | if [ ! -f /usr/share/nginx/html/index.html ]; then 5 | cp /usr/share/holding/index.html /usr/share/nginx/html/index.html 6 | fi 7 | 8 | if [ ! -f /usr/share/nginx/html/apps.json ]; then 9 | cp /usr/share/holding/apps.json /usr/share/nginx/html/apps.json 10 | fi 11 | 12 | if [ ! -f /usr/share/nginx/html/links.json ]; then 13 | cp /usr/share/holding/links.json /usr/share/nginx/html/links.json 14 | fi 15 | 16 | if [ ! -f /usr/share/nginx/html/providers.json ]; then 17 | cp /usr/share/holding/providers.json /usr/share/nginx/html/providers.json 18 | fi 19 | 20 | if [ ! -f /usr/share/nginx/html/favicon.ico ]; then 21 | cp /usr/share/holding/favicon.ico /usr/share/nginx/html/favicon.ico 22 | fi 23 | 24 | #Check assets folder, copy it in if it doesn't exist. 25 | if [ ! -d /usr/share/nginx/html/assets ]; then 26 | cp -R /usr/share/holding/assets /usr/share/nginx/html 27 | fi 28 | 29 | #remove uncessary files 30 | if [ -f /usr/share/nginx/html/License.md ]; then 31 | rm /usr/share/nginx/html/License.md 32 | fi 33 | if [ -f /usr/share/nginx/html/README.md ]; then 34 | rm /usr/share/nginx/html/README.md 35 | fi 36 | if [ -f /usr/share/nginx/html/start.sh ]; then 37 | rm /usr/share/nginx/html/start.sh 38 | fi 39 | 40 | #chmod web files to be executable 41 | chmod -R 777 /usr/share/nginx/html 42 | 43 | #run nginx 44 | nginx -g "daemon off;" 45 | --------------------------------------------------------------------------------