Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97a194d962 | ||
|
|
53d8f1f6b3 | ||
|
|
246a28e493 | ||
|
|
6ca2898a50 | ||
|
|
05df302ecd | ||
|
|
26320c5909 | ||
|
|
8cad9b0d21 | ||
|
|
3661f92c15 | ||
|
|
a12b03ad6b | ||
|
|
94894c7e6f | ||
|
|
8b7e59ca50 | ||
|
|
dc818bd2b5 | ||
|
|
53198646bb | ||
|
|
a4794fd83d | ||
|
|
f07ac80030 | ||
|
|
a401ff0b46 | ||
|
|
8011f7c029 | ||
|
|
20802593b3 | ||
|
|
75013e7211 | ||
|
|
217fbe8229 | ||
|
|
b31d84c68a | ||
|
|
719be38d48 | ||
|
|
7f888c0724 | ||
|
|
c536886eb9 | ||
|
|
1b9133db1b | ||
|
|
561e7cf491 | ||
|
|
19827e2957 | ||
|
|
4069f27750 | ||
|
|
ed05c47d99 | ||
|
|
d9ca19ada6 | ||
|
|
b3c301e27a | ||
|
|
0e83664295 | ||
|
|
aaf8b63af6 | ||
|
|
45e624e907 | ||
|
|
350caf0022 | ||
|
|
41852129a6 | ||
|
|
cbfddba907 | ||
|
|
8366d2e714 | ||
|
|
c9e18a56c8 | ||
|
|
34acddedaa | ||
|
|
8a94d6d112 | ||
|
|
d56530b744 | ||
|
|
0d3ec6be71 | ||
|
|
467f2e9ae0 | ||
|
|
bad263d5b1 | ||
|
|
a7a4d6ce08 | ||
|
|
4b521b5a60 | ||
|
|
21fb6ec091 | ||
|
|
d09cb3971b | ||
|
|
ffe3e6da5c | ||
|
|
0d0e8c03dd | ||
|
|
105cf985e6 | ||
|
|
3cb54d7509 | ||
|
|
193d326ed0 | ||
|
|
abdf70864e | ||
|
|
3240609a7f | ||
|
|
929d3aab9b | ||
|
|
c0bd3d78d5 | ||
|
|
41f465ad94 | ||
|
|
4f618a96b6 |
56
README.md
56
README.md
@@ -1,51 +1,7 @@
|
||||
# Minimal Startpage
|
||||
my fork of [this](https://github.com/deepjyoti30/startpage/) start page
|
||||
|
||||
Just another minimal startpage for browsers.
|
||||
|
||||
<img src=".github/startpage.gif">
|
||||
|
||||
## How to Use
|
||||
|
||||
- Clone the repo
|
||||
|
||||
```console
|
||||
git clone https://github.com/deepjyoti30/startpage
|
||||
```
|
||||
|
||||
Alternately, you can download and extract from the browser.
|
||||
|
||||
### Chrome
|
||||
|
||||
- On chrome, open extensions from the tool menu or open it from [chrome://extensions](chrome://extensions).
|
||||
- Click on load unpacked, navigate to the directory where you cloned the repo and select it.
|
||||
|
||||
### Firefox (temporary)
|
||||
|
||||
- Open ```about:debugging``` from your Firefox browser.
|
||||
- Click on This Firefox and select ```Load Temporary Add On...```
|
||||
- Open the cloned repo directory and select any file there (preferrably ```manifest.json```)
|
||||
- It should be installed.
|
||||
|
||||
## Upcoming
|
||||
|
||||
Will be availabe on Firefox and Chrome add on stores in a while.
|
||||
|
||||
I thought I would work on adding a settings page that would handle the updating the settings but I'm just way to bored and honestly I've come to realize I hate GUI's (I mean I've always loved CLI based apps). Anyway, the extension can still be used and I'm sure the crowd I aimed this extension at knows how to edit a ```json``` file, for the time being I'll just let you guys install it and use it and edit the settings from the ```config```.
|
||||
|
||||
## Settings
|
||||
|
||||
You can edit the settings by writing ```--setting``` in the search bar and clicking enter.
|
||||
|
||||
A JSON editor will open up where you can make the changes.
|
||||
|
||||
| Name | Supported Values | Default | Description |
|
||||
| ---- | ----- | ------- | ------ |
|
||||
| searchEngine | \<DuckDuckGo \| Google \| Bing \|Yahoo\> | DuckDuckGo | Search Engine to use for searching from the bar |
|
||||
| user | string | Deepjyoti (That's my name) | Name of the user to use on the welcome message |
|
||||
| disableMessage | \<false \| true\> | false | Hide the Welcome message |
|
||||
| disableDate | \<false \| true\> | false | Hide the date |
|
||||
| disableSearchBar | \<false \| true\> | false | Hide the search bar |
|
||||
| disable24Hour | \<false \| true\> | true | Disable 24 hour clock and show time in 12 hour format |
|
||||
| disableWeather | \<false \| true\> | true | Disable the weather beside the date |
|
||||
| weatherConf | Object (Check default for child keys) | {"location": "Pune India","unit": "fah"}| Configuration for the weather, location and unit etc. In "unit", "fah" is short for Fahrenheit and "cel" for Celcius, however the whole word can also be passed. |
|
||||
| squares | Object of arrays | Check [config.json](https://github.com/deepjyoti30/startpage/blob/master/config.json) for default values | Values and Names of shortcuts for the cards. |
|
||||
i just changed the color palette to onedark,
|
||||
you will find the documentations and everything on the original repo
|
||||
# SCREENSHOTS
|
||||
<img src = "https://i.imgur.com/s5T9dKK.png">
|
||||
<img src = "https://i.imgur.com/wJagxIv.png">
|
||||
|
||||
40
config.json
40
config.json
@@ -1,19 +1,25 @@
|
||||
{
|
||||
"squares": [
|
||||
{ "name": "media", "links": [
|
||||
{ "name": "media",
|
||||
"color": "#61afef",
|
||||
"links": [
|
||||
{ "name": "Spotify", "url": "https://open.spotify.com/" },
|
||||
{ "name": "YoutubeMusic", "url": "https://music.youtube.com/" },
|
||||
{ "name": "Netflix", "url": "https://netflix.com" },
|
||||
{ "name": "Youtube", "url": "https://youtube.com" }
|
||||
]
|
||||
},
|
||||
{ "name": "work", "links": [
|
||||
{ "name": "work",
|
||||
"color": "#e06c75",
|
||||
"links": [
|
||||
{ "name": "Github", "url": "https://github.com" },
|
||||
{ "name": "Heroku", "url": "http://heroku.com/" },
|
||||
{ "name": "localhost", "url": "http://localhost:8000/" }
|
||||
]
|
||||
},
|
||||
{ "name": "social", "links": [
|
||||
{ "name": "social",
|
||||
"color": "#e5c07b",
|
||||
"links": [
|
||||
{ "name": "Reddit", "url": "https://reddit.com" },
|
||||
{ "name": "Unixporn Love", "url": "https://deepjyoti30.github.io/unixporn-love/" },
|
||||
{ "name": "r/unixporn", "url": "https://www.reddit.com/r/unixporn/" },
|
||||
@@ -21,7 +27,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Others", "links": [
|
||||
"name": "Others",
|
||||
"color": "#98c379",
|
||||
"links": [
|
||||
{"name": "Material Colors", "url": "http://materialuicolors.co/"},
|
||||
{"name": "Gmail", "url": "http://gmail.com/"},
|
||||
{"name": "neo4j Local", "url": "http://localhost:7474/"},
|
||||
@@ -29,15 +37,29 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"searchEngine":"DuckDuckGo",
|
||||
"user": "Deepjyoti",
|
||||
"searchEngine":"Brave",
|
||||
"user": "Pirate",
|
||||
"disableMessage": false,
|
||||
"disableDate": false,
|
||||
"disableSearchBar": false,
|
||||
"disable24Hour": false,
|
||||
"disableWeather": true,
|
||||
"disable24Hour": true,
|
||||
"disableWeather": false,
|
||||
"timeZone": "Africa/Cairo",
|
||||
"weatherConf": {
|
||||
"location": "Pune India",
|
||||
"location": "Giza Egypt",
|
||||
"unit": "cel"
|
||||
},
|
||||
"settingsIcon": false,
|
||||
"style": {
|
||||
"backgroundColor": "#282c34",
|
||||
"messageColor": "#fff",
|
||||
"dateColor": "#c8ccd4",
|
||||
"lineColor": "#fff",
|
||||
"weatherColor": "#c8ccd4",
|
||||
"searchColor": "#c8ccd4",
|
||||
"searchBackgroundColor": "#1e222a",
|
||||
"squareColor": "#abb2bf",
|
||||
"squareBackgroundColor": "#1e222a",
|
||||
"autocompleteHighlightBackgroundColor": "#3e4451"
|
||||
}
|
||||
}
|
||||
|
||||
61
css/main.css
61
css/main.css
@@ -32,9 +32,11 @@ body {
|
||||
width: 50%;
|
||||
margin: 0 auto 50px auto;
|
||||
height: 45px;
|
||||
padding-top: 35px; }
|
||||
.main #search-bar input {
|
||||
height: inherit;
|
||||
padding-top: 35px;
|
||||
position: relative;
|
||||
display: inline-block; }
|
||||
.main #search-bar #search-bar-input {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
background: #2e2e2e;
|
||||
box-shadow: none;
|
||||
@@ -44,10 +46,38 @@ body {
|
||||
outline: none;
|
||||
padding: 7px 14px;
|
||||
font-size: 18px;
|
||||
color: #fff; }
|
||||
.main #search-bar input:focus {
|
||||
color: #fff;
|
||||
box-sizing: border-box; }
|
||||
.main #search-bar #search-bar-input:focus {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); }
|
||||
.main #search-bar .autocomplete-items-container {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
border-radius: 6px;
|
||||
background: #2e2e2e;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); }
|
||||
.main #search-bar .autocomplete-items-container .autocomplete-item {
|
||||
width: 100%;
|
||||
background: #2e2e2e;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
padding: 7px 14px;
|
||||
font-size: 18px;
|
||||
color: #fff;
|
||||
box-sizing: border-box;
|
||||
z-index: -1;
|
||||
cursor: pointer; }
|
||||
.main #search-bar .autocomplete-items-container .autocomplete-item:hover {
|
||||
background: #3b3b3b; }
|
||||
.main #search-bar .autocomplete-items-container .autocomplete-active {
|
||||
background: #353535; }
|
||||
@media only screen and (max-width: 791px) {
|
||||
.main #search-bar {
|
||||
width: 65%; } }
|
||||
@@ -63,10 +93,14 @@ body {
|
||||
background: #2e2e2e;
|
||||
padding: 15px 15px;
|
||||
border-radius: 6px;
|
||||
min-height: 150px; }
|
||||
min-height: 150px;
|
||||
margin-top: 10px; }
|
||||
.main #other-content .sqr h4 {
|
||||
font-size: 18px;
|
||||
margin: 15px; }
|
||||
.main #other-content .sqr h4 a {
|
||||
color: inherit;
|
||||
font-size: inherit; }
|
||||
.main #other-content .sqr:hover {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); }
|
||||
@@ -103,7 +137,7 @@ body {
|
||||
.settings-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
z-index: 3;
|
||||
width: 80vw;
|
||||
height: 80vh;
|
||||
overflow: auto;
|
||||
@@ -122,3 +156,16 @@ body {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
cursor: pointer; }
|
||||
|
||||
#settings-cog {
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
position: absolute;
|
||||
bottom: 25px;
|
||||
right: 25px;
|
||||
transition: 2s ease-in;
|
||||
display: none; }
|
||||
#settings-cog:hover .cog {
|
||||
fill: white;
|
||||
stroke: white;
|
||||
transition: 2s ease-in; }
|
||||
|
||||
12
index.html
12
index.html
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>startpage</title>
|
||||
<title>New Tab</title>
|
||||
<!--Json Editor-->
|
||||
<link rel="stylesheet" href="css/jsoneditor.min.css" type="text/css">
|
||||
<!--Custom CSS-->
|
||||
@@ -30,14 +30,22 @@
|
||||
</div>
|
||||
<div id="search-bar">
|
||||
<input id="search-bar-input"></input>
|
||||
<div class="autocomplete-items-container">
|
||||
</div>
|
||||
</div>
|
||||
<div id="other-content">
|
||||
</div>
|
||||
<svg id="settings-cog" xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-settings" viewBox="0 0 24 24" stroke-width="1.5" stroke="#607D8B" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z"/>
|
||||
<path class="cog" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</svg>
|
||||
</div>
|
||||
<!--JSON Editor-->
|
||||
<script src="js/jsoneditor.min.js"></script>
|
||||
<!--Custom JS-->
|
||||
<script src="js/settings.js"></script>
|
||||
<script src="js/autocomplete.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
118
js/autocomplete.js
Normal file
118
js/autocomplete.js
Normal file
@@ -0,0 +1,118 @@
|
||||
function autocomplete(inp, passedValues, style) {
|
||||
var currentFocus;
|
||||
|
||||
/*execute a function when someone writes in the text field:*/
|
||||
inp.addEventListener("input", function(e) {
|
||||
var item, val = this.value;
|
||||
var parentContainer = document.getElementsByClassName('autocomplete-items-container')[0];
|
||||
|
||||
/*close any already open lists of autocompleted values*/
|
||||
closeAllLists();
|
||||
if (!val) {
|
||||
parentContainer.style.paddingBottom = "0";
|
||||
return false;
|
||||
}
|
||||
currentFocus = -1;
|
||||
|
||||
// Update some values of the parent container
|
||||
parentContainer.setAttribute("id", this.id + "-autocomplete-list");
|
||||
parentContainer.style.paddingBottom = "1rem";
|
||||
|
||||
if(style["searchBackgroundColor"]) {
|
||||
parentContainer.style.backgroundColor = style["searchBackgroundColor"];
|
||||
}
|
||||
|
||||
/*for each item in the array...*/
|
||||
Object.keys(passedValues).forEach((el, i, arr) => {
|
||||
|
||||
/*check if the item starts with the same letters as the text field value:*/
|
||||
if (arr[i].toLowerCase().includes(val.toLowerCase())) {
|
||||
/*create a DIV element for each matching element:*/
|
||||
item = document.createElement("DIV");
|
||||
item.setAttribute("class", "autocomplete-item");
|
||||
// Add the url as an attribute
|
||||
item.setAttribute('url', passedValues[el]);
|
||||
|
||||
if(style["searchBackgroundColor"]) {
|
||||
item.style.backgroundColor = style["searchBackgroundColor"];
|
||||
}
|
||||
|
||||
if(style["searchColor"]) {
|
||||
item.style.color = style["searchColor"];
|
||||
}
|
||||
|
||||
/*make the matching letters bold:*/
|
||||
item.innerHTML = "<strong>" + arr[i].substr(0, val.length) + "</strong>";
|
||||
item.innerHTML += arr[i].substr(val.length);
|
||||
/*insert a input field that will hold the current array item's value:*/
|
||||
item.innerHTML += "<input type='hidden' value='" + arr[i] + "'>";
|
||||
/*execute a function when someone clicks on the item value (DIV element):*/
|
||||
item.addEventListener("click", function(e) {
|
||||
/*insert the value for the autocomplete text field:*/
|
||||
inp.value = this.getElementsByTagName("input")[0].value;
|
||||
/*close the list of autocompleted values,
|
||||
(or any other open lists of autocompleted values:*/
|
||||
closeAllLists();
|
||||
});
|
||||
parentContainer.appendChild(item);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
/*execute a function presses a key on the keyboard:*/
|
||||
inp.addEventListener("keydown", function(e) {
|
||||
var x = document.getElementById(this.id + "-autocomplete-list");
|
||||
if (x) x = x.getElementsByTagName("div");
|
||||
if (e.keyCode == 40) {
|
||||
/*If the arrow DOWN key is pressed,
|
||||
increase the currentFocus variable:*/
|
||||
currentFocus++;
|
||||
/*and and make the current item more visible:*/
|
||||
addActive(x);
|
||||
}
|
||||
else if (e.keyCode == 38) {
|
||||
/*If the arrow UP key is pressed,
|
||||
decrease the currentFocus variable:*/
|
||||
currentFocus--;
|
||||
/*and and make the current item more visible:*/
|
||||
addActive(x);
|
||||
}
|
||||
else if (e.keyCode == 13) {
|
||||
/*If the ENTER key is pressed, prevent the form from being submitted,*/
|
||||
if (currentFocus > -1) {
|
||||
e.preventDefault();
|
||||
/*and simulate a click on the "active" item:*/
|
||||
if (x) window.location = x[0].getAttribute('url');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function addActive(x) {
|
||||
/*a function to classify an item as "active":*/
|
||||
if (!x) return false;
|
||||
/*start by removing the "active" class on all items:*/
|
||||
removeActive(x);
|
||||
if (currentFocus >= x.length) currentFocus = 0;
|
||||
if (currentFocus < 0) currentFocus = (x.length - 1);
|
||||
/*add class "autocomplete-active":*/
|
||||
x[currentFocus].classList.add("autocomplete-active");
|
||||
}
|
||||
|
||||
function removeActive(x) {
|
||||
/*a function to remove the "active" class from all autocomplete items:*/
|
||||
for (var i = 0; i < x.length; i++) {
|
||||
x[i].classList.remove("autocomplete-active");
|
||||
}
|
||||
}
|
||||
|
||||
function closeAllLists(elmnt) {
|
||||
/*close all autocomplete lists in the document,
|
||||
except the one passed as an argument:*/
|
||||
document.getElementsByClassName("autocomplete-items-container")[0].innerHTML = "";
|
||||
}
|
||||
|
||||
/*execute a function when someone clicks in the document:*/
|
||||
document.addEventListener("click", function (e) {
|
||||
closeAllLists(e.target);
|
||||
});
|
||||
}
|
||||
370
js/main.js
370
js/main.js
@@ -1,7 +1,10 @@
|
||||
window.onload = function() {
|
||||
this.initBody()
|
||||
this.initBody();
|
||||
this.listenForSettings();
|
||||
}
|
||||
|
||||
|
||||
debug = false; // Enable while testing on local
|
||||
searchBarDivId = "search-bar"
|
||||
searchBarId = "search-bar-input"
|
||||
messageDivId = "message"
|
||||
@@ -10,6 +13,7 @@ dateId = "date-text"
|
||||
weatherId = "weather-text"
|
||||
lineId = "line"
|
||||
messageId = "message-text"
|
||||
timeZ = undefined
|
||||
otherContentId = "other-content"
|
||||
userName = ""
|
||||
disable24Hour = false;
|
||||
@@ -29,20 +33,29 @@ bgClassContainer = [
|
||||
searchEngines = {
|
||||
"Google": "https://www.google.com/search?q=",
|
||||
"DuckDuckGo": "https://duckduckgo.com/?q=",
|
||||
"Bing": "https://www.bing.com/search?q=",
|
||||
"Yahoo": "https://search.yahoo.com/search?p="
|
||||
"Brave": "https://search.brave.com/search?q=",
|
||||
"Yahoo": "https://search.yahoo.com/search?p=",
|
||||
"Ecosia": "https://www.ecosia.org/search?q="
|
||||
}
|
||||
validWeatherUnit = [
|
||||
"fah", "cel"
|
||||
]
|
||||
validQuickLinks = {}
|
||||
|
||||
|
||||
function initBody() {
|
||||
/**
|
||||
* Function called when the body is loaded.
|
||||
*
|
||||
*
|
||||
* Do everything like adding an event listener to
|
||||
* other things.
|
||||
*/
|
||||
// If running on local, just read the conf
|
||||
if (debug) {
|
||||
readJSON("config.json");
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the json file
|
||||
BROWSER.storage.sync.get(result => {
|
||||
Object.keys(result).length == 0 ? readJSON("config.json") : parseAndCreate(result)
|
||||
@@ -58,18 +71,23 @@ function initSearchBar(jsonData) {
|
||||
searchEngine = "Google"
|
||||
}
|
||||
searchUrl = this.searchEngines[searchEngine]
|
||||
document.getElementById(searchBarId).placeholder = `Search something on ${searchEngine}`
|
||||
document.getElementById(searchBarId).placeholder = `${searchEngine} Search`
|
||||
document.getElementById(searchBarId).addEventListener("keypress", (event) => {
|
||||
if (event.key != 'Enter') return
|
||||
|
||||
// Open google with the search results.
|
||||
// Do whatever the user wants to do
|
||||
query = document.getElementById(searchBarId).value
|
||||
if (query == "--setting") {
|
||||
|
||||
// Open settings
|
||||
if (query == "--setting" || query == "--settings") {
|
||||
showSettings()
|
||||
document.getElementById(searchBarId).value = ""
|
||||
// Remove the autocomplete boxes
|
||||
document.getElementById("search-bar-input-autocomplete-list").style.display = "none";
|
||||
return
|
||||
}
|
||||
|
||||
// If not others, then it's probably a search
|
||||
query = query.replace(/\ /g, "+")
|
||||
document.location = searchUrl + query
|
||||
})
|
||||
@@ -78,7 +96,7 @@ function initSearchBar(jsonData) {
|
||||
function buildMsg() {
|
||||
/**
|
||||
* Build a nice message for the user.
|
||||
*
|
||||
*
|
||||
* Following is how the message would be decided.
|
||||
* 0 - 5:59 : It's too late, take some sleep
|
||||
* 6 - 8:59 : You're up early
|
||||
@@ -108,12 +126,34 @@ function buildMsg() {
|
||||
return ""
|
||||
}
|
||||
|
||||
function handleMessage(userName) {
|
||||
/**
|
||||
* Handle the creation of the message
|
||||
*
|
||||
* Build the message based on the time of the day.
|
||||
* If the message is null then add just the username
|
||||
* Else, add the username before the message.
|
||||
*/
|
||||
var builtMsg = buildMsg()
|
||||
builtMsg == "" ?
|
||||
builtMsg = `Hello ${userName}` : builtMsg = `Hey ${userName}, ${builtMsg}!`
|
||||
return builtMsg;
|
||||
}
|
||||
|
||||
function updateTime() {
|
||||
/**
|
||||
* Get the current time and date and return it.
|
||||
*/
|
||||
currentDate = new Date()
|
||||
finalDate = currentDate.toLocaleString(undefined, {day:'numeric', month:'short', hour:'numeric', minute:'numeric',hour12:disable24Hour})
|
||||
options = {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
hour12: disable24Hour,
|
||||
timeZone: timeZ
|
||||
}
|
||||
finalDate = currentDate.toLocaleString(undefined, options)
|
||||
document.getElementById(dateId).textContent = finalDate
|
||||
}
|
||||
|
||||
@@ -124,16 +164,6 @@ function updateTimeHook() {
|
||||
}, 30 * 1000)
|
||||
}
|
||||
|
||||
function getFahrenheit(inCelcius) {
|
||||
return Math.floor((inCelcius * 9 / 5) + 32)
|
||||
}
|
||||
|
||||
function indexUppercase(unformatted) {
|
||||
return unformatted.split(" ").map(w => {
|
||||
return w[0].toUpperCase() + w.substring(1)
|
||||
}).join(" ")
|
||||
}
|
||||
|
||||
function updateWeather(weatherConfig) {
|
||||
/**
|
||||
* Get the weather using the location passed by the user using
|
||||
@@ -143,7 +173,7 @@ function updateWeather(weatherConfig) {
|
||||
userLocation = weatherConfig["location"].replace(/\ /g, ",")
|
||||
passedUnit = weatherConfig["unit"]
|
||||
unit = validWeatherUnit.includes(passedUnit.substring(0, 3)) ?
|
||||
passedUnit : "cel"
|
||||
passedUnit : "cel"
|
||||
|
||||
fetchUrl = apiUrl + `?q=${userLocation}&appid=${appId}&units=metric`
|
||||
|
||||
@@ -154,16 +184,12 @@ function updateWeather(weatherConfig) {
|
||||
weatherType = jsonData["weather"][0]["main"]
|
||||
|
||||
temp = !unit.includes("cel") ?
|
||||
getFahrenheit(temp) + "°F" : temp + "°C"
|
||||
getFahrenheit(temp) + "°F" : temp + "°C"
|
||||
weatherText = temp + ", " + indexUppercase(weatherType)
|
||||
document.getElementById(weatherId).innerHTML = weatherText
|
||||
})
|
||||
}
|
||||
|
||||
function inRange(number, min, max) {
|
||||
return (number >= min && number <= max)
|
||||
}
|
||||
|
||||
function readJSON(fileName) {
|
||||
// Load the data of the passed file.
|
||||
fetch(fileName)
|
||||
@@ -175,6 +201,8 @@ function readJSON(fileName) {
|
||||
}
|
||||
|
||||
function saveSettings(settings) {
|
||||
if (debug) return;
|
||||
|
||||
BROWSER.storage.sync.set(settings)
|
||||
}
|
||||
|
||||
@@ -184,13 +212,22 @@ function parseAndCreate(jsonData) {
|
||||
*/
|
||||
this.userName = jsonData["user"]
|
||||
|
||||
// Build a message for the user
|
||||
builtMsg = buildMsg()
|
||||
builtMsg == "" ?
|
||||
builtMsg = `Hello ${this.userName}` : builtMsg = `Hey ${this.userName}, ${builtMsg}!`
|
||||
// Enable the settings button if it is enabled
|
||||
if (jsonData["settingsIcon"]) enableCog();
|
||||
|
||||
// If the user has not passed any custom message
|
||||
if (Object.keys(jsonData).includes("message") &&
|
||||
typeof(jsonData["message"]) == "string" &&
|
||||
jsonData["message"] != "")
|
||||
builtMsg = jsonData["message"]
|
||||
else
|
||||
builtMsg = this.handleMessage(this.userName);
|
||||
|
||||
document.getElementById(messageId).textContent = builtMsg
|
||||
// Check if 24 hour is disabled
|
||||
disable24Hour = jsonData["disable24Hour"]
|
||||
timeZ = jsonData["timeZone"]
|
||||
timeZ = isValidTimeZone(timeZ) ? timeZ : undefined
|
||||
// Check if welcome message is supposed to be disabled
|
||||
if (jsonData["disableMessage"])
|
||||
document.getElementById(messageDivId).style.display = "none"
|
||||
@@ -219,34 +256,91 @@ function parseAndCreate(jsonData) {
|
||||
sqr = createSqr(element, index)
|
||||
document.getElementById(otherContentId).appendChild(sqr)
|
||||
})
|
||||
|
||||
|
||||
// Apply styling if present
|
||||
if (jsonData["style"]) {
|
||||
styleData = jsonData["style"]
|
||||
if (styleData["backgroundColor"]) {
|
||||
document.body.style.backgroundColor = styleData["backgroundColor"]
|
||||
}
|
||||
if (styleData["messageColor"]) {
|
||||
document.getElementById(messageId).style.color = styleData["messageColor"]
|
||||
}
|
||||
if (styleData["dateColor"]) {
|
||||
document.getElementById(dateId).style.color = styleData["dateColor"]
|
||||
}
|
||||
if (styleData["lineColor"]) {
|
||||
document.getElementById(lineId).style.color = styleData["lineColor"]
|
||||
}
|
||||
if (styleData["weatherColor"]) {
|
||||
document.getElementById(weatherId).style.color = styleData["weatherColor"]
|
||||
}
|
||||
if (styleData["searchColor"]) {
|
||||
document.getElementById(searchBarId).style.color = styleData["searchColor"]
|
||||
}
|
||||
if (styleData["searchBackgroundColor"]) {
|
||||
document.getElementById(searchBarId).style.backgroundColor = styleData["searchBackgroundColor"]
|
||||
autocompleteBackgroundColor = styleData["searchBackgroundColor"]
|
||||
}
|
||||
if (styleData["searchPlaceholderColor"]) {
|
||||
document.getElementById(searchBarId).classList.add(createPlaceholderStyleClass(styleData["searchPlaceholderColor"]));
|
||||
}
|
||||
if (styleData["autocompleteHighlightBackgroundColor"]) {
|
||||
addAutocompleteStyleClass(styleData["autocompleteHighlightBackgroundColor"]);
|
||||
}
|
||||
if (styleData["squareBackgroundColor"]) {
|
||||
elements = document.getElementsByClassName("sqr")
|
||||
var i;
|
||||
for (i = 0; i < elements.length; i++) {
|
||||
elements[i].style.backgroundColor = styleData["squareBackgroundColor"]
|
||||
}
|
||||
}
|
||||
if (styleData["squareColor"]) {
|
||||
elements = document.querySelectorAll(".sqr a")
|
||||
var i;
|
||||
for (i = 0; i < elements.length; i++) {
|
||||
elements[i].style.color = styleData["squareColor"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract the quicklinks from the sqrs
|
||||
extractQuickLinks(sqrs, jsonData["style"]);
|
||||
}
|
||||
|
||||
function createSqr(sqrData, index) {
|
||||
// Create a new square division with the passed element
|
||||
name = sqrData["name"]
|
||||
links = sqrData["links"]
|
||||
name = sqrData["name"];
|
||||
link = sqrData["url"];
|
||||
links = sqrData["links"];
|
||||
color = sqrData["color"];
|
||||
|
||||
// Sometimes, the user might not have set a value for the color,
|
||||
// in which case it will be undefined.
|
||||
colorValid = (color == undefined) ? false : isColorValid(color);
|
||||
|
||||
div = document.createElement("div")
|
||||
cls = document.createAttribute("class")
|
||||
div.setAttributeNode(cls)
|
||||
div.classList.add("sqr")
|
||||
|
||||
if (index > bgClassContainer.length - 1)
|
||||
customClass = "media"
|
||||
if (colorValid)
|
||||
customClass = createClass(color);
|
||||
else if (index > bgClassContainer.length - 1)
|
||||
customClass = 'media';
|
||||
else
|
||||
customClass = bgClassContainer[index]
|
||||
div.classList.add(customClass)
|
||||
customClass = bgClassContainer[index];
|
||||
|
||||
h4 = document.createElement("h4")
|
||||
h4.textContent = name
|
||||
div.classList.add(customClass);
|
||||
|
||||
h4 = getTitle(name, link);
|
||||
|
||||
div.appendChild(h4)
|
||||
|
||||
links.forEach(element => {
|
||||
aName = element["name"]
|
||||
aHref = element["url"]
|
||||
|
||||
|
||||
a = document.createElement("a")
|
||||
attrHref = document.createAttribute("href")
|
||||
attrHref.value = aHref
|
||||
@@ -258,4 +352,200 @@ function createSqr(sqrData, index) {
|
||||
})
|
||||
|
||||
return div
|
||||
}
|
||||
}
|
||||
|
||||
function getTitle(titleContent, linkHref=null) {
|
||||
/**
|
||||
* Create the title for the sqr card.
|
||||
*
|
||||
* The card will be optionally clicable and will open
|
||||
* a new link.
|
||||
*
|
||||
* If the link is not passed in the config then the title
|
||||
* will not be clickable.
|
||||
*/
|
||||
h4 = document.createElement("h4");
|
||||
|
||||
if (!linkHref) {
|
||||
h4.textContent = titleContent;
|
||||
return h4;
|
||||
}
|
||||
|
||||
// If the link is passed, create a nested child
|
||||
a = document.createElement("a");
|
||||
a.setAttribute("href", linkHref);
|
||||
a.textContent = titleContent;
|
||||
|
||||
h4.appendChild(a);
|
||||
return h4;
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
|
||||
function isValidTimeZone(tz) {
|
||||
if (!Intl || !Intl.DateTimeFormat().resolvedOptions().timeZone) {
|
||||
throw 'Time zones are not available in this environment';
|
||||
}
|
||||
|
||||
try {
|
||||
Intl.DateTimeFormat(undefined, {timeZone: tz});
|
||||
return true;
|
||||
}
|
||||
catch (ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getFahrenheit(inCelcius) {
|
||||
return Math.floor((inCelcius * 9 / 5) + 32)
|
||||
}
|
||||
|
||||
function indexUppercase(unformatted) {
|
||||
return unformatted.split(" ").map(w => {
|
||||
return w[0].toUpperCase() + w.substring(1)
|
||||
}).join(" ")
|
||||
}
|
||||
|
||||
function inRange(number, min, max) {
|
||||
return (number >= min && number <= max)
|
||||
}
|
||||
|
||||
function isColorValid(color) {
|
||||
/**
|
||||
* Check if the passed color is valid.
|
||||
*
|
||||
* Currently supports only css color names
|
||||
* or hex colors having 3 or 6 characters.
|
||||
*/
|
||||
// Check CSS match
|
||||
let defaultStyles = new Option().style;
|
||||
defaultStyles.color = color
|
||||
if (defaultStyles.color == color) return true;
|
||||
|
||||
// In case the above failed, check if it's a hex
|
||||
return /^#([0-9A-F]{3}){1,2}$/i.test(color);
|
||||
}
|
||||
|
||||
function createClass(color) {
|
||||
/**
|
||||
* Create a new class in a style and add it to
|
||||
* the head.
|
||||
*
|
||||
* I did check other alternatives since adding something like
|
||||
* this in the innerHTML is not a preferred way to go,
|
||||
* especially since I'm building this as an extension,
|
||||
* however, there's no other way.
|
||||
*
|
||||
* Since I also want to add hover effects, there seriously
|
||||
* is no other way to do that without adding a style or using
|
||||
* a library (cannot/don't want to do that because this is an extension).
|
||||
*/
|
||||
var style = document.createElement('style');
|
||||
const newClassName = `bg-${Math.random().toString(36).substring(7)}`;
|
||||
style.type = 'text/css';
|
||||
style.innerHTML = `.${newClassName} h4 {color: ${color} !important;} .${newClassName} a:hover {color: ${color} !important;}`;
|
||||
document.getElementsByTagName('head')[0].appendChild(style);
|
||||
|
||||
return newClassName;
|
||||
}
|
||||
|
||||
function createPlaceholderStyleClass(color) {
|
||||
/**
|
||||
* Create a new class with for placeholder styling.
|
||||
*
|
||||
* This is pretty much a continuation of what has done preivously
|
||||
* in the createClass function.
|
||||
*/
|
||||
var style = document.createElement('style');
|
||||
const newClassName = `bg-${Math.random().toString(36).substring(7)}`;
|
||||
style.type = 'text/css';
|
||||
style.innerHTML = `::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
|
||||
color: ${color} !important;
|
||||
opacity: 1; /* Firefox */
|
||||
}
|
||||
|
||||
:-ms-input-placeholder { /* Internet Explorer 10-11 */
|
||||
color: ${color} !important;
|
||||
}
|
||||
|
||||
::-ms-input-placeholder { /* Microsoft Edge */color: ${color} !important;}`;
|
||||
document.getElementsByTagName('head')[0].appendChild(style);
|
||||
|
||||
return newClassName;
|
||||
}
|
||||
|
||||
function addAutocompleteStyleClass(color) {
|
||||
/**
|
||||
* Add some colors for the autocomplete classes in order to
|
||||
* keep. We need to add styles for :hover property so it is
|
||||
* easier to just inject some CSS.
|
||||
*/
|
||||
var style = document.createElement("style");
|
||||
style.type = "text/css";
|
||||
style.innerHTML = `
|
||||
.autocomplete-item:hover {
|
||||
background: ${color} !important;
|
||||
}
|
||||
|
||||
.autocomplete-active {
|
||||
background: ${color} !important;
|
||||
}
|
||||
`;
|
||||
document.getElementsByTagName('head')[0].appendChild(style);
|
||||
}
|
||||
|
||||
|
||||
function extractQuickLinks(passedSqrs, style) {
|
||||
/**
|
||||
* Extract the quicklinks passed in the config
|
||||
*
|
||||
* Cache the quicklinks passed by the user in the config JSON
|
||||
* so that they can be used as a shortcut called from the
|
||||
* search bar.
|
||||
*/
|
||||
passedSqrs.forEach(linksContainer => {
|
||||
linksContainer.links.forEach(linkObject => this.validQuickLinks[linkObject.name] = linkObject.url);
|
||||
});
|
||||
|
||||
// Start the autocomplete
|
||||
autocomplete(document.getElementById("search-bar-input"), this.validQuickLinks, style);
|
||||
}
|
||||
|
||||
// Listen to key click
|
||||
|
||||
function listenForSettings() {
|
||||
document.onkeyup = event => {
|
||||
// Show the settings if ctrl and , is pressed
|
||||
if (event.ctrlKey && event.which == 188)
|
||||
showSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the settings cog
|
||||
|
||||
function enableCog() {
|
||||
/**
|
||||
* Enable the settings cog.
|
||||
*
|
||||
* It will be disabled by default, however, if the user
|
||||
* wishes to enable it through the config, it will be shown.
|
||||
*
|
||||
* Once shown, we need to add some event listeners to it as
|
||||
* well so it works the right way.
|
||||
*/
|
||||
settingsCogElement = document.getElementById("settings-cog");
|
||||
|
||||
// Unhide it
|
||||
settingsCogElement.style.display = "block";
|
||||
|
||||
// Add event listener
|
||||
settingsCogElement.onclick = function() {
|
||||
editor = showSettings()
|
||||
|
||||
// Add an onclick listener to hide settings if the button is clicked
|
||||
// again.
|
||||
settingsCogElement.onclick = () => {
|
||||
hideSettings(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,13 +29,26 @@ function showSettings() {
|
||||
loadJson(editor)
|
||||
|
||||
closeBtn.onclick = () => {
|
||||
modalEl.style.display = "none"
|
||||
// Get the updated JSON
|
||||
updatedJson = editor.get()
|
||||
BROWSER.storage.sync.set(updatedJson)
|
||||
document.getElementById(jsonContainer).innerHTML = ""
|
||||
location.reload()
|
||||
hideSettings(editor);
|
||||
}
|
||||
|
||||
return editor
|
||||
}
|
||||
|
||||
function hideSettings(editor) {
|
||||
/**
|
||||
* Hide the settings.
|
||||
*
|
||||
* This function is to be called when the settings window
|
||||
* is supposed to be hidden, This will automatically
|
||||
* handle saving the updated settings to the localstorage.
|
||||
*/
|
||||
modalEl.style.display = "none"
|
||||
// Get the updated JSON
|
||||
updatedJson = editor.get()
|
||||
BROWSER.storage.sync.set(updatedJson)
|
||||
document.getElementById(jsonContainer).innerHTML = ""
|
||||
location.reload()
|
||||
}
|
||||
|
||||
async function loadJson(editor) {
|
||||
@@ -47,7 +60,7 @@ async function loadJson(editor) {
|
||||
// Populate the editor
|
||||
editor.set(result)
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
function detectBrowser() {
|
||||
// Firefox
|
||||
@@ -56,5 +69,5 @@ function detectBrowser() {
|
||||
else if (!!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime))
|
||||
BROWSER = chrome
|
||||
|
||||
return BROWSER
|
||||
}
|
||||
return BROWSER;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "minimal-startpage",
|
||||
"short_name": "minimal-startpage",
|
||||
"version": "0.1",
|
||||
"version": "0.2",
|
||||
"description": "Overrides the browsers newtab page with a custom startpage.",
|
||||
"author": "deepjyoti30",
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
$background: #212121;
|
||||
$foreground: #fff;
|
||||
$background: #282c34;
|
||||
$foreground: #abb2bf;
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
|
||||
$media: #4DD0E1;
|
||||
$work: #F06292;
|
||||
$social: #FFF176;
|
||||
$others: #81C784;
|
||||
$funky: #4DB6AC;
|
||||
$purple: #9575CD;
|
||||
$media: #61afef;
|
||||
$work: #e06c75;
|
||||
$social: #e5c07b;
|
||||
$others: #98c379;
|
||||
$funky: #56b6c2;
|
||||
$purple: #c678dd;
|
||||
$upvoty: #FF8A65;
|
||||
$indigo: #7986CB;
|
||||
$foxxy: #A1887F;
|
||||
@@ -61,9 +61,12 @@ body {
|
||||
margin: 0 auto 50px auto;
|
||||
height: 45px;
|
||||
padding-top: 35px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
input {
|
||||
height: inherit;
|
||||
|
||||
#search-bar-input {
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
background: lighten($background, 5);
|
||||
box-shadow: none;
|
||||
@@ -74,12 +77,47 @@ body {
|
||||
padding: 7px 14px;
|
||||
font-size: 18px;
|
||||
color: $foreground;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
|
||||
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.autocomplete-items-container {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
border-radius: 6px;
|
||||
background: lighten($background, 5);
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
|
||||
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
|
||||
|
||||
.autocomplete-item {
|
||||
width: 100%;
|
||||
background: lighten($background, 5);
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
padding: 7px 14px;
|
||||
font-size: 18px;
|
||||
color: $foreground;
|
||||
box-sizing: border-box;
|
||||
z-index: -1;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: lighten($background, 10);
|
||||
}
|
||||
}
|
||||
|
||||
.autocomplete-active {
|
||||
background: lighten($background, 8);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 791px) {
|
||||
@@ -101,10 +139,16 @@ body {
|
||||
padding: 15px 15px;
|
||||
border-radius: 6px;
|
||||
min-height: 150px;
|
||||
margin-top: 10px;
|
||||
|
||||
h4 {
|
||||
font-size: 18px;
|
||||
margin: 15px;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -144,7 +188,7 @@ body {
|
||||
h4, a:hover {
|
||||
color: $social;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.others {
|
||||
@@ -188,7 +232,7 @@ body {
|
||||
.settings-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
z-index: 3;
|
||||
width: 80vw;
|
||||
height: 80vh;
|
||||
overflow: auto;
|
||||
@@ -213,4 +257,20 @@ body {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#settings-cog {
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
position: absolute;
|
||||
bottom: 25px;
|
||||
right: 25px;
|
||||
transition: 2s ease-in;
|
||||
display: none;
|
||||
|
||||
&:hover .cog {
|
||||
fill: white;
|
||||
stroke: white;
|
||||
transition: 2s ease-in;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user