aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbackend/server.py202
-rw-r--r--src/App.vue6
-rw-r--r--src/components/AncestryBar.vue2
-rw-r--r--src/components/SearchModal.vue9
-rw-r--r--src/components/Tile.vue4
-rw-r--r--src/components/TileInfoModal.vue40
-rw-r--r--src/layout.ts2
-rw-r--r--src/lib.ts38
-rw-r--r--src/tol.ts25
9 files changed, 178 insertions, 150 deletions
diff --git a/backend/server.py b/backend/server.py
index 5525eb5..888f73a 100755
--- a/backend/server.py
+++ b/backend/server.py
@@ -1,10 +1,10 @@
#!/usr/bin/python3
-import sys, re, sqlite3, json
+import sys, re, sqlite3
import os.path
from http.server import HTTPServer, BaseHTTPRequestHandler
import urllib.parse
-import gzip
+import gzip, jsonpickle
hostname = "localhost"
port = 8000
@@ -26,60 +26,96 @@ if len(sys.argv) > 1:
print(usageInfo, file=sys.stderr)
sys.exit(1)
+# Classes for objects sent as responses (matches lib.ts types in client-side code)
+class TolNode:
+ """ Used when responding to 'node' and 'chain' requests """
+ def __init__(self, otolId, children, parent=None, tips=0, pSupport=False, commonName=None, imgName=None):
+ self.otolId = otolId # string | null
+ self.children = children # string[]
+ self.parent = parent # string | null
+ self.tips = tips # number
+ self.pSupport = pSupport # boolean
+ self.commonName = commonName # null | string
+ self.imgName = imgName # null | string | [string,string] | [null, string] | [string, null]
+class SearchSugg:
+ """ Represents a search suggestion """
+ def __init__(self, name, canonicalName=None):
+ self.name = name # string
+ self.canonicalName = canonicalName # string | null
+class SearchSuggResponse:
+ """ Sent as responses to 'search' requests """
+ def __init__(self, searchSuggs, hasMore):
+ self.suggs = searchSuggs # SearchSugg[]
+ self.hasMore = hasMore # boolean
+class DescInfo:
+ """ Represents a tol-node's associated description """
+ def __init__(self, text, wikiId, fromRedirect, fromDbp):
+ self.text = text # string
+ self.wikiId = wikiId # number
+ self.fromRedirect = fromRedirect # boolean
+ self.fromDbp = fromDbp # boolean
+class ImgInfo:
+ """ Represents a tol-node's associated image """
+ def __init__(self, id, src, url, license, artist, credit):
+ self.id = id # number
+ self.src = src # string
+ self.url = url # string
+ self.license = license # string
+ self.artist = artist # string
+ self.credit = credit # string
+class InfoResponse:
+ """ Sent as responses to 'info' requests """
+ def __init__(self, tolNode, descData, imgData):
+ self.tolNode = tolNode # null | TolNode
+ self.descData = descData # null | DescInfo | [DescInfo, DescInfo]
+ self.imgData = imgData # null | ImgInfo | [ImgInfo, ImgInfo]
+
# Connect to db
dbCon = sqlite3.connect(dbFile)
# Some functions
def lookupNodes(names, useReducedTree):
# Get node info
- nodeObjs = {}
+ nameToNodes = {}
cur = dbCon.cursor()
nodesTable = "nodes" if not useReducedTree else "r_nodes"
edgesTable = "edges" if not useReducedTree else "r_edges"
queryParamStr = ",".join(["?"] * len(names))
query = f"SELECT name, id, tips FROM {nodesTable} WHERE name IN ({queryParamStr})"
for (nodeName, otolId, tips) in cur.execute(query, names):
- nodeObjs[nodeName] = {
- "otolId": otolId,
- "children": [],
- "parent": None,
- "tips": tips,
- "pSupport": False,
- "commonName": None,
- "imgName": None,
- }
+ nameToNodes[nodeName] = TolNode(otolId, [], tips=tips)
# Get child info
query = f"SELECT node, child FROM {edgesTable} WHERE node IN ({queryParamStr})"
for (nodeName, childName) in cur.execute(query, names):
- nodeObjs[nodeName]["children"].append(childName)
+ nameToNodes[nodeName].children.append(childName)
# Order children by tips
- for (nodeName, nodeObj) in nodeObjs.items():
- childList = nodeObj["children"]
+ for (nodeName, node) in nameToNodes.items():
childToTips = {}
- query = "SELECT name, tips FROM {} WHERE name IN ({})".format(nodesTable, ",".join(["?"] * len(childList)))
- for (n, tips) in cur.execute(query, childList):
+ query = "SELECT name, tips FROM {} WHERE name IN ({})"
+ query = query.format(nodesTable, ",".join(["?"] * len(node.children)))
+ for (n, tips) in cur.execute(query, node.children):
childToTips[n] = tips
- childList.sort(key=lambda n: childToTips[n], reverse=True)
+ node.children.sort(key=lambda n: childToTips[n], reverse=True)
# Get parent info
query = f"SELECT node, child, p_support FROM {edgesTable} WHERE child IN ({queryParamStr})"
for (nodeName, childName, pSupport) in cur.execute(query, names):
- nodeObjs[childName]["parent"] = None if nodeName == "" else nodeName
- nodeObjs[childName]["pSupport"] = (pSupport == 1)
+ nameToNodes[childName].parent = nodeName
+ nameToNodes[childName].pSupport = (pSupport == 1)
# Get image names
- idsToNames = {nodeObjs[n]["otolId"]: n for n in nodeObjs.keys()}
+ idsToNames = {nameToNodes[n].otolId: n for n in nameToNodes.keys()}
query = "SELECT nodes.id from nodes INNER JOIN node_imgs ON nodes.name = node_imgs.name" \
" WHERE nodes.id IN ({})".format(",".join(["?"] * len(idsToNames)))
for (otolId,) in cur.execute(query, list(idsToNames.keys())):
- nodeObjs[idsToNames[otolId]]["imgName"] = otolId + ".jpg"
+ nameToNodes[idsToNames[otolId]].imgName = otolId + ".jpg"
# Get 'linked' images for unresolved names
- unresolvedNames = [n for n in nodeObjs if nodeObjs[n]["imgName"] == None]
+ unresolvedNames = [n for n in nameToNodes if nameToNodes[n].imgName == None]
query = "SELECT name, otol_ids from linked_imgs WHERE name IN ({})"
query = query.format(",".join(["?"] * len(unresolvedNames)))
for (name, otolIds) in cur.execute(query, unresolvedNames):
if "," not in otolIds:
- nodeObjs[name]["imgName"] = otolIds + ".jpg"
+ nameToNodes[name].imgName = otolIds + ".jpg"
else:
id1, id2 = otolIds.split(",")
- nodeObjs[name]["imgName"] = [
+ nameToNodes[name].imgName = [
id1 + ".jpg" if id1 != "" else None,
id2 + ".jpg" if id2 != "" else None,
]
@@ -87,9 +123,9 @@ def lookupNodes(names, useReducedTree):
query = f"SELECT name, alt_name FROM names WHERE pref_alt = 1 AND name IN ({queryParamStr})"
for (name, altName) in cur.execute(query, names):
if altName != name:
- nodeObjs[name]["commonName"] = altName
+ nameToNodes[name].commonName = altName
#
- return nodeObjs
+ return nameToNodes
def lookupName(name, useReducedTree):
cur = dbCon.cursor()
results = []
@@ -108,86 +144,80 @@ def lookupName(name, useReducedTree):
" names INNER JOIN r_nodes ON names.name = r_nodes.name" \
" WHERE alt_name LIKE ? ORDER BY length(alt_name) LIMIT ?"
# Join results, and get shortest
- temp = []
- for row in cur.execute(query1, (name + "%", SEARCH_SUGG_LIMIT + 1)):
- temp.append({"name": row[0], "canonicalName": None})
- for row in cur.execute(query2, (name + "%", SEARCH_SUGG_LIMIT + 1)):
- temp.append({"name": row[0], "canonicalName": row[1]})
+ suggs = []
+ for (nodeName,) in cur.execute(query1, (name + "%", SEARCH_SUGG_LIMIT + 1)):
+ suggs.append(SearchSugg(nodeName))
+ for (altName, nodeName) in cur.execute(query2, (name + "%", SEARCH_SUGG_LIMIT + 1)):
+ suggs.append(SearchSugg(altName, nodeName))
# If insufficient results, try substring-search
- foundNames = {n["name"] for n in temp}
- if len(temp) < SEARCH_SUGG_LIMIT:
- newLim = SEARCH_SUGG_LIMIT + 1 - len(temp)
- for (altName,) in cur.execute(query1, ("%" + name + "%", newLim)):
- if altName not in foundNames:
- temp.append({"name": altName, "canonicalName": None})
- foundNames.add(altName)
- if len(temp) < SEARCH_SUGG_LIMIT:
- newLim = SEARCH_SUGG_LIMIT + 1 - len(temp)
- for (altName, cName) in cur.execute(query2, ("%" + name + "%", SEARCH_SUGG_LIMIT + 1)):
+ foundNames = {n.name for n in suggs}
+ if len(suggs) < SEARCH_SUGG_LIMIT:
+ newLim = SEARCH_SUGG_LIMIT + 1 - len(suggs)
+ for (nodeName,) in cur.execute(query1, ("%" + name + "%", newLim)):
+ if nodeName not in foundNames:
+ suggs.append(SearchSugg(nodeName))
+ foundNames.add(nodeName)
+ if len(suggs) < SEARCH_SUGG_LIMIT:
+ newLim = SEARCH_SUGG_LIMIT + 1 - len(suggs)
+ for (altName, nodeName) in cur.execute(query2, ("%" + name + "%", SEARCH_SUGG_LIMIT + 1)):
if altName not in foundNames:
- temp.append({"name": altName, "canonicalName": cName})
+ suggs.append(SearchSugg(altName, nodeName))
foundNames.add(altName)
#
- temp.sort(key=lambda x: x["name"])
- temp.sort(key=lambda x: len(x["name"]))
- results = temp[:SEARCH_SUGG_LIMIT]
- if len(temp) > SEARCH_SUGG_LIMIT:
+ suggs.sort(key=lambda x: x.name)
+ suggs.sort(key=lambda x: len(x.name))
+ results = suggs[:SEARCH_SUGG_LIMIT]
+ if len(suggs) > SEARCH_SUGG_LIMIT:
hasMore = True
- return [results, hasMore]
+ return SearchSuggResponse(results, hasMore)
def lookupNodeInfo(name, useReducedTree):
cur = dbCon.cursor()
# Get node-object info
- temp = lookupNodes([name], useReducedTree)
- nodeObj = temp[name] if name in temp else None
+ nameToNodes = lookupNodes([name], useReducedTree)
+ tolNode = nameToNodes[name] if name in nameToNodes else None
# Get node desc
descData = None
match = re.fullmatch(r"\[(.+) \+ (.+)]", name)
if match == None:
- query = "SELECT wiki_id, redirected, desc, from_dbp FROM" \
+ query = "SELECT desc, wiki_id, redirected, from_dbp FROM" \
" wiki_ids INNER JOIN descs ON wiki_ids.id = descs.wiki_id WHERE wiki_ids.name = ?"
row = cur.execute(query, (name,)).fetchone()
if row != None:
- descData = {"wikiId": row[0], "fromRedirect": row[1] == 1, "text": row[2], "fromDbp": row[3] == 1}
+ (desc, wikiId, redirected, fromDbp) = row
+ descData = DescInfo(desc, wikiId, redirected == 1, fromDbp == 1)
else:
# Get descs for compound-node element
descData = [None, None]
- query = "SELECT name, wiki_id, redirected, desc, from_dbp FROM" \
+ query = "SELECT name, desc, wiki_id, redirected, from_dbp FROM" \
" wiki_ids INNER JOIN descs ON wiki_ids.id = descs.wiki_id WHERE wiki_ids.name IN (?, ?)"
- for row in cur.execute(query, match.group(1,2)):
- if row[0] == match.group(1):
- descData[0] = {"wikiId": row[1], "fromRedirect": row[2] == 1, "text": row[3], "fromDbp": row[4] == 1}
- else:
- descData[1] = {"wikiId": row[1], "fromRedirect": row[2] == 1, "text": row[3], "fromDbp": row[4] == 1}
+ for (nodeName, desc, wikiId, redirected, fromDbp) in cur.execute(query, match.group(1,2)):
+ idx = 0 if nodeName == match.group(1) else 1
+ descData[idx] = DescInfo(desc, wikiId, redirected == 1, fromDbp == 1)
# Get img info
imgData = None
- if nodeObj != None:
- if isinstance(nodeObj["imgName"], str):
- otolId = nodeObj["imgName"][:-4] # Convert filename excluding .jpg suffix
+ if tolNode != None:
+ if isinstance(tolNode.imgName, str):
+ otolId = tolNode.imgName[:-4] # Convert filename excluding .jpg suffix
query = "SELECT images.id, images.src, url, license, artist, credit FROM" \
" nodes INNER JOIN node_imgs ON nodes.name = node_imgs.name" \
" INNER JOIN images ON node_imgs.img_id = images.id AND node_imgs.src = images.src" \
" WHERE nodes.id = ?"
(imgId, imgSrc, url, license, artist, credit) = cur.execute(query, (otolId,)).fetchone()
- imgData = {"imgId": imgId, "imgSrc": imgSrc,
- "url": url, "license": license, "artist": artist, "credit": credit}
- elif isinstance(nodeObj["imgName"], list):
+ imgData = ImgInfo(imgId, imgSrc, url, license, artist, credit)
+ elif isinstance(tolNode.imgName, list):
# Get info for compound-image parts
imgData = [None, None]
- idsToLookup = [n[:-4] for n in nodeObj["imgName"] if n != None]
+ idsToLookup = [n[:-4] for n in tolNode.imgName if n != None]
query = "SELECT nodes.id, images.id, images.src, url, license, artist, credit FROM" \
" nodes INNER JOIN node_imgs ON nodes.name = node_imgs.name" \
" INNER JOIN images ON node_imgs.img_id = images.id AND node_imgs.src = images.src" \
" WHERE nodes.id IN ({})".format(",".join(["?"] * len(idsToLookup)))
for (imgOtolId, imgId, imgSrc, url, license, artist, credit) in cur.execute(query, idsToLookup):
- imgDataVal = {"imgId": imgId, "imgSrc": imgSrc,
- "url": url, "license": license, "artist": artist, "credit": credit}
- imgName1 = nodeObj["imgName"][0]
- if imgName1 != None and imgOtolId == imgName1[:-4]:
- imgData[0] = imgDataVal
- else:
- imgData[1] = imgDataVal
+ imgName1 = tolNode.imgName[0]
+ idx = 0 if (imgName1 != None and imgOtolId == imgName1[:-4]) else 1
+ imgData[idx] = ImgInfo(imgId, imgSrc, url, license, artist, credit)
#
- return {"descData": descData, "imgData": imgData, "nodeObj": nodeObj}
+ return InfoResponse(tolNode, descData, imgData)
class DbServer(BaseHTTPRequestHandler):
def do_GET(self):
@@ -204,11 +234,11 @@ class DbServer(BaseHTTPRequestHandler):
useReducedTree = "tree" in queryDict
# Check query string
if reqType == "node":
- nodeObjs = lookupNodes([name], useReducedTree)
- if len(nodeObjs) > 0:
- nodeObj = nodeObjs[name]
- childNodeObjs = lookupNodes(nodeObj["children"], useReducedTree)
- childNodeObjs[name] = nodeObj
+ tolNodes = lookupNodes([name], useReducedTree)
+ if len(tolNodes) > 0:
+ tolNode = tolNodes[name]
+ childNodeObjs = lookupNodes(tolNode.children, useReducedTree)
+ childNodeObjs[name] = tolNode
self.respondJson(childNodeObjs)
return
elif reqType == "chain":
@@ -216,31 +246,31 @@ class DbServer(BaseHTTPRequestHandler):
ranOnce = False
while True:
# Get node
- nodeObjs = lookupNodes([name], useReducedTree)
- if len(nodeObjs) == 0:
+ tolNodes = lookupNodes([name], useReducedTree)
+ if len(tolNodes) == 0:
if not ranOnce:
self.respondJson(results)
return
print(f"ERROR: Parent-chain node {name} not found", file=sys.stderr)
break
- nodeObj = nodeObjs[name]
- results[name] = nodeObj
+ tolNode = tolNodes[name]
+ results[name] = tolNode
# Conditionally add children
if not ranOnce:
ranOnce = True
else:
childNamesToAdd = []
- for childName in nodeObj["children"]:
+ for childName in tolNode.children:
if childName not in results:
childNamesToAdd.append(childName)
childNodeObjs = lookupNodes(childNamesToAdd, useReducedTree)
results.update(childNodeObjs)
# Check if root
- if nodeObj["parent"] == None:
+ if tolNode.parent == None:
self.respondJson(results)
return
else:
- name = nodeObj["parent"]
+ name = tolNode.parent
elif reqType == "search":
self.respondJson(lookupName(name, useReducedTree))
return
@@ -249,7 +279,7 @@ class DbServer(BaseHTTPRequestHandler):
return
self.send_response(404)
def respondJson(self, val):
- content = json.dumps(val).encode("utf-8")
+ content = jsonpickle.encode(val, unpicklable=False).encode("utf-8")
self.send_response(200)
self.send_header("Content-type", "application/json")
if "accept-encoding" in self.headers and "gzip" in self.headers["accept-encoding"]:
diff --git a/src/App.vue b/src/App.vue
index cddb027..99f4018 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -14,8 +14,8 @@ import SearchIcon from './components/icon/SearchIcon.vue';
import PlayIcon from './components/icon/PlayIcon.vue';
import SettingsIcon from './components/icon/SettingsIcon.vue';
// Other
-import type {TolMap} from './tol';
-import {TolNode} from './tol';
+import type {TolMap} from './lib';
+import {TolNode} from './lib';
import {LayoutNode, initLayoutTree, initLayoutMap, tryLayout} from './layout';
import type {LayoutOptions, LayoutTreeChg} from './layout';
import {arraySum, randWeightedChoice, getScrollBarWidth} from './lib';
@@ -302,7 +302,7 @@ export default defineComponent({
},
onDetachedAncestorClick(layoutNode: LayoutNode, alsoCollapse = false){
if (!this.handleActionForTutorial('unhideAncestor')){
- return;
+ return Promise.resolve(false);
}
this.setLastFocused(null);
this.activeRoot = layoutNode;
diff --git a/src/components/AncestryBar.vue b/src/components/AncestryBar.vue
index 8f31900..a56568c 100644
--- a/src/components/AncestryBar.vue
+++ b/src/components/AncestryBar.vue
@@ -3,7 +3,7 @@ import {defineComponent, PropType} from 'vue';
import Tile from './Tile.vue'
import {LayoutNode} from '../layout';
import type {LayoutOptions} from '../layout';
-import type {TolMap} from '../tol';
+import type {TolMap} from '../lib';
// Displays a sequence of nodes, representing ancestors from a tree-of-life root to a currently-active root
export default defineComponent({
diff --git a/src/components/SearchModal.vue b/src/components/SearchModal.vue
index 9ea10c8..4ea5cea 100644
--- a/src/components/SearchModal.vue
+++ b/src/components/SearchModal.vue
@@ -3,10 +3,7 @@ import {defineComponent, PropType} from 'vue';
import SearchIcon from './icon/SearchIcon.vue';
import InfoIcon from './icon/InfoIcon.vue';
import {LayoutNode} from '../layout';
-import type {TolMap} from '../tol';
-
-type SearchSugg = {name: string, canonicalName: string | null}; // Represents a search string suggestion
-type SearchSuggResponse = [SearchSugg[], boolean]; // Holds search suggestions and an indication of if there was more
+import type {TolMap, SearchSugg, SearchSuggResponse} from '../lib';
// Displays a search box, and sends search requests
export default defineComponent({
@@ -107,8 +104,8 @@ export default defineComponent({
return response.json()
})
.then((results: SearchSuggResponse) => {
- this.searchSuggs = results[0];
- this.searchHasMoreSuggs = results[1];
+ this.searchSuggs = results.suggs;
+ this.searchHasMoreSuggs = results.hasMore;
this.focusedSuggIdx = null;
})
.catch(error => {
diff --git a/src/components/Tile.vue b/src/components/Tile.vue
index 9bbff51..29b4856 100644
--- a/src/components/Tile.vue
+++ b/src/components/Tile.vue
@@ -3,8 +3,8 @@ import {defineComponent, PropType} from 'vue';
import InfoIcon from './icon/InfoIcon.vue';
import {LayoutNode} from '../layout';
import type {LayoutOptions} from '../layout';
-import type {TolMap} from '../tol';
-import {TolNode} from '../tol';
+import type {TolMap} from '../lib';
+import {TolNode} from '../lib';
import {capitalizeWords} from '../lib';
// Displays one, or a hierarchy of, tree-of-life nodes, as a 'tile'
diff --git a/src/components/TileInfoModal.vue b/src/components/TileInfoModal.vue
index 45b3192..bd6cc5d 100644
--- a/src/components/TileInfoModal.vue
+++ b/src/components/TileInfoModal.vue
@@ -4,18 +4,10 @@ import CloseIcon from './icon/CloseIcon.vue';
import Tile from './Tile.vue'
import {LayoutNode} from '../layout';
import type {LayoutOptions} from '../layout';
-import type {TolMap} from '../tol';
-import {TolNode} from '../tol';
+import type {TolMap} from '../lib';
+import {TolNode, DescInfo, ImgInfo, TileInfoResponse} from '../lib';
import {capitalizeWords} from '../lib';
-type DescInfo = {text: string, fromRedirect: boolean, wikiId: number, fromDbp: boolean};
-type ImgInfo = {imgId: number, imgSrc: 'eol' | 'enwiki', url: string, license: string, artist: string, credit: string}
-type TileInfoResponse = {
- tolNode: null | TolNode,
- descData: null | DescInfo | [DescInfo, DescInfo],
- imgData: null | ImgInfo | [ImgInfo, ImgInfo],
-};
-
// Displays information about a tree-of-life node
export default defineComponent({
data(){
@@ -123,18 +115,16 @@ export default defineComponent({
fetch(url.toString())
.then(response => response.json())
.then(obj => {
- if (obj != null){
- this.tolNode = obj.nodeObj;
- if (!Array.isArray(obj.descData)){
- this.descInfo = obj.descData;
- } else {
- [this.descInfo1, this.descInfo2] = obj.descData;
- }
- if (!Array.isArray(obj.imgData)){
- this.imgInfo = obj.imgData;
- } else {
- [this.imgInfo1, this.imgInfo2] = obj.imgData;
- }
+ this.tolNode = obj.tolNode;
+ if (!Array.isArray(obj.descData)){
+ this.descInfo = obj.descData;
+ } else {
+ [this.descInfo1, this.descInfo2] = obj.descData;
+ }
+ if (!Array.isArray(obj.imgData)){
+ this.imgInfo = obj.imgData;
+ } else {
+ [this.imgInfo1, this.imgInfo2] = obj.imgData;
}
});
},
@@ -164,7 +154,7 @@ export default defineComponent({
<div v-else-if="!Array.isArray(tolNode.imgName)">
<div :style="imgStyles"/>
<ul v-if="imgInfo != null">
- <li>Obtained via: {{imgInfo.imgSrc}}</li>
+ <li>Obtained via: {{imgInfo.src}}</li>
<li>License: <a :href="licenseToUrl(imgInfo.license)">{{imgInfo.license}}</a></li>
<li><a :href="imgInfo.url" class="underline">Source URL</a></li>
<li>Artist: {{imgInfo.artist}}</li>
@@ -174,7 +164,7 @@ export default defineComponent({
<div v-else>
<div v-if="tolNode.imgName[0] != null" :style="firstImgStyles"/>
<ul v-if="imgInfo1 != null">
- <li>Obtained via: {{imgInfo1.imgSrc}}</li>
+ <li>Obtained via: {{imgInfo1.src}}</li>
<li>License: <a :href="licenseToUrl(imgInfo1.license)">{{imgInfo1.license}}</a></li>
<li><a :href="imgInfo1.url" class="underline">Source URL</a></li>
<li>Artist: {{imgInfo1.artist}}</li>
@@ -182,7 +172,7 @@ export default defineComponent({
</ul>
<div v-if="tolNode.imgName[1] != null" :style="secondImgStyles"/>
<ul v-if="imgInfo2 != null">
- <li>Obtained via: {{imgInfo2.imgSrc}}</li>
+ <li>Obtained via: {{imgInfo2.src}}</li>
<li>License: <a :href="licenseToUrl(imgInfo2.license)">{{imgInfo2.license}}</a></li>
<li><a :href="imgInfo2.url" class="underline">Source URL</a></li>
<li>Artist: {{imgInfo2.artist}}</li>
diff --git a/src/layout.ts b/src/layout.ts
index 0dd598d..416ec73 100644
--- a/src/layout.ts
+++ b/src/layout.ts
@@ -6,7 +6,7 @@
* find a tile-based layout, filling in node fields to represent placement.
*/
-import type {TolMap} from './tol';
+import type {TolMap} from './lib';
import {range, arraySum, linspace, limitVals, updateAscSeq} from './lib';
// Represents a node/tree that holds layout data for a TolNode node/tree
diff --git a/src/lib.ts b/src/lib.ts
index 67ac4c3..a6c8df1 100644
--- a/src/lib.ts
+++ b/src/lib.ts
@@ -1,7 +1,43 @@
/*
- * Types
+ * Types/classes
*/
+// Used for tree-of-life representation
+// Maps tree-of-life node names to node objects
+export type TolMap = Map<string, TolNode>;
+// Represents a tree-of-life node
+export class TolNode {
+ otolId: string | null;
+ children: string[];
+ parent: string | null;
+ tips: number;
+ pSupport: boolean;
+ commonName: null | string;
+ imgName: null | string | [string, string] | [null, string] | [string, null];
+ constructor(children: string[] = [], parent = null, tips = 0, pSupport = false){
+ this.otolId = null;
+ this.children = children;
+ this.parent = parent;
+ this.tips = tips;
+ this.pSupport = pSupport;
+ this.commonName = null;
+ this.imgName = null;
+ }
+}
+// Used for server search-responses
+export type SearchSugg = {name: string, canonicalName: string | null};
+ // Represents a search-string suggestion
+export type SearchSuggResponse = {suggs: SearchSugg[], hasMore: boolean};
+ // Holds search suggestions and an indication of if there was more
+// Used for server info-responses
+export type DescInfo = {text: string, wikiId: number, fromRedirect: boolean, fromDbp: boolean};
+export type ImgInfo = {id: number, src: string, url: string, license: string, artist: string, credit: string}
+export type TileInfoResponse = {
+ tolNode: null | TolNode,
+ descData: null | DescInfo | [DescInfo, DescInfo],
+ imgData: null | ImgInfo | [ImgInfo, ImgInfo],
+};
+
// Used by auto-mode and tutorial
export type Action =
'expand' | 'collapse' | 'expandToView' | 'unhideAncestor' |
diff --git a/src/tol.ts b/src/tol.ts
deleted file mode 100644
index 59ecadc..0000000
--- a/src/tol.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Provides classes for representing and working with tree-of-life data.
- */
-
-// Maps tree-of-life node names to node objects
-export type TolMap = Map<string, TolNode>;
-// Represents a tree-of-life node
-export class TolNode {
- otolId: string | null;
- children: string[];
- parent: string | null;
- tips: number;
- pSupport: boolean;
- commonName: null | string;
- imgName: null | string | [string, string] | [null, string] | [string, null];
- constructor(children: string[] = [], parent = null, tips = 0, pSupport = false){
- this.otolId = null;
- this.children = children;
- this.parent = parent;
- this.tips = tips;
- this.pSupport = pSupport;
- this.imgName = null;
- this.commonName = null;
- }
-}