mirror of
https://github.com/xvvvyz/tilde.git
synced 2026-03-11 14:44:24 +00:00
493 lines
13 KiB
HTML
493 lines
13 KiB
HTML
<!doctype html>
|
|
|
|
<script>
|
|
'use strict';
|
|
|
|
var config = {
|
|
categories: [
|
|
{ name: "Education", commands: [
|
|
{ key: 'c', name: 'Coursera', url: 'https://www.coursera.org', search: '/courses?query=' },
|
|
{ key: 'e', name: 'Egghead', url: 'https://egghead.io', search: '/search?q=' },
|
|
{ key: 'K', name: 'Khan Academy', url: 'https://www.khanacademy.org', search: '/search?page_search_query=' },
|
|
{ key: 'w', name: 'Wikipedia', url: 'https://en.wikipedia.org', search: '/wiki/' },
|
|
] },
|
|
{ name: "Media", commands: [
|
|
{ key: 'h', name: 'Hypem', url: 'http://hypem.com', search: '/search/' },
|
|
{ key: 's', name: 'SoundCloud', url: 'https://soundcloud.com', search: '/search?q=' },
|
|
{ key: 'y', name: 'YouTube', url: 'https://www.youtube.com', search: '/results?search_query=' },
|
|
{ key: 'Y', name: 'YTS', url: 'https://yts.ag', search: '/browse-movies/' },
|
|
] },
|
|
{ name: "News", commands: [
|
|
{ key: 'H', name: 'Hacker News', url: 'https://hn.algolia.com', search: '/?query=' },
|
|
{ key: 'p', name: 'Product Hunt', url: 'https://www.producthunt.com', search: '/search?q=' },
|
|
{ key: 'r', name: 'Reddit', url: 'https://www.reddit.com', search: '/search?q=' },
|
|
{ key: 'T', name: 'Codrops', url: 'http://tympanus.net/codrops', search: '/?search-type=posts&s=' },
|
|
] },
|
|
{ name: "Social", commands: [
|
|
{ key: 'i', name: 'Inbox', url: 'https://inbox.google.com', search: '/search/' },
|
|
{ key: 'f', name: 'Facebook', url: 'https://www.facebook.com', search: '/search/top/?q=' },
|
|
{ key: 'I', name: 'Instagram', url: 'https://www.instagram.com' },
|
|
{ key: 't', name: 'Twitter', url: 'https://twitter.com', search: '/search?q=' },
|
|
] },
|
|
{ name: "Tools", commands: [
|
|
{ key: 'd', name: 'Drive', url: 'https://drive.google.com/drive', search: '/search?q=' },
|
|
{ key: 'g', name: 'GitHub', url: 'https://github.com', search: '/search?q=' },
|
|
{ key: 'k', name: 'Keep', url: 'https://keep.google.com', search: '/#search/text=' },
|
|
] },
|
|
],
|
|
|
|
// if none of the keys are matched, this is used for searching.
|
|
defaultSearch: 'https://encrypted.google.com/search?q=',
|
|
|
|
// the delimiter between the key and your search query.
|
|
// e.g. to search GitHub for potatoes you'd type "g:potatoes".
|
|
searchDelimiter: ':',
|
|
|
|
// the delimiter between the key and a path.
|
|
// e.g. type "r/r/unixporn" to go to "reddit.com/r/unixporn".
|
|
pathDelimiter: '/',
|
|
|
|
// set to true to instantly redirect when a key is matched.
|
|
// put a space before any search queries to prevent unwanted redirects.
|
|
instantRedirect: false,
|
|
|
|
// suggest your most popular queries as you type.
|
|
suggestions: true,
|
|
|
|
// max amount of suggestions to display.
|
|
suggestionsLimit: 4,
|
|
|
|
// open queries in a new tab.
|
|
newTab: true,
|
|
|
|
// the delimiter between the hours and minutes in the clock.
|
|
clockDelimiter: ' ',
|
|
|
|
// used for determining when to redirect directly to a url.
|
|
urlRegex: /^(?:(http|https)?:\/\/)?(?:[\w-]+\.)+([a-z]|[A-Z]|[0-9]){2,6}/i,
|
|
|
|
// if "urlRegex" matches but this doesn't, "http://" will be
|
|
// prepended to the beginning of the query before redirecting.
|
|
protocolRegex: /^[a-zA-Z]+:\/\//i
|
|
};
|
|
</script>
|
|
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="robots" content="noindex">
|
|
|
|
<title>~</title>
|
|
|
|
<style type="text/css">
|
|
@font-face {
|
|
font-family: 'Lato';
|
|
src: url('https://fonts.gstatic.com/s/lato/v11/22JRxvfANxSmnAhzbFH8PgLUuEpTyoUstqEm5AMlJo4.woff2') format('woff2');
|
|
}
|
|
|
|
body {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
margin: 0;
|
|
padding: 0;
|
|
color: #222;
|
|
font-family: 'Lato', sans-serif;
|
|
}
|
|
|
|
main {
|
|
position: absolute;
|
|
top: 50%;
|
|
right: 0;
|
|
left: 0;
|
|
width: 90%;
|
|
max-width: 310px;
|
|
margin: 0 auto;
|
|
transform: translateY(-100px);
|
|
text-align: center;
|
|
}
|
|
|
|
time {
|
|
display: block;
|
|
margin-bottom: 20px;
|
|
font-size: 5rem;
|
|
letter-spacing: 6px;
|
|
}
|
|
|
|
input,
|
|
input:focus {
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
margin: 0;
|
|
border: 0;
|
|
background: #222;
|
|
outline: 0;
|
|
-webkit-appearance: none;
|
|
-moz-appearance: none;
|
|
color: #fff;
|
|
font-family: 'Lato', sans-serif;
|
|
}
|
|
|
|
ul,
|
|
li {
|
|
margin: 0;
|
|
padding: 0;
|
|
list-style: none;
|
|
}
|
|
|
|
.search-input {
|
|
padding: 12px;
|
|
border-radius: 2px;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
body[search-suggestions='true'] .search-input {
|
|
border-radius: 2px 2px 0 0;
|
|
}
|
|
|
|
.search-suggestions {
|
|
border-radius: 0 0 2px 2px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.search-suggestion {
|
|
padding: 14px 12px;
|
|
transition: 0.5s;
|
|
font-size: .8rem;
|
|
cursor: pointer;
|
|
text-align: left;
|
|
}
|
|
|
|
.search-suggestion:hover,
|
|
.search-suggestion:focus {
|
|
background: #30a388;
|
|
}
|
|
|
|
.overlay {
|
|
position: fixed;
|
|
box-sizing: border-box;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 0 10px;
|
|
transition: 200ms;
|
|
background: #222;
|
|
visibility: hidden;
|
|
opacity: 0;
|
|
overflow: auto;
|
|
}
|
|
|
|
.overlay[data-toggled='true'] {
|
|
visibility: visible;
|
|
opacity: 1;
|
|
}
|
|
|
|
.lists {
|
|
display: flex;
|
|
justify-content: center;
|
|
flex-wrap: wrap;
|
|
padding-bottom: 20px;
|
|
overflow: auto;
|
|
}
|
|
|
|
.category {
|
|
width: 160px;
|
|
margin: 20px 10px 0;
|
|
}
|
|
|
|
.category-name {
|
|
margin: 0;
|
|
padding: 14px;
|
|
border-radius: 2px 2px 0 0;
|
|
background: #30a388;
|
|
color: #165b4b;
|
|
font-size: .7rem;
|
|
text-transform: uppercase;
|
|
text-align: center;
|
|
}
|
|
|
|
.command {
|
|
transition: background 200ms;
|
|
background: #333;
|
|
}
|
|
|
|
.command:hover {
|
|
background: #313131;
|
|
}
|
|
|
|
.command:nth-child(even) {
|
|
background: #3a3a3a;
|
|
}
|
|
|
|
.command:nth-child(even):hover {
|
|
background: #383838;
|
|
}
|
|
|
|
.command:last-of-type {
|
|
border-radius: 0 0 2px 2px;
|
|
}
|
|
|
|
.command a {
|
|
display: block;
|
|
padding: 14px 12px;
|
|
color: #eee;
|
|
font-size: .8rem;
|
|
line-height: 1.3rem;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.command-key {
|
|
display: block;
|
|
width: 32px;
|
|
margin-right: 10px;
|
|
float: left;
|
|
border-radius: 2px;
|
|
background: #292929;
|
|
font-family: 'Courier New', monospace;
|
|
text-align: center;
|
|
}
|
|
|
|
@media (min-width: 740px) {
|
|
.overlay {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<main>
|
|
<time id="js-clock"></time>
|
|
<form id="js-search-form" autocomplete="off">
|
|
<input id="js-search-input" class="search-input" type="text" autofocus>
|
|
<ul id="js-search-suggestions" class="search-suggestions"></ul>
|
|
</form>
|
|
</main>
|
|
|
|
<aside id="js-overlay" class="overlay">
|
|
<ul id="js-lists" class="lists"></ul>
|
|
</aside>
|
|
|
|
<script>
|
|
function $(s) {
|
|
return document.querySelector(s);
|
|
};
|
|
|
|
var Clock = (function() {
|
|
var clock = $('#js-clock');
|
|
|
|
var pad = function(num) {
|
|
return ('0' + num.toString()).slice(-2);
|
|
}
|
|
|
|
var setTime = function() {
|
|
var date = new Date();
|
|
var hours = pad(date.getHours());
|
|
var minutes = pad(date.getMinutes());
|
|
|
|
clock.innerHTML = hours + config.clockDelimiter + minutes;
|
|
}
|
|
|
|
setTime();
|
|
setInterval(setTime, 1000);
|
|
})();
|
|
|
|
var Help = (function() {
|
|
var overlay = $('#js-overlay');
|
|
var lists = $('#js-lists');
|
|
|
|
config.categories.forEach(function(category) {
|
|
var commandItems = '';
|
|
|
|
category.commands.forEach(function(command) {
|
|
commandItems += (
|
|
'<li class="command">' +
|
|
'<a href="' + command.url + '">' +
|
|
'<span class="command-key">' + command.key + '</span>' +
|
|
'<span class="command-name">' + command.name + '</span>' +
|
|
'</a>' +
|
|
'</li>'
|
|
);
|
|
});
|
|
|
|
lists.insertAdjacentHTML(
|
|
'beforeend',
|
|
'<li class="category">' +
|
|
'<h2 class="category-name">' + category.name + '</h2>' +
|
|
'<ul>' + commandItems + '</ul>' +
|
|
'</li>'
|
|
);
|
|
});
|
|
|
|
return {
|
|
toggle: function(show) {
|
|
var toggle = typeof show !== 'undefined' ? show :
|
|
overlay.getAttribute('data-toggled') !== 'true';
|
|
|
|
overlay.setAttribute('data-toggled', toggle);
|
|
}
|
|
};
|
|
})();
|
|
|
|
var Suggestions = (function() {
|
|
var searchSuggestions = $('#js-search-suggestions');
|
|
var queries = JSON.parse(localStorage.getItem('queries')) || [];
|
|
|
|
return {
|
|
add: function(q) {
|
|
if (q.length < 2) return;
|
|
|
|
var exists = false;
|
|
|
|
queries.forEach(function(query) {
|
|
if (query[0] === q) {
|
|
query[1]++;
|
|
exists = true;
|
|
}
|
|
});
|
|
|
|
if (!exists) queries.push([q, 1]);
|
|
|
|
queries = queries.sort(function(current, next) {
|
|
return current[1] > next[1];
|
|
}).reverse();
|
|
|
|
localStorage.setItem('queries', JSON.stringify(queries));
|
|
},
|
|
|
|
show: function(input) {
|
|
searchSuggestions.innerHTML = '';
|
|
document.body.setAttribute('search-suggestions', false);
|
|
|
|
if (!config.suggestions || !input) return false;
|
|
|
|
queries
|
|
.filter(function(query) {
|
|
var matchesQuery = query[0].indexOf(input) !== -1;
|
|
return input && matchesQuery && input !== query[0];
|
|
})
|
|
.slice(0, config.suggestionsLimit).forEach(function(query) {
|
|
searchSuggestions.insertAdjacentHTML(
|
|
'beforeend',
|
|
'<li>' +
|
|
'<input ' +
|
|
'class="search-suggestion"' +
|
|
'type="button" ' +
|
|
'onclick="Form.submitWithThis.call(this)"' +
|
|
'value="' + query[0] + '"' +
|
|
'>' +
|
|
'</li>'
|
|
);
|
|
|
|
document.body.setAttribute('search-suggestions', true)
|
|
});
|
|
}
|
|
};
|
|
})();
|
|
|
|
var Form = (function() {
|
|
var searchForm = $('#js-search-form');
|
|
var searchInput = $('#js-search-input');
|
|
|
|
var execute = function(query, redirect) {
|
|
Suggestions.add(query);
|
|
Suggestions.show('');
|
|
searchInput.value = '';
|
|
|
|
if (config.newTab) window.open(redirect, '_blank');
|
|
else window.location.href = redirect;
|
|
}
|
|
|
|
var keyPress = function(event) {
|
|
var char = String.fromCharCode(event.which);
|
|
|
|
if (char.length && event.which !== 13) {
|
|
Help.toggle(false);
|
|
searchInput.focus();
|
|
}
|
|
|
|
if (config.instantRedirect) {
|
|
config.categories.forEach(function(category) {
|
|
category.commands.forEach(function(command) {
|
|
var query = searchInput.value + char;
|
|
|
|
if (command.key === query) {
|
|
event.preventDefault();
|
|
execute(query, command.url);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
var submit = function(event) {
|
|
if (event) event.preventDefault();
|
|
|
|
var q = searchInput.value.trim();
|
|
|
|
if (!q) {
|
|
Help.toggle();
|
|
return false;
|
|
}
|
|
|
|
var qSplitSearch = q.split(config.searchDelimiter);
|
|
var qSplitPath = q.split(config.pathDelimiter);
|
|
var qIsUrl = q.match(config.urlRegex);
|
|
var qHasProtocol = q.match(config.protocolRegex);
|
|
var redirect = '';
|
|
var breakLoop = false;
|
|
|
|
if (qIsUrl) redirect = qHasProtocol ? q : 'http://' + q;
|
|
else redirect = config.defaultSearch + encodeURIComponent(q);
|
|
|
|
config.categories.forEach(function(category) {
|
|
category.commands.forEach(function(command) {
|
|
var isSearch = qSplitSearch[0] === command.key;
|
|
var isPath = qSplitPath[0] === command.key;
|
|
|
|
if (isSearch || isPath) {
|
|
if (qSplitSearch[1] && command.search) {
|
|
qSplitSearch.shift();
|
|
|
|
var search = encodeURIComponent(
|
|
qSplitSearch.join(config.searchDelimiter).trim()
|
|
);
|
|
|
|
redirect = command.url + command.search + search;
|
|
} else if (qSplitPath[1]) {
|
|
qSplitPath.shift();
|
|
var path = qSplitPath.join(config.pathDelimiter).trim();
|
|
redirect = command.url + '/' + path;
|
|
} else {
|
|
redirect = command.url;
|
|
}
|
|
|
|
breakLoop = true;
|
|
return;
|
|
}
|
|
});
|
|
|
|
if (breakLoop) return;
|
|
});
|
|
|
|
execute(q, redirect);
|
|
}
|
|
|
|
var keyUp = function(event) {
|
|
Suggestions.show(searchInput.value.trim());
|
|
}
|
|
|
|
document.addEventListener('keypress', keyPress);
|
|
searchForm.addEventListener('submit', submit, false);
|
|
searchInput.addEventListener('keyup', keyUp);
|
|
|
|
return {
|
|
submitWithThis: function() {
|
|
searchInput.value = this.value;
|
|
submit();
|
|
}
|
|
};
|
|
})();
|
|
</script>
|