aboutsummaryrefslogtreecommitdiffstats
path: root/searcher
diff options
context:
space:
mode:
Diffstat (limited to 'searcher')
-rw-r--r--searcher/index.html150
-rw-r--r--searcher/script.js270
-rw-r--r--searcher/style.css237
3 files changed, 657 insertions, 0 deletions
diff --git a/searcher/index.html b/searcher/index.html
new file mode 100644
index 0000000..f6ae0a1
--- /dev/null
+++ b/searcher/index.html
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<html lang="en-us">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width">
+ <meta name="description" content="A simple utility to help you work out what chips do.">
+ <meta name="author" content="@✨Aleteoryx✨#1027">
+ <title>CV2 Chip Searcher</title>
+ <link crossorigin href="style.css" rel="stylesheet" type="text/css" />
+ <link crossorigin href="/lib/chips.css" rel="stylesheet" type="text/css" />
+
+ <link crossorigin href="/lib/util.js" rel="preload" as="script"/>
+ <link crossorigin href="/lib/chips.js" rel="preload" as="script"/>
+ <link crossorigin href="script.js" rel="preload" as="script"/>
+ <link crossorigin href="https://cdnjs.cloudflare.com/ajax/libs/fuse.js/6.4.6/fuse.min.js" rel="preload" as="script"/>
+ <link crossorigin href="/lib/chips.css" rel="preload" as="style"/>
+ <link crossorigin href="style.css" rel="preload" as="style"/>
+ <link crossorigin href="/terms.json" rel="preload" as="fetch"/>
+ <link crossorigin href="/circuitsv2.json" rel="preload" as="fetch"/>
+ <link crossorigin href="https://raw.githubusercontent.com/tyleo-rec/CircuitsV2Resources/master/misc/circuitsv2.json" rel="preload" as="fetch"/>
+
+ <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/fuse.js/6.4.6/fuse.min.js"></script>
+ <script crossorigin src="/lib/util.js"></script>
+ <script crossorigin src="/lib/chips.js"></script>
+ <script crossorigin src="script.js"></script>
+ <noscript><style> #form, hr, #listcontrols, #helpbox, #resultslist { display: none } </style></noscript>
+ <datalist id="paletteSearch">
+ </datalist>
+ </head>
+ <body>
+ <noscript>
+ The chip searcher requires JavaScript to function.
+ If you cannot enable it, you can try
+ <kbd>Crtl</kbd>+<kbd>F</kbd>ing your way through
+ <a href="https://raw.githubusercontent.com/tyleo-rec/CircuitsV2Resources/master/misc/circuitsv2.json">
+ the actual chip JSON file
+ </a>.
+
+ </noscript>
+ <div id="imgbox" style="display:none;"></div>
+ <form id="form" autocomplete="off">
+ <input type="text" id="search" placeholder="Search for a chip." list="paletteSearch"/>
+ <hr/>
+ <details>
+ <summary>Options</summary>
+ <div>
+ <input type="checkbox" name="refresh" id="autorefresh" checked/>
+ <label for="autorefresh">Auto Refresh</label>
+ <input type="checkbox" name="beta" id="beta" checked/>
+ <label for="beta">Show Beta Chips</label>
+ <input type="checkbox" name="depr" id="deprecated"/>
+ <label for="deprecated">Show Deprecated Chips</label>
+ <input type="checkbox" name="filterSug" id="fsug" checked>
+ <label for="fsug">Enable Filter Suggestions</label>
+ </div>
+ <hr/>
+ <div>
+ <p>Fuzzy Finder:</p>
+ <input type="radio" name="fuzziness" value='{"thresh":0,"nummod":1}' id="fuzzy0"/>
+ <label for="fuzzy0">Exact Match</label>
+ <input type="radio" name="fuzziness" value='{"thresh":0.3,"nummod":0.95}' id="fuzzy1" checked/>
+ <label for="fuzzy1">Small Typos</label>
+ <input type="radio" name="fuzziness" value='{"thresh":0.6,"nummod":0.9}' id="fuzzy2"/>
+ <label for="fuzzy2">Loose Match</label>
+ </div>
+ <hr/>
+ <div>
+ <p>Search By:</p>
+ <input type="checkbox" name="ReadonlyPaletteName" id="key0" checked/>
+ <label for="key0">Chip Name</label>
+ <input type="checkbox" name="Description" id="key1"/>
+ <label for="key1">Chip Description</label>
+ </div>
+ <hr/>
+ <div>
+ <label for="cfgitems">Items Per Page: </label>
+ <input type="number" name="items" id="cfgitems" value="12" step="1">
+ </div>
+ </details>
+ </form>
+ <hr/>
+ <div id="listcontrols">
+ <button id="first" onclick="first()">&lt;&lt;</button>
+ <button id="prev" onclick="prev()">&lt;</button>
+ <span>Showing page <span id="pagen">0</span>/<span id="of">0</span>.</span>
+ <button id="next" onclick="next()">&gt;</button>
+ <button id="last" onclick="last()">&gt;&gt;</button>
+ </div>
+ <hr/>
+ <div id="resultslist"></div>
+ <p id="grapherlink">Also check out <a href="/grapher">The graphing tool</a>!</p>
+ <p>Maintained by <a href="https://rec.net/user/winrg" target="_blank" rel="noopener noreferrer">@winrg</a>/<code>@✨Aleteoryx, Keeper of Names✨#1027</code></p>
+ <details id="helpbox">
+ <summary>?</summary>
+ <div>
+ <!-- Unused tabbing system. Ignore.
+ <input type="radio" name="test" class="selecttab" checked/>
+ <p class="tab">text for button 1</p>
+ <input type="radio" name="test" class="selecttab"/>
+ <p class="tab">text for button 2</p>
+ <input type="radio" name="test" class="selecttab"/>
+ <p class="tab">text for button 3</p>
+ <input type="radio" name="test" class="selecttab"/>
+ <p class="tab">text for button 4</p>-->
+ <h2>The Basics</h2>
+ <hr/>
+ <p>The chip searcher is a simple program.
+ To get started, simply type the name of the chip you'd like to know about into the top box.
+ By default, the searcher will automatically refresh the results as you type.</p>
+ <h4>Different types of chips</h4>
+ <hr>
+ <ul>
+ <li><b>Beta Chips</b> are indicated by a β symbol.</li>
+ <li><b>Soon-to-be deprecated Chips</b> are indicated by yellow text, as well as a warning.</li>
+ <li><b>Deprecated Chips</b> are indicated by red text, as well as a warning.</li>
+ </ul>
+ <p>Neither form of deprecated chip will display by default.</p>
+ <h2>Options</h2>
+ <hr/>
+ <p>The chip searcher supports the following configuration
+ options, contained in the fold-out menu below the search bar.</p>
+ <h4>Toggles</h4>
+ <hr/>
+ <ul>
+ <li><b>Auto Refresh</b>: Automatically update search results as you type.
+ When disabled, hit <kbd>Enter</kbd> to refresh.</li>
+ <li><b>Show Beta Chips</b>: When enabled, beta chips will be included in the search.</li>
+ <li><b>Show Deprecated Chips</b>: When enabled, deprecated chips will be included in the search.</li>
+ <li><b>Enable Filter Suggestions</b>: When enabled, filters will be displayed as autocomplete options.</li>
+ </ul>
+ <h4>Fuzzy Finder</h4>
+ <hr/>
+ <ul>
+ <li><b>Exact Match</b>: What is typed must appear identically in the results.</li>
+ <li><b>Small Typos</b>: Small errors are ignored, though larger ones will still affect the results.</li>
+ <li><b>Loose Match</b>: Really only useful if you can't spell.</li>
+ </ul>
+ <h4>Search By</h4>
+ <hr/>
+ <ul>
+ <li><b>Name</b>: Search for what is typed in the chip name.</li>
+ <li><b>Description</b>: Search for what is typed in the chip description</li>
+ </ul>
+ <h4>Items Per Page</h4>
+ <hr/>
+ <p>Configures the number of items that will appear in each page. Defaults to 12.</p>
+ </div>
+ </details>
+ </body>
+</html> \ No newline at end of file
diff --git a/searcher/script.js b/searcher/script.js
new file mode 100644
index 0000000..d80cd8f
--- /dev/null
+++ b/searcher/script.js
@@ -0,0 +1,270 @@
+const isNested = Boolean(window.parent != window);
+
+const deprecationStrings = {
+ "Active": {Text: "", Class: "hide"},
+ "": {Text: "Warning! This chip is being deprecated. It will be broken in the near future, and should not be used.", Class: "depwarn"},
+ "Deprecated": {Text: "Warning! This chip is currently deprecated. It is no longer in the palette, and likely does not work.", Class: "depbad"}
+};
+const fusenameopts = {
+ isCaseSensitive: false,
+ minMatchCharLength: 1,
+ ignoreLocation: true,
+ threshold: 0,
+ keys: ["ReadonlyPaletteName", "ReadonlyChipName"],
+ includeScore: true
+};
+const fusedescopts = {
+ isCaseSensitive: false,
+ minMatchCharLength: 1,
+ ignoreLocation: true,
+ threshold: 0,
+ keys: ["Description"],
+ includeScore: true
+};
+
+function genExplanation(name) {
+ return name;
+}
+
+function genSearchPath(path, name) {
+ const ret = document.createElement("li");
+ ret.classList.add("searchpath");
+ ret.innerText = path.join("/") + "/" + name;
+ return ret;
+}
+
+function getChipAddListener(GUID) {
+ return function(e) {
+ window.parent.postMessage(
+ {
+ type: 'newChip',
+ GUID: GUID
+ }
+ );
+ }
+}
+
+var searchresults = [];
+var page = 0
+
+window.addEventListener("load", async (e) => {
+ const search = document.getElementById("search");
+ const form = document.getElementById("form");
+ const suggestions = document.getElementById("paletteSearch");
+
+ if (isNested) document.getElementById("grapherlink").remove();
+
+ if (localStorage.length) {
+ form.depr.checked = localStorage.getItem("depr");
+ form.beta.checked = localStorage.getItem("beta");
+ form.ReadonlyPaletteName.checked = localStorage.getItem("ReadonlyPaletteName");
+ form.Description.checked = localStorage.getItem("Description");
+ form.refresh.checked = localStorage.getItem("refresh");
+ form.fuzziness.value = localStorage.getItem("fuzz");
+ form.items.value = localStorage.getItem("items");
+ form.filterSug.checked = localStorage.getItem("fsug");
+ }
+
+ let v2pr = fetch(/*"https://raw.githubusercontent.com/tyleo-rec/CircuitsV2Resources/master/misc/circuitsv2.json"/*/"/circuitsv2.json")
+ .then(res => res.json());
+ let termspr = fetch("/terms.json")
+ .then(res => res.json());
+
+ [v2json, termsjson] = await Promise.all([v2pr, termspr]);
+ //console.log(v2json, termsjson);
+
+
+ //GUID needs to be in the same object now, because we convert to an array
+ const searchable = Object.entries(v2json.Nodes)
+ .map(pair => {return {GUID: pair[0], ...pair[1]}})
+ searchable.sort((a, b) => (a.ReadonlyPaletteName.toLowerCase() > b.ReadonlyPaletteName.toLowerCase()) ? 1 : -1);
+
+ const fuses = [new Fuse(searchable, fusenameopts),
+ new Fuse(searchable, fusedescopts)];
+
+ const filterTree = {nodes: searchable};
+ searchable.forEach(chip => {
+ chip.NodeFilters.forEach(filter => {
+ var currentNode = filterTree;
+ filter.FilterPath.forEach(path => {
+ if (!currentNode[path.toUpperCase()]) currentNode[path.toUpperCase()] = {nodes: []};
+ currentNode = currentNode[path.toUpperCase()]
+ if (!currentNode.nodes.includes(chip))currentNode.nodes.push(chip);
+ currentNode.actualname = path;
+ })
+ })
+ })
+
+ //console.log(filterTree);
+
+ const redraw = targ => {
+ const fuseinuse = fuses.filter(fuse => form[fuse.options.keys[0]].checked);
+
+ start = performance.now();
+
+ localStorage.setItem("depr", form.depr.checked ? "set" : "");
+ localStorage.setItem("beta", form.beta.checked ? "set" : "");
+ localStorage.setItem("ReadonlyPaletteName", form.ReadonlyPaletteName.checked ? "set" : "");
+ localStorage.setItem("Description", form.Description.checked ? "set" : "");
+ localStorage.setItem("refresh", form.refresh.checked ? "set" : "");
+ localStorage.setItem("fsug", form.filterSug.checked ? "set" : "");
+ localStorage.setItem("fuzz", form.fuzziness.value);
+ localStorage.setItem("items", form.items.value);
+
+ suggestions.id = form.filterSug.checked ? "paletteSearch" : ""
+
+ var content2 = filterTree;
+
+ const fullPath = targ.value.replace(/\\+|\/+/, "/")
+ .split("/");
+ const startOfPath = fullPath.slice(0, -1);
+ const endOfPath = fullPath.at(-1);
+ const startOfSuggestion = startOfPath.join('/');
+
+ startOfPath.forEach(path => {
+ try {
+ content2 = content2[path.toUpperCase()]
+ } catch (e) {}
+ });
+
+ if (!content2) content2 = {nodes: []};
+
+ suggestions.innerText = '';
+ suggestions.append(...Object.keys(content2)
+ .filter(i => i.toUpperCase() == i)
+ .sort((a, b) => (a > b) ? 1 : -1)
+ .map(i => {
+ var e = document.createElement('option');
+ e.value = startOfSuggestion + (startOfSuggestion ? '/' : '') + content2[i].actualname + '/';
+ return e;
+ })
+ )
+
+
+ fuseinuse.forEach(fuse => {
+ fuse.options.minMatchCharLength = Math.floor(endOfPath.length * JSON.parse(form.fuzziness.value).nummod);
+ fuse.options.threshold = JSON.parse(form.fuzziness.value).thresh;
+ fuse.setCollection(content2.nodes);
+ });
+
+ var content = endOfPath ? (() => {
+ let m = fuseinuse.map(fuse => fuse.search(endOfPath)).flat()
+ m.sort((a,b) => a.score - b.score);
+ m = Array.from(new Set(m.map(e => e.item)));
+ return m;
+ })() : content2.nodes;
+ //console.log(content2.nodes)
+
+ content = content.map(el => {
+ if ((form.depr.checked || el.DeprecationStage == 0) && (form.beta.checked || !el.IsBetaChip)) {
+ const ret = newEl("details", "returnedchip");
+ if (el.IsBetaChip) ret.classList.add("betaChip");
+
+ const iret = newEl("div", "infocontainer");
+
+ const name = document.createElement("summary");
+ name.innerText = el.ReadonlyPaletteName +
+ ((el.ReadonlyPaletteName == el.ReadonlyChipName) ? "" : ` - aka "${el.ReadonlyChipName}"`);
+
+ const deprinfo = deprecationStrings[el.DeprecationStage];
+ const depr = newEl("div", deprinfo.Class);
+ depr.innerText = deprinfo.Text;
+
+ const desc = newEl("p", "chipdesc")
+ desc.innerText = el.Description == "" ? "No Description!" : el.Description;
+
+ const filters = newEl("ul", "filters")
+ filters.append(...Object.values(el.NodeFilters).map(val => genSearchPath(val.FilterPath, el.ReadonlyPaletteName)));
+
+ iret.append(desc, filters);
+
+ const addBtn = newEl('button', 'addBtn');
+ addBtn.innerText = "+";
+ addBtn.setAttribute('title', 'Add to graph');
+ addBtn.onclick = getChipAddListener(el.GUID);
+ if (isNested) name.append(addBtn);
+
+ let m = generateChipHTML(el.NodeDescs);
+
+ ret.append(depr, name, iret, m);
+
+ return ret;
+ }
+ }).filter(e => e != undefined);
+ var perfstring = `returned in ${parseInt(performance.now() - start)} ms.`
+ display(perfstring, content);
+ };
+
+ const redrawHandler = e => redraw(search);
+
+ form.addEventListener("submit", e => {console.log(e); e.preventDefault()});
+ search.addEventListener("change", redrawHandler);
+ form.addEventListener("input", redrawHandler);
+
+ form.refresh.addEventListener("change", e => {
+ if (e.target.checked) {
+ form.addEventListener("input", redrawHandler);
+ } else {
+ form.removeEventListener("input", redrawHandler);
+ }
+ });
+
+ try {
+ redraw(search);
+ } catch (error) {
+ setTimeout(() => redraw(search), 1000);
+ }
+});
+
+function display(perf, content) {
+ searchresults = content;
+ page = 0;
+ update();
+}
+
+function first() {
+ page = 0;
+ update();
+}
+function prev() {
+ page = Math.max(page - 1, 0);
+ update();
+}
+
+function next() {
+ const pagesize = parseInt(document.getElementById("cfgitems").value);
+
+ page = (((page + 1) * pagesize) > searchresults.length) ? page : (page + 1);
+ update();
+}
+
+function last() {
+ const pagesize = parseInt(document.getElementById("cfgitems").value);
+
+ page = Math.max(parseInt((searchresults.length - 1) / pagesize), 0);
+ update();
+}
+
+
+function update() {
+ const ndisplay = document.getElementById("pagen");
+ const ofdisplay = document.getElementById("of");
+ const output = document.getElementById("resultslist");
+ const pagesel = document.getElementById("cfgitems");
+
+ //pagesel.value = ;
+
+ const pagesize = pagesel.value == "" ? 1 : Math.max(parseInt(pagesel.value), 1);
+
+ const start = page * pagesize;
+ const end = Math.min(searchresults.length, start + pagesize);
+
+ ndisplay.innerText = page + 1;
+ ofdisplay.innerText = parseInt((searchresults.length - 1) / pagesize) + 1;
+
+ //console.log(start, end);
+
+ output.innerHTML = "";
+ output.append(...searchresults.slice(start, end));
+}
+
diff --git a/searcher/style.css b/searcher/style.css
new file mode 100644
index 0000000..d45a04a
--- /dev/null
+++ b/searcher/style.css
@@ -0,0 +1,237 @@
+@import url('https://fonts.googleapis.com/css2?family=Raleway&display=swap');
+
+body {
+}
+html {
+ --foreforeground: #3788ae;
+ --foreground: #082f41;
+ --background: #03141c;
+ color: white;
+ background: var(--background);
+ /*padding: 1cm;*/
+ font-size: 12pt;
+ font-family: 'Raleway', sans-serif;
+ height: 100%;
+}
+
+hr {
+ border: 0.5mm solid var(--foreforeground);
+ background: var(--foreforeground);
+ width: 100%;
+ height: 0%;
+
+}
+
+.filters::before {
+ content: "Pallette Search paths:";
+}
+
+.hide {display: none;}
+
+input, button, details{
+ color: white;
+ background: var(--foreground);
+ padding: 1rem;
+
+ border: none;
+ border-radius: 0.5cm;
+ font-size: 13pt;
+ margin: 0.5rem;
+}
+
+input::placeholder {
+ color: var(--foreforeground);
+}
+
+details :is(input, button) {
+ background: var(--background);
+}
+
+/*input[type=checkbox] {
+ display: none;
+}
+
+input[type=checkbox] + label {
+ position: relative;
+ padding-left: 2.5rem;
+ padding-right: .5rem;
+}
+
+input[type=checkbox] + label::before {
+ background: var(--foreground);
+ width: 1.5rem;
+ height: 1.5rem;
+ margin: auto;
+ content: "";
+ position: absolute;
+ left: 0;
+ top: -0.33rem;
+ border-radius: 0.25rem;
+ border: 0.25rem var(--foreforeground) solid;
+}
+
+
+input[type=checkbox]:checked + label::before {
+ border-color: var(--foreground);
+ background: var(--foreforeground);
+ color: var(--foreground);
+ font-size: 16pt;
+ /*content: "✓";* /
+ align-content: center;
+}*/
+
+details {
+ padding: 1rem;
+ background: var(--foreground);
+}
+
+details.betaChip > summary::before {
+ content: "β ";
+ color: paleturquoise;
+}
+
+.returnedchip > summary {
+ position: relative;
+}
+
+.returnedchip > summary > button.addBtn {
+ padding: 0rem;
+ margin: 0;
+ height: 2rem;
+ width: 2rem;
+ position: absolute;
+ top: calc(50% - 1rem);
+ right: 0;
+ left: calc(100% - 2rem);
+ bottom: calc(50% - 1rem);
+ text-align: center;
+ background: #0000;
+ font-size: 2rem;
+}
+
+details[open] > summary {
+ border-bottom: 1mm var(--foreforeground) solid;
+ margin-bottom: 0.5rem;
+}
+
+body {
+ display: flex;
+ flex-flow: column;
+ height: calc(100% - 2cm);
+ margin: 1cm;
+}
+
+#resultslist {
+ overflow: auto;
+}
+
+#listcontrols {
+ display: flex;
+ flex-flow: row;
+ align-items: center;
+}
+
+
+#helpbox > summary {
+ height: 1.5cm;
+ width: 1.5cm;
+ display: flex;
+ background: #0000;
+ border: #fff7 solid 0.9mm;
+ color: #fff7;
+ border-radius: 1.5cm;
+ padding: 0;
+ font-size: 1.5cm;
+ align-items: center;
+ justify-content: center;
+ float: left;
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ user-select: none;
+}
+#helpbox[open] > summary {
+ margin-bottom: 0;
+ background: #fff7;
+ border: #0000 solid 0.9mm;
+ color: #fff;
+}
+
+#helpbox {
+ position: fixed;
+ bottom: 2rem;
+ right: 2rem;
+ background: #0000;
+ border-radius: 0;
+}
+
+#helpbox > div {
+ background: var(--foreground);
+ border-radius: 0.5cm;
+ height: 60vh;
+ width: 10cm;
+ overflow-x: auto;
+ padding: 1rem;
+ position: relative;
+ bottom: 2cm;
+ box-shadow: black 1mm 1mm 3mm
+}
+a:link {
+ color: white;
+}
+a:visited {
+ color: var(--foreforeground);
+}
+
+.depbad, .depbad + summary {
+ color: palevioletred;
+}
+.depwarn, .depwarn + summary {
+ color: palegoldenrod;
+}
+
+kbd {
+ background: white;
+ color: black;
+ padding: 0.5mm;
+ border-radius: 1mm;
+ box-shadow: inset black -0.25mm -0.25mm 1mm;
+}
+
+input:not(:checked).selecttab + .tab {
+ display: none;
+}
+
+.tab {
+ position: absolute;
+ top: 2rem;
+}
+
+summary,
+button,
+:is(input[type=radio], input[type=checkbox]),
+:is(input[type=radio], input[type=checkbox]) + label {
+ cursor: pointer;
+}
+
+
+@media (orientation: portrait) {
+ #listcontrols {
+ justify-content: space-between;
+ }
+ #form > details {
+ overflow: auto;
+ max-height: 20vh;
+ }
+ label::after {
+ content: "\a";
+ white-space: pre;
+ }
+ input[type=text] {
+ width: calc(100vw - 3.25cm);
+ }
+ #helpbox > div {
+ width: calc(100vw - 3.5cm);
+ height: 60vh;
+ }
+} \ No newline at end of file