API search now works

This commit is contained in:
ポール ウェッブ 2018-08-01 17:18:39 -05:00
parent 976f535aec
commit 68a1e27881
7 changed files with 296 additions and 42 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,246 @@
/* global define */
/*! Jets.js - v0.14.1 - 2018-06-22
* http://NeXTs.github.com/Jets.js/
* Copyright (c) 2015 Denis Lukov; Refactored 2018 Paul Anthony Webb; Licensed MIT */
(function (root, definition) {
if (typeof module !== "undefined") module.exports = definition();
else if (typeof define === "function" && typeof define.amd === "object") define(definition);
else root["Jets"] = definition();
}(this, function () {
"use strict";
function Jets(opts) {
if (!(this instanceof Jets)) return new Jets(opts);
const defaults = {
diacriticsMap: {},
hideBy: "display: none",
searchSelector: "*AND"
};
const self = this;
self.options = {};
[
"addImportant",
"callSearchManually",
"columns",
"diacriticsMap",
"didSearch",
"hideBy",
"invert",
"manualContentHandling",
"searchInSpecificColumn",
"searchSelector"
].forEach(name => self.options[name] = opts[name] || defaults[name]);
if (this.options.searchSelector.length > 1) {
const searchSelector = self.options["searchSelector"].trim();
self.options.searchSelector = searchSelector.substr(0, 1);
self.options.searchSelectorMode = searchSelector.substr(1).toUpperCase();
}
self.content_tag = document.querySelectorAll(opts.contentTag);
if (!self.content_tag) throw new Error("Error! Could not find contentTag element");
self.content_param = opts.contentTag;
self.search_tag = document.querySelector(opts.searchTag);
if (
!self.search_tag &&
!self.options.callSearchManually
) throw new Error("Error! Provide one of search methods: searchTag or callSearchManually and call .search(\"phrase\") manually");
let last_search_query = self.search_tag && self.search_tag.value || "";
self.search = (search_query, optional_column) => {
const new_search_query =
self.options.callSearchManually &&
typeof search_query !== "undefined"
? search_query
: self.search_tag
? self.search_tag.value
: "";
if (last_search_query === (last_search_query = new_search_query)) return;
(0, self._applyCSS(last_search_query, optional_column));
self.options.didSearch && self.options.didSearch(last_search_query);
};
self._onSearch = function (event) {
if (event.type === "keydown") return setTimeout(self.search, 0);
self.search();
};
self.destroy = function () {
if (!self.options.callSearchManually) self._processEventListeners("remove");
self._destroy();
};
if (!self.options.callSearchManually) self._processEventListeners("add");
self._addStyleTag();
self._setJets();
self._applyCSS(last_search_query);
}
Jets.prototype = {
constructor: Jets,
_processEventListeners: function (action) {
[
"change",
"input",
"keydown"
].forEach(function (event_type) {
this.search_tag[action + "EventListener"](event_type, this._onSearch);
}.bind(this));
},
_applyCSS: function (search_query, optional_column) {
const options = this.options;
const search_phrase = this.replaceDiacritics(
search_query
.trim()
.toLowerCase()
.replace(/\s\s+/g, " ")
).replace(/\\/g, "\\\\");
const words = options.searchSelectorMode
? search_phrase.split(" ").filter((item, pos, arr) => arr.indexOf(item) === pos)
: [search_phrase];
const is_strict_selector = options.searchSelectorMode === "AND";
const selectors = new Array(words.length);
for (let i = 0, ii = words.length; i < ii; i++) {
selectors[i] =
(is_strict_selector ? this.content_param + ">" : "") +
(options.invert ? "" : ":not(") +
"[data-jets" + (typeof optional_column !== "undefined" ? "-col-" + optional_column : "") + options.searchSelector + `="${words[i]}"]` +
(options.invert ? "" : ")");
}
const hide_rules =
options.hideBy
.split(";")
.filter(Boolean)
.map(rule => rule + (options.addImportant ? "!important" : ""));
const css_rule = (is_strict_selector ? "" : this.content_param + ">") + selectors.join(is_strict_selector ? "," : "") + "{" + hide_rules.join(";") + "}";
this.styleTag.innerHTML = search_phrase.length ? css_rule : "";
},
_addStyleTag: function () {
this.styleTag = document.createElement("style");
document.head.appendChild(this.styleTag);
},
_getText: function (tag) {
return tag && (tag.textContent || tag.innerText) || "";
},
_sanitize: function (text) {
return this.replaceDiacritics(text).trim().replace(/\s+/g, " ").toLowerCase();
},
_getContentTags: function (query) {
return Array.prototype.slice.call(this.content_tag).reduce((all, elem) => {
return all.concat(Array.prototype.slice.call(elem.querySelectorAll(query || ":scope > *")));
}, []);
},
_handleSpecificColumns: function (tag, set) {
const self = this;
if (!self.options.searchInSpecificColumn) return;
Array.prototype.slice.call(tag.children).map((children, i) => {
if (
self.options.columns &&
self.options.columns.length &&
self.options.columns.indexOf(i) === -1
) return;
tag[(set || "remove") + "Attribute"]("data-jets-col-" + i, set && self._sanitize(self._getText(children)));
});
},
_setJets: function (query, force) {
const self = this;
const tags = self._getContentTags(force ? "" : query);
let text;
for (const tag of tags) {
if (tag.hasAttribute("data-jets") && !force) continue;
text = this.options.manualContentHandling
? this.options.manualContentHandling(tag)
: self.options.columns &&
self.options.columns.length
? self.options.columns.map(column => self._getText(tag.children[column])).join(" ")
: self._getText(tag);
tag.setAttribute("data-jets", self._sanitize(text));
self._handleSpecificColumns(tag, "set");
}
},
replaceDiacritics: function (text) {
const diacritics = this.options.diacriticsMap;
for (const letter in diacritics) if (diacritics.hasOwnProperty(letter)) {
for (let i = 0, ii = diacritics[letter].length; i < ii; i++) {
text = text.replace(new RegExp(diacritics[letter][i], "g"), letter);
}
}
return text;
},
update: function (force) {
this._setJets(":scope > :not([data-jets])", force);
},
_destroy: function () {
this.styleTag.parentNode && document.head.removeChild(this.styleTag);
const tags = this._getContentTags();
for (const tag of tags) {
tag.removeAttribute("data-jets");
this._handleSpecificColumns(tag);
}
}
}
// :scope polyfill
// https://stackoverflow.com/a/17989803/1221082
;(function (doc, proto) {
try {
doc.querySelector(":scope body");
} catch (err) {
["querySelector", "querySelectorAll"].forEach(method => {
const nativ = proto[method];
proto[method] = function (selectors) {
if (/(^|,)\s*:scope/.test(selectors)) {
const id = this.getAttribute("id");
this.id = "ID_" + Date.now();
selectors = selectors.replace(/((^|,)\s*):scope/g, "$1#" + this.getAttribute("id"));
const result = doc[method](selectors);
this.id = id;
return result;
}
return nativ.call(this, selectors);
};
});
}
})(window.document, Element.prototype);
return Jets;
}));

View file

@ -28,9 +28,9 @@
.api__toc__search__field { .api__toc__search__field {
border-bottom: 1px solid rgba($gray, 0.3); border-bottom: 1px solid rgba($gray, 0.3);
font-size: 1rem; font-size: 0.8rem;
line-height: 2rem; line-height: 2rem;
padding: 0.25rem calc(2rem + 4px) 0.25rem 0.5rem; padding: 0.25rem calc(2rem + 4px) 0.25rem 0.75rem;
width: 100%; width: 100%;
} }
@ -43,7 +43,7 @@
color: $white; color: $white;
cursor: pointer; cursor: pointer;
font-size: 1rem; font-size: 1rem;
line-height: 1.3; line-height: 1.15;
position: absolute; position: absolute;
text-align: center; text-align: center;
transition: opacity 0.2s; transition: opacity 0.2s;
@ -59,39 +59,9 @@
} }
} }
.api__toc__search__results,
.api__toc__items { .api__toc__items {
font-size: 0.8rem; font-size: 0.8rem;
line-height: 1.33; line-height: 1.33;
}
.api__toc__search__results {
list-style-type: none;
&:not(.active) {
display: none;
}
&.active {
background-color: rgba($gray, 0.3);
border-bottom: 1px solid rgba($gray, 0.3);
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
a {
display: block;
padding: 0.25rem 0.5rem 0.25rem 0.75rem;
&:hover {
background-color: rgba($gray, 0.3);
}
}
}
.api__toc__items {
list-style-type: none; list-style-type: none;
padding-top: 0.25rem; padding-top: 0.25rem;
padding-bottom: 0.25rem; padding-bottom: 0.25rem;
@ -127,7 +97,6 @@
} }
h3 { h3 {
// border-bottom: 1px solid rgba($gray, 0.3);
font-size: 0.8rem; font-size: 0.8rem;
letter-spacing: 0.1rem; letter-spacing: 0.1rem;
margin-bottom: 0.25rem; margin-bottom: 0.25rem;
@ -140,10 +109,6 @@
margin-bottom: 1rem; margin-bottom: 1rem;
} }
ol, ul {
// padding-left: 1rem;
}
table { table {
border: 1px solid rgba($white, 0.1); border: 1px solid rgba($white, 0.1);
border-radius: 0.3rem; border-radius: 0.3rem;

View file

@ -6,6 +6,7 @@
const dedent = require("dedent"); const dedent = require("dedent");
const fetch = require("make-fetch-happen").defaults({ cacheManager: "./cache" }); const fetch = require("make-fetch-happen").defaults({ cacheManager: "./cache" });
const fs = require("graceful-fs");
const html = require("choo-async/html"); const html = require("choo-async/html");
const raw = require("nanohtml/raw"); const raw = require("nanohtml/raw");
@ -16,6 +17,8 @@ const apiFileLink = process.env.NODE_ENV === "development" ?
"https://cdn.rawgit.com/lbryio/lbry/5b3103e4/docs/api.json" "https://cdn.rawgit.com/lbryio/lbry/5b3103e4/docs/api.json"
; ;
const apiScripts = "<script>" + fs.readFileSync("./views/partials/api-scripts.js", "utf-8") + "</script>";
// E X P O R T // E X P O R T
@ -34,6 +37,8 @@ module.exports = exports = () => async () => parseApiFile().then(response => htm
<section class="api__content" id="toc-content">${raw(createApiContent(response).join(""))}</section> <section class="api__content" id="toc-content">${raw(createApiContent(response).join(""))}</section>
</div> </div>
${raw(apiScripts)}
`); `);
@ -46,9 +51,9 @@ function createApiContent(apiDetails) {
for (const apiDetail of apiDetails) { for (const apiDetail of apiDetails) {
const apiDetailsReturns = JSON.parse(JSON.stringify(apiDetail.returns)); const apiDetailsReturns = JSON.parse(JSON.stringify(apiDetail.returns));
/* if (apiDetail.name !== "settings_set") */ apiContent.push(` apiContent.push(`
<div class="api__content__body"> <div class="api__content__body">
<h2 id="#${apiDetail.name}">${apiDetail.name}</h2> <h2 id="${apiDetail.name}">${apiDetail.name}</h2>
<p>${apiDetail.description}</p> <p>${apiDetail.description}</p>
<h3>Returns</h3> <h3>Returns</h3>

View file

@ -0,0 +1,37 @@
/* global $, Jets */ "use strict";
let jets = new Jets({
searchTag: "#input-search",
contentTag: "#toc"
});
$("#input-search")[0].value = ""; // reset on page load
$("#input-search").on("keyup", () => {
if ($("#input-search").val()) $(".api__toc__search__clear").addClass("active");
else $(".api__toc__search__clear").removeClass("active");
});
$(".api__toc__search__clear").on("click", () => {
$("#input-search")[0].value = "";
$(".api__toc__search__clear").removeClass("active");
jets.destroy();
reinitJets();
});
// H E L P E R
function reinitJets() {
jets = new Jets({
searchTag: "#input-search",
contentTag: "#toc"
});
$("#input-search").focus();
}

View file

@ -55,6 +55,7 @@ module.exports = exports = () => async (state) => {
html`<link rel="stylesheet" href="/assets/css/style.css"/>`, html`<link rel="stylesheet" href="/assets/css/style.css"/>`,
html`<script src="/assets/scripts/vendor/zepto.js"></script>`, html`<script src="/assets/scripts/vendor/zepto.js"></script>`,
pageTitle === "API" ? html`<script src="/assets/scripts/plugins/jets.js"></script>` : "",
html`<script>const ws = new WebSocket(location.origin.replace(/^http/, "ws"));</script>`, html`<script>const ws = new WebSocket(location.origin.replace(/^http/, "ws"));</script>`,
html`<script src="/assets/scripts/sockets.js"></script>` html`<script src="/assets/scripts/sockets.js"></script>`
]}`; ]}`;