surfingkeys-conf/completions.js

1151 lines
35 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { keys } = require("./conf.priv.js")
const { escape, createSuggestionItem, createURLItem } = require("./util")
const wpDefaultIcon = "data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%3E%0A%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2056%2056%22%20enable-background%3D%22new%200%200%2056%2056%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23eee%22%20d%3D%22M0%200h56v56h-56z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23999%22%20d%3D%22M36.4%2013.5h-18.6v24.9c0%201.4.9%202.3%202.3%202.3h18.7v-25c.1-1.4-1-2.2-2.4-2.2zm-6.2%203.5h5.1v6.4h-5.1v-6.4zm-8.8%200h6v1.8h-6v-1.8zm0%204.6h6v1.8h-6v-1.8zm0%2015.5v-1.8h13.8v1.8h-13.8zm13.8-4.5h-13.8v-1.8h13.8v1.8zm0-4.7h-13.8v-1.8h13.8v1.8z%22%2F%3E%0A%3C%2Fsvg%3E%0A"
const cbDefaultIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAAAAAByaaZbAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAACYktHRAD/h4/MvwAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAAd0SU1FB+EICxEMErRVWUQAAABOdEVYdFJhdyBwcm9maWxlIHR5cGUgZXhpZgAKZXhpZgogICAgICAyMAo0NTc4Njk2NjAwMDA0OTQ5MmEwMDA4MDAwMDAwMDAwMDAwMDAwMDAwCnwMkD0AAAGXSURBVEjH1ZRvc4IwDMb7/T8dbVr/sEPlPJQd3g22GzJdmxVOHaQa8N2WN7wwvyZ5Eh/hngzxTwDr0If/TAK67POxbqxnpgCIx9dkrkEvswYnAFiutFSgtQapS4ejwFYqbXQXBmC+QxawuI/MJb0LiCq0DICNHoZRKQdYLKQZEhATcQmwDYD5GR8DDtfqaYAMActvTiVMaUvqhZPVYhYAK2SBAwGMTHngnc4wVmFPW9L6k1PJxbSCkfvhqolKSQhsWSClizNyxwAWdzIADixQRXRmdWSHthsg+TknaztFMZgC3vh/nG/qo68TLAKrCSrUg1ulp3cH+BpItBp3DZf0lFXVOIDnBdwKkLO4D5Q3QMO6HJ+hUb1NKNWMGJn3jf4ejPKn99CXOtsuyab95obGL/rpdZ7oIJK87iPiumG01drbdggoCZuq/f0XaB8/FbG62Ta5cD97XJwuZUT7ONbZTIK5m94hBuQs8535MsL5xxPw6ZoNj0DiyzhhcyMf9BJ0Jk1uRRpNyb4y0UaM9UI7E8+kt/EHgR/R6042JzmiwgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wOC0xMVQxNzoxMjoxOC0wNDowMLy29LgAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDgtMTFUMTc6MTI6MTgtMDQ6MDDN60wEAAAAAElFTkSuQmCC"
const completions = {}
// Helper functions for Google Custom Search Engine completions
const googleCxURL = (alias) => {
const key = `google_cx_${alias}`
return `https://www.googleapis.com/customsearch/v1?key=${keys.google_cs}&cx=${keys[key]}&q=`
}
const googleCxPublicURL = (alias) => {
const key = `google_cx_${alias}`
return `https://cse.google.com/cse/publicurl?cx=${keys[key]}&q=`
}
const googleCxCallback = (response) => {
const res = JSON.parse(response.text).items
return res.map((s) => createSuggestionItem(`
<div>
<div class="title"><strong>${s.htmlTitle}</strong></div>
<div>${s.htmlSnippet}</div>
</div>
`, { url: s.link }))
}
// ****** Arch Linux ****** //
// Arch Linux official repos
completions.al = {
alias: "al",
name: "archlinux",
search: "https://www.archlinux.org/packages/?arch=x86_64&q=",
compl: googleCxURL("al"),
callback: googleCxCallback,
}
// Arch Linux AUR
completions.au = {
alias: "au",
name: "AUR",
favicon: "https://aur.archlinux.org/images/favicon.ico",
search: "https://aur.archlinux.org/packages/?O=0&SeB=nd&outdated=&SB=v&SO=d&PP=100&do_Search=Go&K=",
compl: "https://aur.archlinux.org/rpc?type=suggest&arg=",
}
completions.au.callback = (response) => {
const res = JSON.parse(response.text)
return res.map((s) => createURLItem(s, `https://aur.archlinux.org/packages/${s}`))
}
// Arch Linux Wiki
completions.aw = {
alias: "aw",
name: "archwiki",
search: "https://wiki.archlinux.org/index.php?go=go&search=",
compl: "https://wiki.archlinux.org/api.php?action=opensearch&format=json&formatversion=2&namespace=0&limit=10&suggest=true&search=",
}
completions.aw.callback = (response) => JSON.parse(response.text)[1]
// Arch Linux Forums
completions.af = {
alias: "af",
name: "archforums",
domain: "bbs.archlinux.org",
search: googleCxPublicURL("af"),
compl: googleCxURL("af"),
callback: googleCxCallback,
}
// ****** Technical Resources ****** //
// AlternativeTo
completions.at = {
alias: "at",
name: "alternativeTo",
search: "https://alternativeto.net/browse/search/?q=",
compl: `https://zidpns2vb0-dsn.algolia.net/1/indexes/fullitems?x-algolia-application-id=ZIDPNS2VB0&x-algolia-api-key=${keys.alternativeTo}&attributesToRetrieve=Name,UrlName,TagLine,Description,Likes,HasIcon,IconId,IconExtension,InternalUrl&query=`,
}
completions.at.callback = (response) => {
const res = JSON.parse(response.text)
return res.hits.map((s) => {
const name = escape(s.Name)
let title = name
let prefix = ""
if (s._highlightResult) { // eslint-disable-line no-underscore-dangle
if (s._highlightResult.Name) { // eslint-disable-line no-underscore-dangle
title = s._highlightResult.Name.value // eslint-disable-line no-underscore-dangle
}
}
if (s.Likes) {
prefix += `[↑${s.Likes}] `
}
let tagline = ""
if (s.TagLine) {
tagline = escape(s.TagLine)
}
const desc = s.Description ? `<div class="title">${escape(s.Description)}</div>` : ""
let icUrl = wpDefaultIcon
if (s.HasIcon) {
const icBase = "https://d2.alternativeto.net/dist/icons/"
const icQuery = "?width=100&height=100&mode=crop&upscale=false"
const icName = s.UrlName
icUrl = encodeURI(`${icBase}${icName}_${s.IconId}${s.IconExtension}${icQuery}`)
}
return createSuggestionItem(`
<div style="padding:5px;display:grid;grid-template-columns:60px 1fr;grid-gap:15px">
<img style="width:60px" src="${icUrl}" alt="${escape(s.Name)}">
<div>
<div class="title"><strong>${prefix}${title}</strong> ${tagline}</div>
${desc}
</div>
</div>
`, { url: `https://${s.InternalUrl}` })
})
}
// Chrome Webstore
completions.cs = {
alias: "cs",
name: "chromestore",
favicon: "https://www.google.com/images/icons/product/chrome_web_store-32.png",
search: "https://chrome.google.com/webstore/search/",
compl: googleCxURL("cs"),
callback: googleCxCallback,
}
const parseFirefoxAddonsRes = (response) => JSON.parse(response.text).results.map((s) => {
let { name } = s
if (typeof name === "object") {
if (name[navigator.language] !== undefined) {
name = name[navigator.language]
} else {
[name] = Object.values(name)
}
}
name = escape(name)
let prefix = ""
switch (s.type) {
case "extension":
prefix += "🧩 "
break
case "statictheme":
prefix += "🖌 "
break
default:
break
}
return createSuggestionItem(`
<div style="padding:5px;display:grid;grid-template-columns:2em 1fr;grid-gap:15px">
<img style="width:2em" src="${s.icon_url}" alt="${name}">
<div>
<div class="title"><strong>${prefix}${name}</strong></div>
</div>
</div>
`, { url: s.url })
})
// Firefox Addons
completions.fa = {
alias: "fa",
name: "firefox-addons",
search: "https://addons.mozilla.org/en-US/firefox/search/?q=",
compl: "https://addons.mozilla.org/api/v4/addons/autocomplete/?q=",
callback: parseFirefoxAddonsRes,
}
// Firefox Themes
completions.ft = {
alias: "ft",
name: "firefox-themes",
search: "https://addons.mozilla.org/en-US/firefox/search/?type=statictheme&q=",
compl: "https://addons.mozilla.org/api/v4/addons/autocomplete/?type=statictheme&q=",
callback: parseFirefoxAddonsRes,
}
// Firefox Extensions
completions.fe = {
alias: "fe",
name: "firefox-extensions",
search: "https://addons.mozilla.org/en-US/firefox/search/?type=extension&q=",
compl: "https://addons.mozilla.org/api/v4/addons/autocomplete/?type=extension&q=",
callback: parseFirefoxAddonsRes,
}
// OWASP Wiki
completions.ow = {
alias: "ow",
name: "owasp",
search: "https://www.owasp.org/index.php?go=go&search=",
compl: "https://www.owasp.org/api.php?action=opensearch&format=json&formatversion=2&namespace=0&limit=10&suggest=true&search=",
}
completions.ow.callback = (response) => JSON.parse(response.text)[1]
// StackOverflow
completions.so = {
alias: "so",
name: "stackoverflow",
search: "https://stackoverflow.com/search?q=",
compl: "https://api.stackexchange.com/2.2/search/advanced?pagesize=10&order=desc&sort=relevance&site=stackoverflow&q=",
}
completions.so.callback = (response) => JSON.parse(response.text).items.map((s) => createURLItem(`[${s.score}] ${s.title}`, s.link))
// DockerHub repo search
completions.dh = {
alias: "dh",
name: "dockerhub",
search: "https://hub.docker.com/search/?page=1&q=",
compl: "https://hub.docker.com/v2/search/repositories/?page_size=20&query=",
}
completions.dh.callback = (response) => JSON.parse(response.text).results.map((s) => {
let meta = ""
let repo = s.repo_name
meta += `[★${escape(s.star_count)}] `
meta += `[↓${escape(s.pull_count)}] `
if (repo.indexOf("/") === -1) {
repo = `_/${repo}`
}
return createSuggestionItem(`
<div>
<div class="title"><strong>${escape(repo)}</strong></div>
<div>${meta}</div>
<div>${escape(s.short_description)}</div>
</div>
`, { url: `https://hub.docker.com/r/${repo}` })
})
// GitHub
completions.gh = {
alias: "gh",
name: "github",
search: "https://github.com/search?q=",
compl: "https://api.github.com/search/repositories?sort=stars&order=desc&q=",
}
completions.gh.callback = (response) => JSON.parse(response.text).items.map((s) => {
let prefix = ""
if (s.stargazers_count) {
prefix += `[★${s.stargazers_count}] `
}
return createURLItem(prefix + s.full_name, s.html_url)
})
// Domainr domain search
completions.do = {
alias: "do",
name: "domainr",
search: "https://domainr.com/?q=",
compl: "https://5jmgqstc3m.execute-api.us-west-1.amazonaws.com/v1/domainr?q=",
}
completions.do.callback = (response) => Object.entries(JSON.parse(response.text))
.map(([domain, data]) => {
let color = "inherit"
let symbol = "<strong>?</strong> "
switch (data.summary) {
case "inactive":
color = "#23b000"
symbol = "✔ "
break
case "unknown":
break
default:
color = "#ff4d00"
symbol = "✘ "
}
return createSuggestionItem(
`<div><div class="title" style="color:${color}"><strong>${symbol}${escape(domain)}</strong></div></div>`,
{ url: `https://domainr.com/${domain}` }
)
})
// Vim Wiki
completions.vw = {
alias: "vw",
name: "vimwiki",
favicon: "https://vignette.wikia.nocookie.net/vim/images/6/64/Favicon.ico",
search: "https://vim.fandom.com/wiki/Special:Search?query=",
compl: "https://vim.fandom.com/api.php?action=opensearch&format=json&formatversion=2&namespace=0&limit=10&suggest=true&search=",
}
completions.vw.callback = (response) => JSON.parse(response.text)[1]
.map((r) => createURLItem(r, `https://vim.fandom.com/wiki/${r}`))
// ****** Shopping & Food ****** //
// Amazon
completions.az = {
alias: "az",
name: "amazon",
search: "https://smile.amazon.com/s/?field-keywords=",
compl: "https://completion.amazon.com/search/complete?method=completion&mkt=1&search-alias=aps&q=",
}
completions.az.callback = (response) => JSON.parse(response.text)[1]
// Craigslist
completions.cl = {
alias: "cl",
name: "craigslist",
search: "https://www.craigslist.org/search/sss?query=",
compl: "https://www.craigslist.org/suggest?v=12&type=search&cat=sss&area=1&term=",
}
completions.cl.callback = (response) => JSON.parse(response.text)
// EBay
completions.eb = {
alias: "eb",
name: "ebay",
search: "https://www.ebay.com/sch/i.html?_nkw=",
compl: "https://autosug.ebay.com/autosug?callback=0&sId=0&kwd=",
}
completions.eb.callback = (response) => JSON.parse(response.text).res.sug
// Yelp
completions.yp = {
alias: "yp",
name: "yelp",
search: "https://www.yelp.com/search?find_desc=",
compl: "https://www.yelp.com/search_suggest/v2/prefetch?prefix=",
}
completions.yp.callback = (response) => {
const res = JSON.parse(response.text).response
const words = []
res.forEach((r) => {
r.suggestions.forEach((s) => {
const w = s.query
if (words.indexOf(w) === -1) {
words.push(w)
}
})
})
return words
}
// ****** General References, Calculators & Utilities ****** //
const parseDatamuseRes = (res, o = {}) => {
const opts = {
maxDefs: -1,
ellipsis: false,
}
Object.assign(opts, o)
return res.map((r) => {
const defs = []
let defsHtml = ""
if ((opts.maxDefs <= -1 || opts.maxDefs > 0) && r.defs && r.defs.length > 0) {
for (const d of r.defs.slice(0, opts.maxDefs <= -1 ? undefined : opts.maxDefs)) {
const ds = d.split("\t")
const partOfSpeech = `(${escape(ds[0])})`
const def = escape(ds[1])
defs.push(`<span><em>${partOfSpeech}</em> ${def}</span>`)
}
if (opts.ellipsis && r.defs.length > opts.maxDefs) {
defs.push("<span><em>&hellip;</em></span>")
}
defsHtml = `<div>${defs.join("<br />")}</div>`
}
return createSuggestionItem(`
<div>
<div class="title"><strong>${escape(r.word)}</strong></div>
${defsHtml}
</div>
`, { url: `${opts.wordBaseURL}${r.word}` })
})
}
// Dictionary
completions.de = {
alias: "de",
name: "define",
search: "http://onelook.com/?w=",
compl: "https://api.datamuse.com/words?md=d&sp=%s*",
opts: {
maxDefs: 16,
ellipsis: true,
wordBaseURL: "http://onelook.com/?w=",
},
}
completions.de.callback = (response) => {
const res = JSON.parse(response.text)
return parseDatamuseRes(res, completions.de.opts)
}
// Thesaurus
completions.th = {
alias: "th",
name: "thesaurus",
search: "https://www.onelook.com/thesaurus/?s=",
compl: "https://api.datamuse.com/words?md=d&ml=%s",
opts: {
maxDefs: 3,
ellipsis: true,
wordBaseURL: "http://onelook.com/thesaurus/?s=",
},
}
completions.th.callback = (response) => {
const res = JSON.parse(response.text)
return parseDatamuseRes(res, completions.th.opts)
}
// Wikipedia
completions.wp = {
alias: "wp",
name: "wikipedia",
search: "https://en.wikipedia.org/w/index.php?search=",
compl: "https://en.wikipedia.org/w/api.php?action=query&format=json&generator=prefixsearch&prop=info|pageprops%7Cpageimages%7Cdescription&redirects=&ppprop=displaytitle&piprop=thumbnail&pithumbsize=100&pilimit=6&inprop=url&gpssearch=",
}
completions.wp.callback = (response) => Object.values(JSON.parse(response.text).query.pages)
.map((p) => {
const img = p.thumbnail ? p.thumbnail.source : wpDefaultIcon
const desc = p.description ? p.description : ""
return createSuggestionItem(
`
<div style="padding:5px;display:grid;grid-template-columns:60px 1fr;grid-gap:15px">
<img style="width:60px" src="${img}" alt="${p.title}">
<div>
<div class="title"><strong>${p.title}</strong></div>
<div class="title">${desc}</div>
</div>
</div>
`,
{ url: p.fullurl }
)
})
// Wikipedia - Simple English version
completions.ws = {
alias: "ws",
name: "wikipedia-simple",
search: "https://simple.wikipedia.org/w/index.php?search=",
compl: "https://simple.wikipedia.org/w/api.php?action=query&format=json&generator=prefixsearch&prop=info|pageprops%7Cpageimages%7Cdescription&redirects=&ppprop=displaytitle&piprop=thumbnail&pithumbsize=100&pilimit=6&inprop=url&gpssearch=",
callback: completions.wp.callback,
}
// Wiktionary
completions.wt = {
alias: "wt",
name: "wiktionary",
search: "https://en.wiktionary.org/w/index.php?search=",
compl: "https://en.wiktionary.org/w/api.php?action=query&format=json&generator=prefixsearch&gpssearch=",
}
completions.wt.callback = (response) => Object.values(JSON.parse(response.text).query.pages)
.map((p) => p.title)
// WolframAlpha
completions.wa = {
alias: "wa",
name: "wolframalpha",
search: "http://www.wolframalpha.com/input/?i=",
compl: `http://api.wolframalpha.com/v2/query?appid=${keys.wolframalpha}&format=plaintext&output=json&reinterpret=true&input=%s`,
}
completions.wa.callback = (response) => {
const res = JSON.parse(response.text).queryresult
if (res.error) {
return [createSuggestionItem(`
<div>
<div class="title"><strong>Error</strong> (Code ${escape(res.error.code)})</div>
<div class="title">${escape(res.error.msg)}</div>
</div>`, { url: "https://www.wolframalpha.com/" })]
}
if (!res.success) {
if (res.tips) {
return [createSuggestionItem(`
<div>
<div class="title"><strong>No Results</strong></div>
<div class="title">${escape(res.tips.text)}</div>
</div>`, { url: "https://www.wolframalpha.com/" })]
}
if (res.didyoumeans) {
return res.didyoumeans.map((s) => createSuggestionItem(`
<div>
<div class="title"><strong>Did you mean...?</strong></div>
<div class="title">${escape(s.val)}</div>
</div>`, { url: "https://www.wolframalpha.com/" }))
}
return [createSuggestionItem(`
<div>
<div class="title"><strong>Error</strong></div>
<div class="title">An unknown error occurred.</div>
</div>`, { url: "https://www.wolframalpha.com/" })]
}
const results = []
res.pods.forEach((p) => {
const result = {
title: escape(p.title),
values: [],
url: "http://www.wolframalpha.com/input/?i=",
}
if (p.numsubpods > 0) {
result.url += encodeURIComponent(p.subpods[0].plaintext)
p.subpods.forEach((sp) => {
if (!sp.plaintext) return
let v = ""
if (sp.title) {
v += `<strong>${escape(sp.title)}</strong>: `
}
v += escape(sp.plaintext)
result.values.push(`<div class="title">${v}</div>`)
})
}
if (result.values.length > 0) {
results.push(result)
}
})
return results.map((r) => createSuggestionItem(`
<div>
<div class="title"><strong>${r.title}</strong></div>
${r.values.join("\n")}
</div>`, { url: r.url }))
}
// ****** Business Utilities & References ****** //
const parseCrunchbase = (response, parse) => {
const res = JSON.parse(response.text).data.items
if (res.length === 0) {
return [createSuggestionItem(`
<div>
<div class="title"><strong>No Results</strong></div>
<div class="title">Nothing matched your query</div>
</div>`, { url: "https://www.crunchbase.com/" })]
}
const objs = res.map((obj) => parse(obj))
return objs.map((p) => {
const domain = p.domain ? ` | <a href="https://${p.domain}" target="_blank">${p.domain}</a>` : ""
const location = p.loc ? ` located in <em>${p.loc}</em>` : ""
return createSuggestionItem(`
<div style="padding:5px;display:grid;grid-template-columns:60px 1fr;grid-gap:15px">
<img style="width:60px" src="${p.img}" alt="${p.name}">
<div style="display:grid;grid-template-rows:1fr 1fr 0.8fr">
<div class="title"><strong style="font-size: 1.2em">${p.name}</strong></div>
<div class="title" style="font-size: 1.2em">${p.desc}</div>
<div class="title"><em>${p.role}</em>${location}${domain}</div>
</div>
</div>`, { url: p.url })
})
}
// Crunchbase Organization Search
completions.co = {
alias: "co",
name: "crunchbase-orgs",
search: "https://www.crunchbase.com/textsearch?q=",
compl: `https://api.crunchbase.com/v/3/odm_organizations?user_key=${keys.crunchbase}&query=%s`,
}
completions.co.callback = (response) => parseCrunchbase(response, (org) => {
const r = org.properties
const p = {
name: escape(r.name),
domain: r.domain !== null ? escape(r.domain).replace(/\/$/, "") : null,
desc: escape(r.short_description),
role: escape(r.primary_role),
img: cbDefaultIcon,
loc: "",
url: `https://www.crunchbase.com/${r.web_path}`,
}
p.loc += (r.city_name !== null) ? escape(r.city_name) : ""
p.loc += (r.region_name !== null && p.loc !== "") ? ", " : ""
p.loc += (r.region_name !== null) ? escape(r.region_name) : ""
p.loc += (r.country_code !== null && p.loc !== "") ? ", " : ""
p.loc += (r.country_code !== null) ? escape(r.country_code) : ""
if (r.profile_image_url !== null) {
const u = r.profile_image_url
const img = u.slice(u.indexOf("t_api_images") + "t_api_images".length + 1)
p.img = `https://res-4.cloudinary.com/crunchbase-production/image/upload/c_lpad,h_100,w_100,f_auto,b_white,q_auto:eco/${img}`
}
return p
})
// Crunchbase People Search
completions.cp = {
alias: "cp",
name: "crunchbase-people",
search: "https://www.crunchbase.com/app/search/?q=",
compl: `https://api.crunchbase.com/v/3/odm_people?user_key=${keys.crunchbase}&query=%s`,
}
completions.cp.callback = (response) => parseCrunchbase(response, (person) => {
const r = person.properties
const p = {
name: `${escape(r.first_name)} ${escape(r.last_name)}`,
desc: "",
img: cbDefaultIcon,
role: "",
loc: "",
url: `https://www.crunchbase.com/${r.web_path}`,
}
p.desc += (r.title !== null) ? escape(r.title) : ""
p.desc += (r.organization_name !== null && p.desc !== "") ? ", " : ""
p.desc += (r.organization_name !== null) ? escape(r.organization_name) : ""
p.loc += (r.city_name !== null) ? escape(r.city_name) : ""
p.loc += (r.region_name !== null && p.loc !== "") ? ", " : ""
p.loc += (r.region_name !== null) ? escape(r.region_name) : ""
p.loc += (r.country_code !== null && p.loc !== "") ? ", " : ""
p.loc += (r.country_code !== null) ? escape(r.country_code) : ""
if (r.profile_image_url !== null) {
const url = r.profile_image_url
const path = url.split("/")
const img = encodeURIComponent(path[path.length - 1])
p.img = `http://public.crunchbase.com/t_api_images/v1402944794/c_pad,h_50,w_50/${img}`
}
return p
})
// ****** Search Engines ****** //
// DuckDuckGo
completions.dd = {
alias: "dd",
name: "duckduckgo",
search: "https://duckduckgo.com/?q=",
compl: "https://duckduckgo.com/ac/?q=",
}
completions.dd.callback = (response) => JSON.parse(response.text).map((r) => r.phrase)
// DuckDuckGo - I'm Feeling Lucky
completions.D = {
alias: "D",
name: "duckduckgo-lucky",
search: "https://duckduckgo.com/?q=\\",
compl: "https://duckduckgo.com/ac/?q=\\",
callback: completions.dd.callback,
}
// DuckDuckGo Images
completions.di = {
alias: "di",
name: "duckduckgo-images",
search: "https://duckduckgo.com/?ia=images&iax=images&q=",
compl: "https://duckduckgo.com/ac/?ia=images&iax=images&q=",
callback: completions.dd.callback,
}
// DuckDuckGo Videos
completions.dv = {
alias: "dv",
name: "duckduckgo-videos",
search: "https://duckduckgo.com/?ia=videos&iax=videos&q=",
compl: "https://duckduckgo.com/ac/?ia=videos&iax=videos&q=",
callback: completions.dd.callback,
}
// DuckDuckGo News
completions.dn = {
alias: "dn",
name: "duckduckgo-news",
search: "https://duckduckgo.com/?iar=news&ia=news&q=",
compl: "https://duckduckgo.com/ac/?iar=news&ia=news&q=",
callback: completions.dd.callback,
}
// DuckDuckGo Maps
completions.dm = {
alias: "dm",
name: "duckduckgo-maps",
search: "https://duckduckgo.com/?ia=maps&iax=maps&iaxm=places&q=",
compl: "https://duckduckgo.com/ac/?ia=maps&iax=maps&iaxm=places&q=",
callback: completions.dd.callback,
}
// Google
completions.go = {
alias: "go",
name: "google",
search: "https://www.google.com/search?q=",
compl: "https://www.google.com/complete/search?client=chrome-omni&gs_ri=chrome-ext&oit=1&cp=1&pgcl=7&q=",
}
completions.go.callback = (response) => JSON.parse(response.text)[1]
// Google Images
completions.gi = {
alias: "gi",
name: "google-images",
search: "https://www.google.com/search?tbm=isch&q=",
compl: "https://www.google.com/complete/search?client=chrome-omni&gs_ri=chrome-ext&oit=1&cp=1&pgcl=7&ds=i&q=",
callback: completions.go.callback,
}
// Google - I'm Feeling Lucky
completions.G = {
alias: "G",
name: "google-lucky",
search: "https://www.google.com/search?btnI=1&q=",
compl: "https://www.google.com/complete/search?client=chrome-omni&gs_ri=chrome-ext&oit=1&cp=1&pgcl=7&q=",
callback: completions.go.callback,
}
// ****** Elixir ****** //
// Hex.pm
completions.hx = {
alias: "hx",
name: "hex",
search: "https://hex.pm/packages?sort=downloads&search=",
compl: "https://hex.pm/api/packages?sort=downloads&hx&search=",
}
completions.hx.callback = (response) => JSON.parse(response.text).map((s) => {
let dls = ""
let desc = ""
let liscs = ""
if (s.downloads && s.downloads.all) {
dls = `[↓${escape(s.downloads.all)}] `
}
if (s.meta) {
if (s.meta.description) {
desc = escape(s.meta.description)
}
if (s.meta.licenses) {
s.meta.licenses.forEach((l) => {
liscs += `[&copy;${escape(l)}] `
})
}
}
return createSuggestionItem(`
<div>
<div class="title">${escape(s.repository)}/<strong>${escape(s.name)}</strong></div>
<div>${dls}${liscs}</div>
<div>${desc}</div>
</div>
`, { url: s.html_url })
})
// hexdocs
// Same as hex but links to documentation pages
completions.hd = {
alias: "hd",
name: "hexdocs",
search: "https://hex.pm/packages?sort=downloads&search=",
compl: "https://hex.pm/api/packages?sort=downloads&hd&search=",
}
completions.hd.callback = (response) => JSON.parse(response.text).map((s) => {
let dls = ""
let desc = ""
if (s.downloads && s.downloads.all) {
dls = `[↓${escape(s.downloads.all)}]`
}
if (s.meta) {
if (s.meta.description) {
desc = escape(s.meta.description)
}
}
return createSuggestionItem(`
<div>
<div class="title">${escape(s.repository)}/<strong>${escape(s.name)}</strong>${dls}</div>
<div></div>
<div>${desc}</div>
</div>
`, { url: `https://hexdocs.pm/${encodeURIComponent(s.name)}` })
})
// Exdocs
// Similar to `hd` but searches inside docs using Google Custom Search
completions.ex = {
alias: "ex",
name: "exdocs",
search: "https://hex.pm/packages?sort=downloads&ex&search=",
compl: googleCxURL("ex"),
}
completions.ex.callback = (response) => JSON.parse(response.text).items.map((s) => {
let hash = ""
const snippet = s.htmlSnippet
const openTag = "<b>"
const closeTag = "</b>"
const openArgs = "("
const closeArgs = ")"
let f1 = snippet.indexOf(openTag)
if (f1 === -1) {
return null
}
const f2 = snippet.indexOf(closeTag)
if (f2 === -1) {
return null
}
f1 += openTag.length
const f3 = f2 + closeTag.length
const fname = snippet.slice(f1, f2)
const snippetEnd = snippet.slice(f3)
const a1 = snippetEnd.indexOf(openArgs)
if (a1 !== 0) {
return null
}
let a2 = snippetEnd.indexOf(closeArgs)
if (a2 === -1) {
return null
}
a2 += closeArgs.length
const fargs = snippetEnd.slice(a1, a2)
const fary = fargs.replace(new RegExp(openArgs + closeArgs), "").split(",").length
hash = escape(`${fname}/${fary}`)
const moduleName = escape(s.title).split(" ")[0]
let subtitle = ""
if (hash) {
subtitle = `
<div style="font-size:1.1em; line-height:1.25em">
<em>${moduleName}</em>.<strong>${hash}</strong>
</div>`
}
return createSuggestionItem(`
<div>
<div class="title"><strong>${s.htmlTitle}</strong></div>
${subtitle}
<div>${s.htmlSnippet}</div>
</div>
`, { url: `${s.link}#${hash}` })
}).filter((s) => s !== null)
// ****** Golang ****** //
// Golang Docs (Google CSE)
completions.gg = {
alias: "gg",
name: "golang",
domain: "golang.org",
search: googleCxPublicURL("gg"),
compl: googleCxURL("gg"),
callback: googleCxCallback,
}
// Godoc
completions.gd = {
alias: "gd",
name: "godoc",
search: "https://godoc.org/?q=",
compl: "https://api.godoc.org/search?q=",
}
completions.gd.callback = (response) => JSON.parse(response.text).results.map((s) => {
let prefix = ""
if (s.import_count) {
prefix += `[↓${s.import_count}] `
}
if (s.stars) {
prefix += `[★${s.stars}] `
}
return createURLItem(prefix + s.path, `https://godoc.org/${s.path}`)
})
// Gowalker
completions.gw = {
alias: "gw",
name: "gowalker",
favicon: "https://gowalker.org/img/favicon.png",
search: "https://gowalker.org/search?auto_redirect=true&q=",
compl: "https://gowalker.org/search/json?q=",
}
completions.gw.callback = (response) => JSON.parse(response.text).results.map((s) => {
const title = escape(s.title)
const desc = escape(s.description)
return createSuggestionItem(`
<div>
<div class="title"><strong>${title}</strong></div>
<div>${desc}</div>
</div>
`, { url: `https://golang.org/doc/${encodeURIComponent(s.url)}` })
})
// Go-Search
completions.gs = {
alias: "gs",
name: "go-search",
favicon: "https://go-search.org/images/logo-16.png",
search: "http://go-search.org/search?q=",
compl: "http://go-search.org/api?action=search&q=",
}
completions.gs.callback = (response) => JSON.parse(response.text).hits
.map((r) => r.package)
// ****** Haskell ****** //
// Hackage
completions.ha = {
alias: "ha",
name: "hackage",
favicon: "https://hackage.haskell.org/static/favicon.png",
search: "https://hackage.haskell.org/packages/search?terms=",
compl: "https://hackage.haskell.org/packages/search.json?terms=",
}
completions.ha.callback = (response) => JSON.parse(response.text)
.map((s) => createURLItem(s.name, `https://hackage.haskell.org/package/${s.name}`))
// Hoogle
completions.ho = {
alias: "ho",
name: "hoogle",
favicon: "https://www.haskell.org/img/favicon.ico",
search: `https://www.haskell.org/hoogle/?hoogle=${
encodeURIComponent("+platform +xmonad +xmonad-contrib ")}`, // This tells Hoogle to include these modules in the search - encodeURIComponent is only used for better readability
compl: `https://www.haskell.org/hoogle/?mode=json&hoogle=${
encodeURIComponent("+platform +xmonad +xmonad-contrib ")}`,
}
completions.ho.callback = (response) => JSON.parse(response.text).results
.map((s) => createURLItem(s.self, s.location))
// Haskell Wiki
completions.hw = {
alias: "hw",
name: "haskellwiki",
favicon: "https://www.haskell.org/img/favicon.ico",
search: "https://wiki.haskell.org/index.php?go=go&search=",
compl: "https://wiki.haskell.org/api.php?action=opensearch&format=json&formatversion=2&namespace=0&limit=10&suggest=true&search=",
}
completions.hw.callback = (response) => JSON.parse(response.text)[1]
// Hayoo
completions.hy = {
alias: "hy",
name: "hayoo",
favicon: "https://www.haskell.org/img/favicon.ico",
search: "http://hayoo.fh-wedel.de/?query=",
compl: "http://hayoo.fh-wedel.de/json?query=",
}
completions.hy.callback = (response) => JSON.parse(response.text).result
.map((s) => createURLItem(`[${s.resultType}] ${s.resultName}`, s.resultUri))
// ****** HTML, CSS, JavaScript, NodeJS, ... ****** //
// jQuery API documentation
completions.jq = {
alias: "jq",
name: "jquery",
domain: "jquery.com",
search: googleCxPublicURL("jq"),
compl: googleCxURL("jq"),
callback: googleCxCallback,
}
// NodeJS standard library documentation
completions.no = {
alias: "no",
name: "node",
domain: "nodejs.org",
search: googleCxPublicURL("no"),
compl: googleCxURL("no"),
callback: googleCxCallback,
}
// Mozilla Developer Network (MDN)
completions.md = {
alias: "md",
name: "mdn",
search: "https://developer.mozilla.org/en-US/search?q=",
compl: "https://developer.mozilla.org/en-US/search.json?q=",
}
completions.md.callback = (response) => {
const res = JSON.parse(response.text)
return res.documents.map((s) => {
let excerpt = escape(s.excerpt)
if (excerpt.length > 240) {
excerpt = `${excerpt.slice(0, 240)}`
}
res.query.split(" ").forEach((q) => {
excerpt = excerpt.replace(new RegExp(q, "gi"), "<strong>$&</strong>")
})
const title = escape(s.title)
const slug = escape(s.slug)
return createSuggestionItem(`
<div>
<div class="title"><strong>${title}</strong></div>
<div style="font-size:0.8em"><em>${slug}</em></div>
<div>${excerpt}</div>
</div>
`, { url: `https://developer.mozilla.org/${s.slug}` })
})
}
// NPM registry search
completions.np = {
alias: "np",
name: "npm",
favicon: "https://static.npmjs.com/da3ab40fb0861d15c83854c29f5f2962.png",
search: "https://www.npmjs.com/search?q=",
compl: "https://api.npms.io/v2/search/suggestions?size=20&q=",
}
completions.np.callback = (response) => JSON.parse(response.text)
.map((s) => {
let flags = ""
let desc = ""
let stars = ""
let score = ""
if (s.package.description) {
desc = escape(s.package.description)
}
if (s.score) {
if (s.score.final) {
score = Math.round(Number(s.score.final) * 5)
stars = "★".repeat(score) + "☆".repeat(5 - score)
}
}
if (s.flags) {
Object.keys(s.flags).forEach((f) => {
flags += `[<span style='color:#ff4d00'>⚑</span> ${escape(f)}] `
})
}
return createSuggestionItem(`
<div>
<style>
.title>em {
font-weight: bold;
}
</style>
<div class="title">${s.highlight}</div>
<div>
<span style="font-size:2em;line-height:0.5em">${stars}</span>
<span>${flags}</span>
</div>
<div>${desc}</div>
</div>
`, { url: s.package.links.npm })
})
// ****** Social Media & Entertainment ****** //
// Hacker News (YCombinator)
completions.hn = {
alias: "hn",
name: "hackernews",
domain: "news.ycombinator.com",
search: "https://hn.algolia.com/?query=",
compl: "https://hn.algolia.com/api/v1/search?tags=(story,comment)&query=",
}
completions.hn.callback = (response) => {
const res = JSON.parse(response.text)
return res.hits.map((s) => {
let title = ""
let prefix = ""
if (s.points) {
prefix += `[↑${s.points}] `
}
if (s.num_comments) {
prefix += `[↲${s.num_comments}] `
}
switch (s._tags[0]) { // eslint-disable-line no-underscore-dangle
case "story":
title = s.title // eslint-disable-line prefer-destructuring
break
case "comment":
title = s.comment_text
break
default:
title = s.objectID
}
const re = new RegExp(`(${res.query.split(" ").join("|")})`, "ig")
title = title.replace(re, "<strong>$&</strong>")
const url = `https://news.ycombinator.com/item?id=${s.objectID}`
return createSuggestionItem(`
<div>
<div class="title">${prefix + title}</div>
<div class="url">${url}</div>
</div>
`, { url })
})
}
// Reddit
completions.re = {
alias: "re",
name: "reddit",
search: "https://www.reddit.com/search?sort=relevance&t=all&q=",
compl: "https://api.reddit.com/search?syntax=plain&sort=relevance&limit=20&q=",
}
completions.re.callback = (response) => JSON.parse(response.text).data.children
.map((s) => createURLItem(`[${s.data.score}] ${s.data.title}`, `https://reddit.com${s.data.permalink}`))
// YouTube
completions.yt = {
alias: "yt",
name: "youtube",
search: "https://www.youtube.com/search?q=",
compl: `https://www.googleapis.com/youtube/v3/search?maxResults=20&part=snippet&type=video,channel&key=${keys.google_yt}&safeSearch=none&q=`,
}
completions.yt.callback = (response) => JSON.parse(response.text).items
.map((s) => {
switch (s.id.kind) {
case "youtube#channel":
return createURLItem(
`${s.snippet.channelTitle}: ${s.snippet.description}`,
`https://youtube.com/channel/${s.id.channelId}`,
)
case "youtube#video":
return createURLItem(
`${s.snippet.title}`,
`https://youtu.be/${s.id.videoId}`,
)
default:
return null
}
}).filter((s) => s !== null)
module.exports = completions