tilde/index.html

1312 lines
32 KiB
HTML
Raw Normal View History

2018-12-16 11:50:11 +00:00
<!DOCTYPE html>
2016-02-01 02:28:14 +00:00
<script>
2017-03-03 07:37:14 +00:00
const CONFIG = {
2019-01-14 20:42:03 +00:00
/**
* The category, name, key, url, search path and color for your commands.
* If none of the specified keys are matched, the '*' key is used.
* Commands without a category don't show up in the help menu.
*/
2017-07-11 06:04:57 +00:00
commands: [
2018-12-16 11:50:11 +00:00
{
name: 'Google',
key: '*',
url: 'https://encrypted.google.com',
search: '/search?q={}',
2019-04-14 17:11:02 +00:00
color: '#000',
2018-12-16 11:50:11 +00:00
},
{
category: 'Work',
name: 'Drive',
key: 'd',
url: 'https://drive.google.com',
search: '/drive/search?q={}',
color: 'linear-gradient(135deg, #4285f4, #4259f4)',
},
{
category: 'Work',
name: 'GitHub',
key: 'g',
url: 'https://github.com',
search: '/search?q={}',
2019-04-16 03:57:35 +00:00
color: 'linear-gradient(135deg, #9cdaf1, #7dbbe6)',
2018-12-16 11:50:11 +00:00
},
{
category: 'Work',
name: 'Keep',
key: 'k',
url: 'https://keep.google.com',
search: '/#search/text={}',
color: 'linear-gradient(135deg, #fca550, #fcd050)',
},
{
category: 'Lurk',
name: 'Hunt',
key: 'p',
url: 'https://www.producthunt.com',
search: '/search?q={}',
color: 'linear-gradient(135deg, #da552f, #da802f)',
},
{
category: 'Lurk',
name: 'Reddit',
key: 'r',
url: 'https://www.reddit.com',
search: '/search?q={}',
color: 'linear-gradient(135deg, #5f99cf, #5f7dcf)',
},
{
category: 'Lurk',
name: 'Unsplash',
key: 'u',
url: 'https://unsplash.com/new',
search: '/search/{}',
color: '#000',
},
{
category: 'Listen',
name: 'Hypem',
key: 'h',
url: 'https://hypem.com/popular',
search: '/search/{}',
color: 'linear-gradient(135deg, #83c441, #62c441)',
},
{
category: 'Listen',
name: 'Line Radio',
key: 'l',
url: 'https://linerad.io',
search: '/#{}',
color: 'linear-gradient(135deg, #a29bfe, #bb9bfe)',
},
{
category: 'Listen',
name: 'SoundCloud',
key: 's',
url: 'https://soundcloud.com/discover',
search: '/search?q={}',
color: 'linear-gradient(135deg, #ff8800, #ffc800)',
},
{
category: 'Watch',
name: 'Netflix',
key: 'n',
url: 'https://www.netflix.com/browse',
search: '/search?q={}',
color: 'linear-gradient(135deg, #e50914, #e53509)',
},
{
category: 'Watch',
name: 'Twitch',
key: 't',
url: 'https://www.twitch.tv/directory/following',
color: 'linear-gradient(135deg, #6441a5, #7d41a5)',
},
{
category: 'Watch',
name: 'YouTube',
key: 'y',
url: 'https://youtube.com/feed/subscriptions',
search: '/results?search_query={}',
color: 'linear-gradient(135deg, #cd201f, #cd4c1f)',
},
{
category: 'Learn',
name: 'Academy',
key: 'a',
url: 'https://www.khanacademy.org',
search: '/search?page_search_query={}',
color: 'linear-gradient(135deg, #9cb443, #80b443)',
},
{
category: 'Learn',
name: 'Coursera',
key: 'c',
url: 'https://www.coursera.org',
search: '/courses?query={}',
color: 'linear-gradient(135deg, #407ED7, #4058d7)',
},
{
category: 'Learn',
name: 'Egghead',
key: 'e',
url: 'https://egghead.io',
search: '/search?q={}',
color: 'linear-gradient(135deg, #171C23, #171923)',
},
{
category: 'Download',
name: '7digital',
key: '7',
url: 'https://us.7digital.com',
search: '/search?q={}',
color: 'linear-gradient(135deg, #07606e, #07466e)',
},
{
category: 'Download',
name: 'Bay',
key: 'b',
url: 'https://thepiratebay.org',
search: '/search/{}',
color: 'linear-gradient(135deg, #d2b9a6, #d2c4a6)',
},
{
category: 'Download',
name: 'YTS',
key: 'Y',
url: 'https://yts.am/browse-movies/all/1080p/all/7/latest',
search: '/browse-movies/{}',
color: 'linear-gradient(135deg, #2f2f2f, #373737)',
},
],
2019-01-14 20:42:03 +00:00
/**
* Get suggestions as you type.
*/
2017-06-28 05:13:49 +00:00
suggestions: true,
suggestionsLimit: 4,
2019-01-14 20:42:03 +00:00
/**
* The order and limit for each suggestion influencer. An "influencer" is
* just a suggestion source. The following influencers are available:
*
* - "Default" suggestions come from CONFIG.defaultSuggestions
* - "DuckDuckGo" suggestions come from the duck duck go search api
* - "History" suggestions come from your previously entered queries
*/
2017-06-28 05:13:49 +00:00
influencers: [
{ name: 'Default', limit: 4 },
{ name: 'History', limit: 1 },
{ name: 'DuckDuckGo', limit: 4 },
2017-06-28 05:13:49 +00:00
],
2019-01-14 20:42:03 +00:00
/**
* Default search suggestions for the specified queries.
*/
2017-07-17 04:24:42 +00:00
defaultSuggestions: {
2018-12-16 11:50:11 +00:00
g: ['g/issues', 'g/pulls', 'gist.github.com'],
l: ['l/#electronic+chill', 'l/#focus+instrumental', 'l/#piano+sleep'],
r: ['r/r/unixporn', 'r/r/startpages', 'r/r/webdev', 'r/r/technology'],
s: ['s/you/likes', 's/discover/the-upload'],
2017-07-17 04:24:42 +00:00
},
2019-01-14 20:42:03 +00:00
/**
* Instantly redirect when a key is matched. Put a space before any other
* queries to prevent unwanted redirects.
*/
2017-07-17 04:24:42 +00:00
instantRedirect: false,
2019-01-14 20:42:03 +00:00
/**
* Open triggered queries in a new tab.
*/
2017-06-07 01:07:20 +00:00
newTab: true,
2019-01-14 20:42:03 +00:00
/**
* Dynamic overlay background colors when command domains are matched.
*/
2018-12-15 19:06:43 +00:00
colors: true,
2018-03-18 04:47:53 +00:00
2019-01-14 20:42:03 +00:00
/**
* The delimiter between a command key and your search query. For example,
* to search GitHub for potatoes, you'd type "g:potatoes".
*/
2017-07-17 04:24:42 +00:00
searchDelimiter: ':',
2019-01-14 20:42:03 +00:00
/**
* The delimiter between a command key and a path. For example, you'd type
* "r/r/unixporn" to go to "https://reddit.com/r/unixporn".
*/
2017-07-17 04:24:42 +00:00
pathDelimiter: '/',
2019-01-14 20:42:03 +00:00
/**
* The delimiter between the hours and minutes on the clock.
*/
2018-10-09 19:15:49 +00:00
clockDelimiter: ' ',
2017-07-23 22:42:24 +00:00
2019-01-14 20:42:03 +00:00
/**
* Show a twenty-four-hour clock instead of a twelve-hour clock with AM/PM.
*/
twentyFourHourClock: true,
/**
* Action to take when the clock is clicked. Options include:
*
* - "Menu" to show the help menu
* - "Search" to show the search input
*/
clockAction: 'Menu',
};
</script>
2016-02-01 08:41:16 +00:00
<style type="text/css">
2018-03-18 04:47:53 +00:00
:root {
2019-01-14 20:42:03 +00:00
/* colors */
2019-04-14 17:11:02 +00:00
--background: #fff;
--foreground: #232931;
--searchBackground: #000;
2019-04-14 17:11:02 +00:00
--searchForeground: #fff;
2019-01-14 20:42:03 +00:00
/* fonts */
--font-family: Lato, sans-serif;
--base-font-size: 18px;
--font-weight-normal: 400;
--font-weight-bold: 700;
}
/* to download a different font locally:
https://google-webfonts-helper.herokuapp.com/fonts */
@font-face {
font-family: Lato;
font-style: normal;
font-weight: 400;
2019-02-28 01:15:52 +00:00
font-display: swap;
2019-01-14 20:42:03 +00:00
src: local('Lato Regular'), local('Lato-Regular'),
url('./fonts/lato-v14-latin-regular.woff2') format('woff2'),
url('./fonts/lato-v14-latin-regular.woff') format('woff');
2018-12-16 11:50:11 +00:00
}
2019-01-14 20:42:03 +00:00
@font-face {
font-family: Lato;
font-style: normal;
font-weight: 900;
2019-02-28 01:15:52 +00:00
font-display: swap;
2019-01-14 20:42:03 +00:00
src: local('Lato Black'), local('Lato-Black'),
url('./fonts/lato-v14-latin-900.woff2') format('woff2'),
url('./fonts/lato-v14-latin-900.woff') format('woff');
}
</style>
<style type="text/css">
2018-12-16 11:50:11 +00:00
* {
box-sizing: border-box;
2018-03-18 04:47:53 +00:00
}
2019-01-14 20:42:03 +00:00
html {
font-family: var(--font-family);
font-size: var(--base-font-size);
font-weight: var(--font-weight-normal);
}
2016-02-01 08:41:16 +00:00
body {
position: absolute;
2016-06-01 03:14:45 +00:00
top: 0;
left: 0;
2016-02-01 08:41:16 +00:00
width: 100%;
height: 100%;
margin: 0;
padding: 0;
2018-12-16 11:50:11 +00:00
transition: background 0.2s;
2019-01-14 20:42:03 +00:00
background: var(--background);
color: var(--foreground);
2016-06-01 03:14:45 +00:00
}
2016-06-14 02:14:50 +00:00
input,
2019-01-14 20:42:03 +00:00
button {
2017-07-03 02:29:55 +00:00
display: block;
2016-10-14 05:24:42 +00:00
width: 100%;
2017-07-17 01:15:20 +00:00
margin: 0;
2019-01-14 20:42:03 +00:00
padding: 0;
2018-12-16 11:50:11 +00:00
background: transparent;
2018-07-01 01:09:33 +00:00
color: inherit;
2019-01-14 20:42:03 +00:00
font-family: var(--font-family);
font-size: 1rem;
}
input,
button,
input:focus,
button:focus {
border: 0;
outline: 0;
2017-03-04 02:45:21 +00:00
-webkit-appearance: none;
-moz-appearance: none;
2016-06-01 03:14:45 +00:00
}
ul,
li {
2016-10-02 18:21:06 +00:00
margin: 0;
padding: 0;
list-style: none;
}
2017-07-17 03:39:57 +00:00
a,
a:focus {
2017-07-17 01:15:20 +00:00
color: inherit;
2017-07-17 03:39:57 +00:00
outline: 0;
2017-07-17 01:15:20 +00:00
}
2018-12-16 11:50:11 +00:00
.center {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.overlay {
position: fixed;
top: 0;
left: 0;
overflow: auto;
width: 100%;
height: 100%;
visibility: hidden;
}
.clock {
2017-07-17 01:15:20 +00:00
display: block;
2018-12-16 11:50:11 +00:00
margin-top: -0.06rem;
2019-01-14 20:42:03 +00:00
font-size: 4rem;
font-weight: var(--font-weight-normal);
2018-04-17 22:47:56 +00:00
text-align: center;
2018-12-16 11:50:11 +00:00
letter-spacing: 0.05rem;
cursor: pointer;
2017-07-17 01:15:20 +00:00
}
2018-09-14 05:41:11 +00:00
2018-12-16 11:50:11 +00:00
.am-pm {
2019-01-14 20:50:12 +00:00
font-size: 1rem;
letter-spacing: 0.1rem;
2018-04-17 22:47:56 +00:00
}
2017-07-17 01:15:20 +00:00
2018-12-16 11:50:11 +00:00
.search-form {
background: var(--searchBackground);
2019-04-14 17:11:02 +00:00
color: var(--searchForeground);
2017-07-17 01:15:20 +00:00
z-index: 2;
2016-10-14 05:24:42 +00:00
}
2018-12-16 11:50:11 +00:00
.search-form > div {
2017-07-19 07:04:54 +00:00
width: 100%;
}
2019-01-14 20:42:03 +00:00
.search-input {
2017-07-19 07:04:54 +00:00
width: 100%;
2019-01-14 20:42:03 +00:00
padding: 0 1rem;
2017-07-17 01:15:20 +00:00
margin-bottom: 20px;
2018-12-16 11:50:11 +00:00
font-size: 1.5rem;
2019-01-14 20:42:03 +00:00
font-weight: var(--font-weight-bold);
2019-04-16 03:57:35 +00:00
letter-spacing: 0.125rem;
2017-07-17 01:15:20 +00:00
text-transform: uppercase;
2016-10-14 05:24:42 +00:00
}
2018-12-16 11:50:11 +00:00
.search-suggestions {
2017-06-29 08:01:06 +00:00
display: none;
2017-07-17 01:15:20 +00:00
justify-content: center;
2019-01-14 20:42:03 +00:00
flex-direction: column;
flex-wrap: wrap;
overflow: hidden;
2016-10-14 05:24:42 +00:00
}
2018-12-16 11:50:11 +00:00
body.suggestions .search-suggestions {
2017-07-17 01:15:20 +00:00
display: flex;
2017-06-29 08:01:06 +00:00
}
2016-10-14 05:24:42 +00:00
.search-suggestion {
2019-04-16 03:57:35 +00:00
padding: 0.5rem 1rem;
2019-01-14 20:42:03 +00:00
text-align: left;
2017-07-17 01:15:20 +00:00
white-space: nowrap;
2017-03-04 02:45:21 +00:00
cursor: pointer;
2016-10-14 05:24:42 +00:00
}
2017-07-02 06:52:47 +00:00
.search-suggestion.highlight {
2019-04-14 17:11:02 +00:00
background: var(--searchForeground);
2019-04-16 03:57:35 +00:00
color: transparent;
2016-10-14 05:24:42 +00:00
}
2017-07-17 01:15:20 +00:00
.search-suggestion b {
2017-07-19 07:04:54 +00:00
position: relative;
2019-01-14 20:42:03 +00:00
font-weight: var(--font-weight-normal);
2017-07-19 07:04:54 +00:00
}
.search-suggestion b::after {
content: ' ';
position: absolute;
right: 0;
2019-01-14 20:42:03 +00:00
bottom: -0.3rem;
2017-07-19 07:04:54 +00:00
left: 0;
2019-01-14 20:42:03 +00:00
height: 2px;
2019-04-14 17:11:02 +00:00
background: var(--searchForeground);
2018-12-16 11:50:11 +00:00
opacity: 0.4;
2017-07-19 07:04:54 +00:00
}
.search-suggestion.highlight b::after {
opacity: 0;
2016-02-01 08:41:16 +00:00
}
2018-12-16 11:50:11 +00:00
.help {
2017-07-19 07:04:54 +00:00
display: block;
2017-07-17 01:15:20 +00:00
padding: 8vw;
2019-01-14 20:42:03 +00:00
background: var(--background);
2017-07-17 01:15:20 +00:00
z-index: 1;
2016-02-01 08:41:16 +00:00
}
.category {
2018-12-16 11:50:11 +00:00
margin-bottom: 3rem;
2017-07-17 01:15:20 +00:00
}
.category-name {
2018-12-16 11:50:11 +00:00
margin-bottom: 1.5rem;
2019-01-14 20:42:03 +00:00
font-size: 0.75rem;
letter-spacing: 0.15rem;
text-transform: uppercase;
}
.command a {
2018-12-16 11:50:11 +00:00
display: flex;
2017-03-04 02:45:21 +00:00
position: relative;
2018-12-16 11:50:11 +00:00
margin: 1rem 0;
line-height: 2rem;
text-decoration: none;
}
2017-07-19 07:04:54 +00:00
.command:last-of-type a {
margin-bottom: 0;
}
.command-key {
display: block;
float: left;
2019-01-14 20:42:03 +00:00
width: 2rem;
height: 2rem;
2018-12-16 11:50:11 +00:00
margin-right: 1rem;
2017-03-04 02:45:21 +00:00
border-radius: 50%;
2019-01-14 20:42:03 +00:00
background: var(--foreground);
color: var(--background);
font-size: 0.75rem;
line-height: 2rem;
text-align: center;
2016-06-01 03:14:45 +00:00
}
2017-03-04 02:45:21 +00:00
.command-name {
position: relative;
}
.command-name::after {
content: ' ';
2018-12-16 11:50:11 +00:00
display: none;
2017-03-04 02:45:21 +00:00
position: absolute;
right: 0;
2018-12-16 11:50:11 +00:00
bottom: 2px;
2017-03-04 02:45:21 +00:00
left: 0;
height: 2px;
2018-12-16 11:50:11 +00:00
transition: 0.2s;
2019-01-14 20:42:03 +00:00
transform: translateX(-2rem);
background: var(--foreground);
2017-03-04 02:45:21 +00:00
opacity: 0;
}
2018-12-16 11:50:11 +00:00
body.help .command-name::after {
display: block;
}
2017-07-17 03:39:57 +00:00
.command a:hover .command-name::after,
.command a:focus .command-name::after {
2017-03-04 02:45:21 +00:00
transform: translateX(0);
opacity: 1;
}
2018-12-16 11:50:11 +00:00
body.help .help,
body.form .search-form {
2017-07-17 01:15:20 +00:00
visibility: visible;
}
2017-07-19 07:04:54 +00:00
@media (min-width: 500px) {
2019-01-14 20:42:03 +00:00
.clock {
font-size: 6rem;
}
.search-input {
text-align: center;
}
.search-suggestions {
align-items: center;
}
2017-07-19 07:04:54 +00:00
.categories {
display: grid;
2019-01-14 20:42:03 +00:00
grid-template-columns: 250px 175px;
2017-07-19 07:04:54 +00:00
justify-content: space-around;
}
.category:nth-last-child(2) {
margin-bottom: 0;
}
}
2017-03-04 02:45:21 +00:00
2017-07-19 07:04:54 +00:00
@media (min-width: 1000px) {
2018-12-16 11:50:11 +00:00
.help {
2017-03-04 02:45:21 +00:00
display: flex;
2017-07-19 07:04:54 +00:00
padding: 0;
2017-03-04 02:45:21 +00:00
}
2019-01-14 20:42:03 +00:00
.search-input {
font-size: 2.5rem;
}
.search-suggestions {
flex-direction: row;
}
2017-03-04 02:45:21 +00:00
.category {
2017-07-19 07:04:54 +00:00
margin: 2rem 0;
2017-07-17 08:14:32 +00:00
}
2017-07-19 07:04:54 +00:00
.categories {
2019-01-14 20:42:03 +00:00
grid-template-columns: repeat(2, 300px) 175px;
}
2017-07-19 07:04:54 +00:00
}
2017-07-17 01:15:20 +00:00
2019-01-14 20:42:03 +00:00
@media (min-width: 1500px) {
2017-07-19 07:04:54 +00:00
.categories {
2019-01-14 20:42:03 +00:00
grid-template-columns: repeat(5, 220px) 175px;
2017-07-17 01:15:20 +00:00
}
2016-02-01 18:20:01 +00:00
}
2016-02-01 08:41:16 +00:00
</style>
2018-12-16 11:50:11 +00:00
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
2018-12-16 11:53:52 +00:00
<meta name="robots" content="noindex" />
<title>~</title>
2018-12-16 11:50:11 +00:00
<div class="center"><time class="clock" id="clock"></time></div>
2018-12-16 11:53:52 +00:00
2018-12-16 11:50:11 +00:00
<form
autocomplete="off"
class="center overlay search-form"
id="search-form"
spellcheck="false"
>
2017-07-19 07:04:54 +00:00
<div>
2018-12-16 11:50:11 +00:00
<input class="search-input" id="search-input" title="search" type="text" />
<ul class="search-suggestions" id="search-suggestions"></ul>
2017-07-17 01:15:20 +00:00
</div>
</form>
2018-12-16 11:53:52 +00:00
2018-12-16 11:50:11 +00:00
<aside class="center help overlay" id="help"></aside>
2016-06-01 03:14:45 +00:00
<script>
2017-03-03 07:37:14 +00:00
const $ = {
2017-07-11 06:04:57 +00:00
bodyClassAdd: c => $.el('body').classList.add(c),
2017-07-19 07:04:54 +00:00
bodyClassRemove: c => $.el('body').classList.remove(c),
2017-03-04 04:03:53 +00:00
el: s => document.querySelector(s),
2017-04-06 01:44:17 +00:00
els: s => [].slice.call(document.querySelectorAll(s) || []),
2017-07-11 06:04:57 +00:00
escapeRegex: s => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'),
2018-12-16 11:50:11 +00:00
flattenAndUnique: arr => [...new Set([].concat.apply([], arr))],
2017-07-19 07:04:54 +00:00
ieq: (a, b) => a.toLowerCase() === b.toLowerCase(),
iin: (a, b) => a.toLowerCase().indexOf(b.toLowerCase()) !== -1,
2017-07-11 06:04:57 +00:00
isDown: e => ['c-n', 'down', 'tab'].includes($.key(e)),
isRemove: e => ['backspace', 'delete'].includes($.key(e)),
isUp: e => ['c-p', 'up', 's-tab'].includes($.key(e)),
2017-06-28 05:13:49 +00:00
jsonp: url => {
let script = document.createElement('script');
script.src = url;
$.el('head').appendChild(script);
},
2017-07-02 06:52:47 +00:00
key: e => {
const ctrl = e.ctrlKey;
2017-07-11 06:04:57 +00:00
const shift = e.shiftKey;
2017-07-02 06:52:47 +00:00
switch (e.which) {
2018-12-16 11:50:11 +00:00
case 8:
return 'backspace';
case 9:
return shift ? 's-tab' : 'tab';
case 13:
return 'enter';
case 16:
return 'shift';
case 17:
return 'ctrl';
case 18:
return 'alt';
case 27:
return 'escape';
case 38:
return 'up';
case 40:
return 'down';
case 46:
return 'delete';
case 78:
return ctrl ? 'c-n' : 'n';
case 80:
return ctrl ? 'c-p' : 'p';
case 91:
2018-12-16 22:18:09 +00:00
case 93:
case 224:
2018-12-16 11:50:11 +00:00
return 'super';
2017-07-02 06:52:47 +00:00
}
},
2018-10-09 19:15:49 +00:00
pad: v => ('0' + v.toString()).slice(-2),
2016-07-12 21:26:27 +00:00
};
2018-12-16 11:50:11 +00:00
</script>
2016-07-12 21:26:27 +00:00
2018-12-16 11:50:11 +00:00
<script>
2017-03-03 07:37:14 +00:00
class Clock {
constructor(options) {
2017-07-11 06:04:57 +00:00
this._el = $.el('#clock');
this._delimiter = options.delimiter;
this._twentyFourHour = options.twentyFourHour;
2017-03-03 07:37:14 +00:00
this._setTime = this._setTime.bind(this);
this._el.addEventListener('click', options.onClick);
2018-10-09 19:15:49 +00:00
this._start();
}
2017-03-03 07:37:14 +00:00
_setTime() {
const date = new Date();
2018-10-09 19:15:49 +00:00
let hours = $.pad(date.getHours());
2018-04-17 22:47:56 +00:00
let amPm = '';
2018-09-14 05:39:50 +00:00
if (!this._twentyFourHour) {
2018-12-16 11:50:11 +00:00
hours = date.getHours();
if (hours > 12) hours -= 12;
else if (hours === 0) hours = 12;
2018-09-14 05:39:50 +00:00
2018-12-16 11:50:11 +00:00
amPm =
`&nbsp;<span class="am-pm">` +
2018-09-14 05:39:50 +00:00
`${date.getHours() > 12 ? 'PM' : 'AM'}</span>`;
2018-04-17 22:47:56 +00:00
}
2018-09-14 05:39:50 +00:00
2018-10-09 19:15:49 +00:00
const minutes = $.pad(date.getMinutes());
2018-09-14 05:39:50 +00:00
this._el.innerHTML = `${hours}${this._delimiter}${minutes}${amPm}`;
2018-04-17 22:47:56 +00:00
this._el.setAttribute('datetime', date.toTimeString());
2017-03-03 07:37:14 +00:00
}
2017-03-03 07:37:14 +00:00
_start() {
this._setTime();
setInterval(this._setTime, 1000);
2016-06-01 03:14:45 +00:00
}
2017-03-03 07:37:14 +00:00
}
2018-12-16 11:50:11 +00:00
</script>
2016-06-01 03:14:45 +00:00
2018-12-16 11:50:11 +00:00
<script>
2017-03-03 07:37:14 +00:00
class Help {
2017-07-19 07:04:54 +00:00
constructor(options) {
2017-07-11 06:04:57 +00:00
this._el = $.el('#help');
2017-07-19 07:04:54 +00:00
this._commands = options.commands;
this._newTab = options.newTab;
2017-07-11 06:04:57 +00:00
this._toggled = false;
2018-10-09 19:15:49 +00:00
this._handleKeydown = this._handleKeydown.bind(this);
2018-12-16 11:50:11 +00:00
this.toggle = this.toggle.bind(this);
2017-03-03 07:37:14 +00:00
this._buildAndAppendLists();
2017-03-04 04:03:53 +00:00
this._registerEvents();
2017-03-03 07:37:14 +00:00
}
2016-06-01 03:14:45 +00:00
2017-03-03 07:37:14 +00:00
toggle(show) {
2018-12-16 11:50:11 +00:00
this._toggled = typeof show !== 'undefined' ? show : !this._toggled;
2017-07-11 06:04:57 +00:00
this._toggled ? $.bodyClassAdd('help') : $.bodyClassRemove('help');
}
2016-06-01 03:14:45 +00:00
2017-03-03 07:37:14 +00:00
_buildAndAppendLists() {
2017-07-11 06:04:57 +00:00
const lists = document.createElement('ul');
2017-07-19 07:04:54 +00:00
lists.classList.add('categories');
2017-07-11 06:04:57 +00:00
2017-07-19 07:04:54 +00:00
this._getCategories().forEach(category => {
2017-07-11 06:04:57 +00:00
lists.insertAdjacentHTML(
2017-03-03 07:37:14 +00:00
'beforeend',
`<li class="category">
2017-07-19 07:04:54 +00:00
<h2 class="category-name">${category}</h2>
<ul>${this._buildListCommands(category)}</ul>
2018-12-16 11:50:11 +00:00
</li>`
2016-10-01 19:35:45 +00:00
);
2017-07-19 07:04:54 +00:00
});
2017-07-11 06:04:57 +00:00
this._el.appendChild(lists);
}
2018-12-16 11:50:11 +00:00
_buildListCommands(currentCategory) {
2018-10-09 19:15:49 +00:00
return this._commands
2018-12-16 11:50:11 +00:00
.map(({ category, name, key, url }) => {
if (category === currentCategory) {
return `
<li class="command">
2018-10-09 19:15:49 +00:00
<a href="${url}" target="${this._newTab ? '_blank' : '_self'}">
<span class="command-key">${key}</span>
<span class="command-name">${name}</span>
</a>
2018-12-16 11:50:11 +00:00
</li>
`;
2018-10-09 19:15:49 +00:00
}
})
.join('');
2017-03-03 07:37:14 +00:00
}
2017-07-19 07:04:54 +00:00
_getCategories() {
const categories = this._commands
2018-12-16 11:50:11 +00:00
.map(v => v.category)
2017-07-19 07:04:54 +00:00
.filter(category => category);
return [...new Set(categories)];
2017-03-03 07:37:14 +00:00
}
2017-03-04 04:03:53 +00:00
2017-07-02 06:52:47 +00:00
_handleKeydown(e) {
if ($.key(e) === 'escape') this.toggle(false);
2017-03-04 04:03:53 +00:00
}
_registerEvents() {
document.addEventListener('keydown', this._handleKeydown);
}
2017-03-03 07:37:14 +00:00
}
2018-12-16 11:50:11 +00:00
</script>
2016-06-01 03:14:45 +00:00
2018-12-16 11:50:11 +00:00
<script>
2017-07-11 06:04:57 +00:00
class Influencer {
2017-07-19 07:04:54 +00:00
constructor(options) {
this._limit = options.limit;
2018-12-16 11:50:11 +00:00
this._parseQuery = options.parseQuery;
2017-07-11 06:04:57 +00:00
}
addItem() {}
getSuggestions() {}
2017-07-19 07:04:54 +00:00
_addSearchPrefix(items, query) {
2018-09-14 05:41:11 +00:00
const searchPrefix = this._getSearchPrefix(query);
2018-12-16 11:50:11 +00:00
return items.map(s => (searchPrefix ? searchPrefix + s : s));
2017-07-19 07:04:54 +00:00
}
_getSearchPrefix(query) {
const { isSearch, key, split } = this._parseQuery(query);
return isSearch ? `${key}${split} ` : false;
}
2017-07-11 06:04:57 +00:00
}
2018-12-16 11:50:11 +00:00
</script>
2017-07-11 06:04:57 +00:00
2018-12-16 11:50:11 +00:00
<script>
2017-07-11 06:30:49 +00:00
class DefaultInfluencer extends Influencer {
constructor({ defaultSuggestions }) {
super(...arguments);
this._defaultSuggestions = defaultSuggestions;
}
getSuggestions(query) {
return new Promise(resolve => {
const suggestions = this._defaultSuggestions[query];
resolve(suggestions ? suggestions.slice(0, this._limit) : []);
});
}
}
2018-12-16 11:50:11 +00:00
</script>
2017-07-11 06:30:49 +00:00
2018-12-16 11:50:11 +00:00
<script>
2017-07-11 06:30:49 +00:00
class DuckDuckGoInfluencer extends Influencer {
2017-07-19 07:04:54 +00:00
constructor({ queryParser }) {
2017-07-11 06:30:49 +00:00
super(...arguments);
}
2017-07-19 07:04:54 +00:00
getSuggestions(rawQuery) {
const { query } = this._parseQuery(rawQuery);
if (!query) return Promise.resolve([]);
2017-07-11 06:30:49 +00:00
return new Promise(resolve => {
const endpoint = 'https://duckduckgo.com/ac/';
const callback = 'autocompleteCallback';
window[callback] = res => {
2018-12-16 11:50:11 +00:00
const suggestions = res
.map(i => i.phrase)
2017-07-19 07:04:54 +00:00
.filter(s => !$.ieq(s, query))
2018-09-14 05:41:11 +00:00
.slice(0, this._limit);
2017-07-17 01:15:20 +00:00
2017-07-19 07:04:54 +00:00
resolve(this._addSearchPrefix(suggestions, rawQuery));
2017-07-11 06:30:49 +00:00
};
$.jsonp(`${endpoint}?callback=${callback}&q=${query}`);
});
}
}
2018-12-16 11:50:11 +00:00
</script>
2017-07-11 06:30:49 +00:00
2018-12-16 11:50:11 +00:00
<script>
2017-07-11 06:30:49 +00:00
class HistoryInfluencer extends Influencer {
2017-07-11 06:04:57 +00:00
constructor() {
super(...arguments);
this._storeName = 'history';
2017-03-03 07:37:14 +00:00
}
2017-03-03 07:37:14 +00:00
addItem(query) {
2017-03-29 01:53:13 +00:00
if (query.length < 2) return;
2017-07-11 06:04:57 +00:00
let exists;
const history = this._getHistory().map(([item, count]) => {
2017-07-19 07:04:54 +00:00
const match = $.ieq(item, query);
2017-07-11 06:04:57 +00:00
if (match) exists = true;
return [item, match ? count + 1 : count];
});
if (!exists) history.push([query, 1]);
2018-10-09 19:15:49 +00:00
const sorted = history
.sort((current, next) => current[1] - next[1])
.reverse();
this._setHistory(sorted);
2017-03-03 07:37:14 +00:00
}
2016-06-14 02:14:50 +00:00
2017-07-11 06:04:57 +00:00
getSuggestions(query) {
2017-03-03 07:37:14 +00:00
return new Promise(resolve => {
2017-07-11 06:04:57 +00:00
const suggestions = this._getHistory()
2017-07-17 01:15:20 +00:00
.map(([item]) => item)
2018-10-09 19:15:49 +00:00
.filter(item => query && !$.ieq(item, query) && $.iin(item, query))
2017-07-17 01:15:20 +00:00
.slice(0, this._limit);
2016-10-14 09:41:34 +00:00
2017-03-03 07:37:14 +00:00
resolve(suggestions);
});
}
2016-10-14 05:24:42 +00:00
2017-07-11 06:04:57 +00:00
_fetch() {
return JSON.parse(localStorage.getItem(this._storeName)) || [];
}
_getHistory() {
this._history = this._history || this._fetch();
return this._history;
2017-03-03 07:37:14 +00:00
}
2016-06-14 02:14:50 +00:00
2017-07-11 06:04:57 +00:00
_save(history) {
localStorage.setItem(this._storeName, JSON.stringify(history));
2016-10-14 06:04:42 +00:00
}
2017-07-11 06:04:57 +00:00
_setHistory(history) {
this._history = history;
this._save(history);
2017-03-03 07:37:14 +00:00
}
}
2018-12-16 11:50:11 +00:00
</script>
2017-03-03 07:37:14 +00:00
2018-12-16 11:50:11 +00:00
<script>
2017-03-03 07:37:14 +00:00
class Suggester {
2017-07-19 07:04:54 +00:00
constructor(options) {
2017-07-11 06:04:57 +00:00
this._el = $.el('#search-suggestions');
this._enabled = options.enabled;
2017-07-19 07:04:54 +00:00
this._influencers = options.influencers;
this._limit = options.limit;
2017-07-11 06:04:57 +00:00
this._suggestionEls = [];
2018-10-09 19:15:49 +00:00
this._handleKeydown = this._handleKeydown.bind(this);
2017-07-11 06:04:57 +00:00
this._registerEvents();
}
setOnClick(callback) {
this._onClick = callback;
2017-03-03 07:37:14 +00:00
}
2017-07-11 06:04:57 +00:00
setOnHighlight(callback) {
this._onHighlight = callback;
}
setOnUnhighlight(callback) {
this._onUnhighlight = callback;
}
success(query) {
this._clearSuggestions();
2017-06-28 05:13:49 +00:00
this._influencers.forEach(i => i.addItem(query));
}
2017-07-11 06:04:57 +00:00
suggest(input) {
if (!this._enabled) return;
2017-07-11 06:04:57 +00:00
input = input.trim();
if (input === '') this._clearSuggestions();
2018-07-01 04:42:59 +00:00
Promise.all(this._getInfluencerPromises(input)).then(res => {
2018-12-16 11:50:11 +00:00
const suggestions = $.flattenAndUnique(res);
2018-07-01 04:42:59 +00:00
this._clearSuggestions();
2018-10-09 19:15:49 +00:00
2018-07-01 04:42:59 +00:00
if (suggestions.length) {
this._appendSuggestions(suggestions, input);
2019-01-17 23:14:21 +00:00
this._registerSuggestionHighlightEvents();
this._registerSuggestionClickEvents();
2018-07-01 04:42:59 +00:00
$.bodyClassAdd('suggestions');
}
});
2017-03-03 07:37:14 +00:00
}
2017-07-11 06:04:57 +00:00
_appendSuggestions(suggestions, input) {
2018-12-16 11:50:11 +00:00
suggestions.some((suggestion, i) => {
2017-07-19 07:04:54 +00:00
const match = new RegExp($.escapeRegex(input), 'ig');
2017-07-11 06:04:57 +00:00
const suggestionHtml = suggestion.replace(match, `<b>${input}</b>`);
this._el.insertAdjacentHTML(
'beforeend',
`<li>
<button
type="button"
class="js-search-suggestion search-suggestion"
data-suggestion="${suggestion}"
tabindex="-1"
>
${suggestionHtml}
</button>
2018-12-16 11:50:11 +00:00
</li>`
2017-07-11 06:04:57 +00:00
);
2017-06-29 06:42:49 +00:00
2018-12-16 11:50:11 +00:00
if (i + 1 >= this._limit) return true;
});
2017-03-03 07:37:14 +00:00
2017-07-11 06:04:57 +00:00
this._suggestionEls = $.els('.js-search-suggestion');
}
2019-01-17 23:14:21 +00:00
_clearSuggestionClickEvents() {
2017-03-04 20:01:07 +00:00
this._suggestionEls.forEach(el => {
2019-01-17 23:14:21 +00:00
el.removeEventListener('click', this._onClick);
});
}
_clearSuggestionHighlightEvents() {
this._suggestionEls.forEach(el => {
el.removeEventListener('mouseover', this._highlight);
el.removeEventListener('mouseout', this._unHighlight);
2017-03-04 20:01:07 +00:00
});
2017-03-03 07:37:14 +00:00
}
2017-03-03 07:37:14 +00:00
_clearSuggestions() {
2017-07-11 06:04:57 +00:00
$.bodyClassRemove('suggestions');
2019-01-17 23:14:21 +00:00
this._clearSuggestionHighlightEvents();
this._clearSuggestionClickEvents();
2017-07-11 06:04:57 +00:00
this._suggestionEls = [];
this._el.innerHTML = '';
2017-03-03 07:37:14 +00:00
}
2017-07-11 06:04:57 +00:00
_focusNext(e) {
const exists = this._suggestionEls.some((el, i) => {
if (el.classList.contains('highlight')) {
this._highlight(this._suggestionEls[i + 1], e);
return true;
}
});
2017-07-02 06:52:47 +00:00
2017-07-11 06:04:57 +00:00
if (!exists) this._highlight(this._suggestionEls[0], e);
2017-03-03 07:37:14 +00:00
}
2017-07-11 06:04:57 +00:00
_focusPrevious(e) {
const exists = this._suggestionEls.some((el, i) => {
if (el.classList.contains('highlight') && i) {
this._highlight(this._suggestionEls[i - 1], e);
return true;
}
});
2017-07-02 06:52:47 +00:00
2017-07-11 06:04:57 +00:00
if (!exists) this._unHighlight(e);
2017-03-03 07:37:14 +00:00
}
2017-07-11 06:04:57 +00:00
_getInfluencerPromises(input) {
2018-12-16 11:50:11 +00:00
return this._influencers.map(influencer =>
influencer.getSuggestions(input)
);
2017-06-28 05:13:49 +00:00
}
2017-07-02 06:52:47 +00:00
_handleKeydown(e) {
2017-07-11 06:04:57 +00:00
if ($.isDown(e)) this._focusNext(e);
if ($.isUp(e)) this._focusPrevious(e);
2017-07-02 06:52:47 +00:00
}
2017-07-11 06:04:57 +00:00
_highlight(el, e) {
this._unHighlight();
2019-01-17 23:14:21 +00:00
if (!el) return;
this._onHighlight(el.getAttribute('data-suggestion'));
el.classList.add('highlight');
e.preventDefault();
2017-07-02 06:52:47 +00:00
}
2017-07-11 06:04:57 +00:00
_registerEvents() {
document.addEventListener('keydown', this._handleKeydown);
}
2019-01-17 23:14:21 +00:00
_registerSuggestionClickEvents() {
this._suggestionEls.forEach(el => {
const value = el.getAttribute('data-suggestion');
el.addEventListener('click', this._onClick.bind(null, value));
});
}
_registerSuggestionHighlightEvents() {
2018-10-09 19:15:49 +00:00
const noHighlightUntilMouseMove = () => {
window.removeEventListener('mousemove', noHighlightUntilMouseMove);
this._suggestionEls.forEach(el => {
el.addEventListener('mouseover', this._highlight.bind(this, el));
el.addEventListener('mouseout', this._unHighlight.bind(this));
});
};
window.addEventListener('mousemove', noHighlightUntilMouseMove);
}
2017-06-28 05:13:49 +00:00
2017-07-11 06:04:57 +00:00
_unHighlight(e) {
const el = $.el('.highlight');
2019-01-17 23:14:21 +00:00
if (!el) return;
this._onUnhighlight();
el.classList.remove('highlight');
if (e) e.preventDefault();
2017-06-28 05:13:49 +00:00
}
2017-03-03 07:37:14 +00:00
}
2018-12-16 11:50:11 +00:00
</script>
2016-10-01 19:35:45 +00:00
2018-12-16 11:50:11 +00:00
<script>
2017-03-03 07:37:14 +00:00
class QueryParser {
2017-07-19 07:04:54 +00:00
constructor(options) {
this._commands = options.commands;
this._searchDelimiter = options.searchDelimiter;
this._pathDelimiter = options.pathDelimiter;
2017-07-11 06:04:57 +00:00
this._protocolRegex = /^[a-zA-Z]+:\/\//i;
2017-07-19 07:04:54 +00:00
this._urlRegex = /^((https?:\/\/)?[\w-]+(\.[\w-]+)+\.?(:\d+)?(\/\S*)?)$/i;
2019-03-08 07:48:07 +00:00
this._cachedQuery = '';
2019-03-25 18:57:18 +00:00
this._cachedRes = {};
2018-12-16 11:50:11 +00:00
this.parse = this.parse.bind(this);
2017-07-11 06:04:57 +00:00
}
2017-07-17 03:39:57 +00:00
parse(query) {
2019-03-08 07:48:07 +00:00
if (this._cachedQuery === query) return this._cachedRes;
this._cachedQuery = query;
2017-07-19 07:04:54 +00:00
const res = { query: query, split: null };
2018-12-16 11:50:11 +00:00
if (this._urlRegex.test(query)) {
const hasProtocol = this._protocolRegex.test(query);
2017-07-17 03:39:57 +00:00
res.redirect = hasProtocol ? query : 'http://' + query;
2017-03-03 07:37:14 +00:00
} else {
2018-12-16 11:50:11 +00:00
const trimmed = query.trim();
const splitSearch = trimmed.split(this._searchDelimiter);
const splitPath = trimmed.split(this._pathDelimiter);
2018-12-16 11:50:11 +00:00
this._commands.some(({ category, key, name, search, url }) => {
2019-01-14 20:42:03 +00:00
if (query === key) {
2017-07-19 07:04:54 +00:00
res.key = key;
2019-01-14 20:42:03 +00:00
res.isKey = true;
res.redirect = url;
return true;
}
2016-10-13 23:15:21 +00:00
2019-01-14 20:42:03 +00:00
if (splitSearch[0] === key && search) {
res.key = key;
res.isSearch = true;
res.split = this._searchDelimiter;
res.query = QueryParser._shiftAndTrim(splitSearch, res.split);
res.redirect = QueryParser._prepSearch(url, search, res.query);
return true;
}
2016-10-13 23:15:21 +00:00
2019-01-14 20:42:03 +00:00
if (splitPath[0] === key) {
res.key = key;
res.isPath = true;
res.split = this._pathDelimiter;
res.path = QueryParser._shiftAndTrim(splitPath, res.split);
res.redirect = QueryParser._prepPath(url, res.path);
2017-03-03 07:37:14 +00:00
return true;
}
2017-07-17 01:15:20 +00:00
if (key === '*') {
2018-12-16 11:50:11 +00:00
res.redirect = QueryParser._prepSearch(url, search, query);
2017-07-17 01:15:20 +00:00
}
2016-09-25 19:17:46 +00:00
});
2017-03-03 07:37:14 +00:00
}
2018-10-09 19:15:49 +00:00
res.color = QueryParser._getColorFromUrl(this._commands, res.redirect);
2019-03-08 07:48:07 +00:00
this._cachedRes = res;
2017-07-17 03:39:57 +00:00
return res;
2017-03-03 07:37:14 +00:00
}
2018-10-09 19:15:49 +00:00
static _getColorFromUrl(commands, url) {
const domain = new URL(url).hostname;
2017-07-11 06:04:57 +00:00
2018-12-16 11:50:11 +00:00
return (
commands
2019-01-14 20:42:03 +00:00
.filter(c => new URL(c.url).hostname.includes(domain))
2018-12-16 11:50:11 +00:00
.map(c => c.color)[0] || null
);
2017-03-03 07:37:14 +00:00
}
2018-10-09 19:15:49 +00:00
static _prepPath(url, path) {
return QueryParser._stripUrlPath(url) + '/' + path;
2017-03-03 07:37:14 +00:00
}
2018-10-09 19:15:49 +00:00
static _prepSearch(url, searchPath, query) {
2017-07-11 06:04:57 +00:00
if (!searchPath) return url;
2018-10-09 19:15:49 +00:00
const baseUrl = QueryParser._stripUrlPath(url);
2017-07-11 06:04:57 +00:00
const urlQuery = encodeURIComponent(query);
searchPath = searchPath.replace('{}', urlQuery);
return baseUrl + searchPath;
2017-03-03 07:37:14 +00:00
}
2016-10-13 23:15:21 +00:00
2018-10-09 19:15:49 +00:00
static _shiftAndTrim(arr, delimiter) {
2017-03-03 07:37:14 +00:00
arr.shift();
return arr.join(delimiter).trim();
}
2018-10-09 19:15:49 +00:00
static _stripUrlPath(url) {
2017-03-04 03:35:28 +00:00
const parser = document.createElement('a');
parser.href = url;
return `${parser.protocol}//${parser.hostname}`;
}
2017-03-03 07:37:14 +00:00
}
2018-12-16 11:50:11 +00:00
</script>
2017-03-03 07:37:14 +00:00
2018-12-16 11:50:11 +00:00
<script>
2017-03-03 07:37:14 +00:00
class Form {
2017-07-19 07:04:54 +00:00
constructor(options) {
2018-12-16 11:50:11 +00:00
this._colors = options.colors;
2017-07-11 06:04:57 +00:00
this._formEl = $.el('#search-form');
this._inputEl = $.el('#search-input');
2018-12-16 11:50:11 +00:00
this._inputElVal = '';
2017-07-19 07:04:54 +00:00
this._instantRedirect = options.instantRedirect;
this._newTab = options.newTab;
2018-12-16 11:50:11 +00:00
this._parseQuery = options.parseQuery;
this._suggester = options.suggester;
this._toggleHelp = options.toggleHelp;
2018-12-15 20:45:02 +00:00
this._clearPreview = this._clearPreview.bind(this);
2018-10-09 19:15:49 +00:00
this._handleInput = this._handleInput.bind(this);
2018-12-16 21:38:56 +00:00
this._handleKeydown = this._handleKeydown.bind(this);
2018-10-09 19:15:49 +00:00
this._previewValue = this._previewValue.bind(this);
this._submitForm = this._submitForm.bind(this);
2018-12-15 20:45:02 +00:00
this._submitWithValue = this._submitWithValue.bind(this);
2018-12-16 11:50:11 +00:00
this.hide = this.hide.bind(this);
this.show = this.show.bind(this);
2017-03-29 01:53:13 +00:00
this._registerEvents();
2017-07-23 22:42:24 +00:00
this._loadQueryParam();
2017-03-29 01:53:13 +00:00
}
hide() {
$.bodyClassRemove('form');
this._inputEl.value = '';
this._inputElVal = '';
this._suggester.suggest('');
this._setBackgroundFromQuery('');
}
show() {
$.bodyClassAdd('form');
this._inputEl.focus();
}
2017-07-11 06:04:57 +00:00
_clearPreview() {
2017-08-08 05:01:31 +00:00
this._previewValue(this._inputElVal);
2017-07-11 06:04:57 +00:00
this._inputEl.focus();
}
2018-12-16 11:50:11 +00:00
_handleInput() {
const newQuery = this._inputEl.value;
const isHelp = newQuery === '?';
const { isKey } = this._parseQuery(newQuery);
this._inputElVal = newQuery;
this._suggester.suggest(newQuery);
2018-12-16 11:50:11 +00:00
this._setBackgroundFromQuery(newQuery);
if (!newQuery || isHelp) this.hide();
if (isHelp) this._toggleHelp();
if (this._instantRedirect && isKey) this._submitWithValue(newQuery);
}
2018-12-16 21:38:56 +00:00
_handleKeydown(e) {
2017-07-19 07:04:54 +00:00
if ($.isUp(e) || $.isDown(e) || $.isRemove(e)) return;
2017-07-17 01:15:20 +00:00
switch ($.key(e)) {
case 'alt':
case 'ctrl':
case 'enter':
case 'shift':
2018-09-14 05:41:11 +00:00
case 'super':
return;
case 'escape':
this.hide();
return;
2017-07-17 01:15:20 +00:00
}
2017-07-19 07:04:54 +00:00
this.show();
2017-07-17 01:15:20 +00:00
}
2017-07-23 22:42:24 +00:00
_loadQueryParam() {
const q = new URLSearchParams(window.location.search).get('q');
if (q) this._submitWithValue(q);
}
2017-07-11 06:04:57 +00:00
_previewValue(value) {
this._inputEl.value = value;
2017-07-19 07:04:54 +00:00
this._setBackgroundFromQuery(value);
2017-07-11 06:04:57 +00:00
}
2017-03-03 07:37:14 +00:00
_redirect(redirect) {
2017-07-11 06:04:57 +00:00
if (this._newTab) window.open(redirect, '_blank');
2017-03-03 07:37:14 +00:00
else window.location.href = redirect;
2016-10-14 05:24:42 +00:00
}
2017-03-03 07:37:14 +00:00
_registerEvents() {
2018-12-16 21:38:56 +00:00
document.addEventListener('keydown', this._handleKeydown);
2017-07-30 05:47:59 +00:00
this._inputEl.addEventListener('input', this._handleInput);
2017-03-03 07:37:14 +00:00
this._formEl.addEventListener('submit', this._submitForm, false);
2017-07-19 07:04:54 +00:00
if (this._suggester) {
this._suggester.setOnClick(this._submitWithValue);
this._suggester.setOnHighlight(this._previewValue);
this._suggester.setOnUnhighlight(this._clearPreview);
}
}
_setBackgroundFromQuery(query) {
2017-07-30 05:47:59 +00:00
if (!this._colors) return;
2019-02-28 01:23:25 +00:00
const color = this._parseQuery(query).color;
if (!color) return;
this._formEl.style.background = color;
2017-03-03 07:37:14 +00:00
}
2016-10-14 05:24:42 +00:00
2017-07-02 06:52:47 +00:00
_submitForm(e) {
if (e) e.preventDefault();
2017-07-19 07:04:54 +00:00
const query = this._inputEl.value;
if (this._suggester) this._suggester.success(query);
this.hide();
2018-12-16 11:50:11 +00:00
this._redirect(this._parseQuery(query).redirect);
2017-03-03 07:37:14 +00:00
}
_submitWithValue(value) {
this._inputEl.value = value;
this._submitForm();
}
}
2017-03-29 01:53:13 +00:00
</script>
2017-03-03 07:37:14 +00:00
2017-03-29 01:53:13 +00:00
<script>
2018-12-16 11:50:11 +00:00
const queryParser = new QueryParser({
commands: CONFIG.commands,
pathDelimiter: CONFIG.pathDelimiter,
searchDelimiter: CONFIG.searchDelimiter,
});
2017-07-11 06:04:57 +00:00
2018-12-16 11:50:11 +00:00
const influencers = CONFIG.influencers.map(influencerConfig => {
return new {
2017-07-11 06:30:49 +00:00
Default: DefaultInfluencer,
DuckDuckGo: DuckDuckGoInfluencer,
History: HistoryInfluencer,
2018-12-16 11:50:11 +00:00
}[influencerConfig.name]({
defaultSuggestions: CONFIG.defaultSuggestions,
limit: influencerConfig.limit,
parseQuery: queryParser.parse,
2017-07-11 06:04:57 +00:00
});
2018-12-16 11:50:11 +00:00
});
2017-06-28 05:13:49 +00:00
2018-12-16 11:50:11 +00:00
const suggester = new Suggester({
enabled: CONFIG.suggestions,
2018-12-16 11:50:11 +00:00
influencers,
limit: CONFIG.suggestionsLimit,
});
2017-07-11 06:04:57 +00:00
2018-12-16 11:50:11 +00:00
const help = new Help({
commands: CONFIG.commands,
newTab: CONFIG.newTab,
});
2017-06-28 05:13:49 +00:00
2018-12-16 11:50:11 +00:00
const form = new Form({
colors: CONFIG.colors,
instantRedirect: CONFIG.instantRedirect,
newTab: CONFIG.newTab,
parseQuery: queryParser.parse,
suggester,
2018-12-16 11:50:11 +00:00
toggleHelp: help.toggle,
});
2017-07-11 06:04:57 +00:00
new Clock({
delimiter: CONFIG.clockDelimiter,
onClick: CONFIG.clockAction === 'Search' ? form.show : help.toggle,
twentyFourHour: CONFIG.twentyFourHourClock,
2017-07-11 06:04:57 +00:00
});
2016-02-01 08:41:16 +00:00
</script>