Quellcode durchsuchen

Bare-bones search UI in index page.

scossu vor 4 Tagen
Ursprung
Commit
1f5308f43e

+ 31 - 24
src/html_generator.lua

@@ -36,22 +36,17 @@ local idx_keys = {}
 
 -- HTML templates. Compile them only once.
 -- TODO Add override for user-maintained templates.
-local fh, idx_tpl, dres_tpl, ores_tpl
-fh = datafile.open("templates/index.html")
-idx_tpl = assert(etlua.compile(fh:read("a")))
-fh:close()
-
-fh = datafile.open("templates/dres.html")
-dres_tpl = assert(etlua.compile(fh:read("a")))
-fh:close()
-
-fh = datafile.open("templates/ores.html")
-ores_tpl = assert(etlua.compile(fh:read("a")))
-fh:close()
-
-fh = datafile.open("templates/header.html")
-header_tpl = assert(etlua.compile(fh:read("a")))
-fh:close()
+local templates = {
+    idx     = {file = "templates/index.html"},
+    dres    = {file = "templates/dres.html"},
+    ores    = {file = "templates/ores.html"},
+    head    = {file = "templates/head_common.html"},
+    header  = {file = "templates/header.html"},
+}
+for _, tpl in pairs(templates) do
+    local fh = datafile.open(tpl.file)
+    tpl.data = assert(etlua.compile(fh:read("a")))
+end
 
 
 -- HTML generator module.
@@ -59,6 +54,7 @@ local M = {
     res_dir = plpath.join(pkar.config.htmlgen.out_dir, "res"),
     asset_dir = asset_dir,
     media_dir = plpath.join(pkar.config.htmlgen.out_dir, "media"),
+    webroot = "",  -- TODO switch depending on local FS or webserver generation.
 }
 
 
@@ -129,7 +125,7 @@ local function generate_dres(s, mconf)
 
                 while child_s do
                     -- Loop trough all next nodes for each first child.
-                    require "debugger".assert(get_tn_url(child_s))
+                    --require "debugger".assert(get_tn_url(child_s))
                     table.insert(ll, {
                         href = pkar.gen_pairtree(
                                 "/res", child_s.data, ".html", true),
@@ -169,10 +165,12 @@ local function generate_dres(s, mconf)
     logger:debug("Children:", pp.write(children))
     logger:debug("Breadcrumbs:", pp.write(get_breadcrumbs(mconf)))
 
-    out_html = dres_tpl({
+    out_html = templates.dres.data({
+        webroot = M.webroot,
         site_title = pkar.config.site.title or pkar.default_title,
         title = title or s.data,
-        header_tpl = header_tpl,
+        head_tpl = templates.head.data,
+        header_tpl = templates.header.data,
         mconf = mconf,
         uri = s,
         dmd = dmd,
@@ -261,10 +259,12 @@ local function generate_ores(s, mconf)
         logger:info("Thumbnail: ", tn)
     end
 
-    out_html = ores_tpl({
+    out_html = templates.ores.data({
+        webroot = M.webroot,
         site_title = pkar.config.site.title or pkar.default_title,
         fname = plpath.basename(techmd["pas:sourcePath"][1]),
-        header_tpl = header_tpl,
+        head_tpl = templates.head.data,
+        header_tpl = templates.header.data,
         mconf = mconf,
         uri = s,
         techmd = techmd,
@@ -294,7 +294,12 @@ end
 
 
 M.generate_res_idx = function(s, mconf)
-    local rrep = {id = nsm.denormalize_uri(s.data)}
+    local rrep = {
+        id = nsm.denormalize_uri(s.data),
+        tn = get_tn_url(s):gsub(M.media_dir, "/media/tn"),
+        href = pkar.gen_pairtree("/res", s.data, ".html", true),
+    }
+
     local attrs = gr:connections(s, term.LINK_OUTBOUND)
 
     local function format_value(fname, o)
@@ -414,9 +419,11 @@ M.generate_idx = function()
     table.sort(obj_idx, function(a, b) return a.created < b.created end)
 
     logger:debug(pp.write(obj_idx))
-    out_html = idx_tpl({
+    out_html = templates.idx.data({
+        webroot = M.webroot,
         title = pkar.config.site.title or pkar.default_title,
-        header_tpl = header_tpl,
+        head_tpl = templates.head.data,
+        header_tpl = templates.header.data,
         nsm = nsm,
         obj_idx = obj_idx,
     })

+ 11 - 0
templates/assets/css/pkar.css

@@ -0,0 +1,11 @@
+/* Hidden only on visual browsers. */
+.v_hidden {
+    position:absolute;
+    left:-10000px;
+    top:auto;
+    width:1px;
+    height:1px;
+    overflow:hidden;
+}
+
+.hidden {display: none;}

+ 44 - 0
templates/assets/js/fuse_init.js

@@ -0,0 +1,44 @@
+/*
+ * Init search engine and index.
+ */
+async function get_json(url) {
+    try {
+        const response = await fetch(url);
+        if (!response.ok) {
+            throw new Error(`Response status: ${response.status}`);
+        }
+
+        const json = await response.json();
+        console.log(json);
+
+        return json;
+    } catch (error) {
+        console.error(error.message);
+    }
+}
+
+async function fuse_init() {
+    let [idx_json, keys_json] = await Promise.all([
+        get_json('/js/fuse_index.json'),
+        get_json('/js/fuse_keys.json')
+    ]);
+
+    const fuseOptions = {
+        // isCaseSensitive: false,
+        // includeScore: false,
+        // ignoreDiacritics: false,
+        // shouldSort: true,
+        // includeMatches: false,
+        // findAllMatches: false,
+        // minMatchCharLength: 1,
+        // location: 0,
+        // threshold: 0.6,
+        // distance: 100,
+        // useExtendedSearch: false,
+        // ignoreLocation: false,
+        // ignoreFieldNorm: false,
+        // fieldNormWeight: 1,
+        keys: keys_json,
+    };
+    return new Fuse(idx_json, fuseOptions);
+}

+ 46 - 0
templates/assets/js/search.js

@@ -0,0 +1,46 @@
+/*
+ * Query handling.
+ */
+const params = new Proxy(new URLSearchParams(window.location.search), {
+  get: (searchParams, prop) => searchParams.get(prop),
+});
+const qstring = params.q;
+
+if (qstring != undefined) {
+    (async () => {
+        // Re-fill query string in form field.
+        const search_field = document.getElementById("qry_f");
+        search_field.value = qstring;
+
+        const fuse = await fuse_init();
+
+        const results = fuse.search(qstring);
+        console.log(results);
+        const res_el = document.getElementById('result_list');
+
+        results.forEach(sres => {
+            const rsrc = sres.item;
+
+            const res_tn = document.createElement("img");
+            res_tn.setAttribute("src", window.webroot + rsrc.tn);
+            res_tn.setAttribute("alt", rsrc.Title);
+
+            res_txt = document.createTextNode(rsrc.Title);
+
+            const res_link = document.createElement("a");
+            res_link.setAttribute("href", window.webroot + rsrc.href);
+            res_link.appendChild(res_tn);
+            res_link.appendChild(res_txt);
+
+            const res_li = document.createElement("li");
+            res_li.setAttribute("id", "sres-" + rsrc.id);
+            res_li.appendChild(res_link);
+
+            res_el.appendChild(res_li);
+        });
+
+        // Reveal section after populating.
+        const res_sec = document.getElementById('search_results');
+        res_sec.classList.remove("hidden");
+    })();
+}

+ 6 - 0
templates/head_common.html

@@ -0,0 +1,6 @@
+<!-- Milligram CSS -->
+<link rel="stylesheet" href="/css/normalize.css">
+<link rel="stylesheet" href="/css/milligram.css">
+<link rel="stylesheet" href="/css/pkar.css">
+<title><%= title %>&emsp;&#x2741;&emsp;<%= site_title %></title>
+

+ 6 - 4
templates/header.html

@@ -1,5 +1,7 @@
-<!-- Milligram CSS -->
-<link rel="stylesheet" href="/css/normalize.css">
-<link rel="stylesheet" href="/css/milligram.css">
-<title><%= title %>&emsp;&#x2741;&emsp;<%= site_title %></title>
+<header>
+    <nav>
+        <a href="/"><%= title %></a>
+        <a href="/cats">Categories</a>
+    </nav>
+</header>
 

+ 25 - 49
templates/index.html

@@ -1,61 +1,37 @@
 <!DOCTYPE html>
 <html>
     <head>
-        <%- header_tpl({site_title = site_title, title = "Index"}) %>
+        <%- head_tpl({site_title = site_title, title = "Index"}) %>
         <!-- JS client-side search. -->
         <script src="/js/fuse.min.js"></script>
-        <script type="application/javascript">
-            async function get_json(url) {
-                try {
-                    const response = await fetch(url);
-                    if (!response.ok) {
-                        throw new Error(`Response status: ${response.status}`);
-                    }
-
-                    const json = await response.json();
-                    console.log(json);
-
-                    return json;
-                } catch (error) {
-                    console.error(error.message);
-                }
-            }
-            async function fuse_init() {
-                let [idx_json, keys_json] = await Promise.all([
-                    get_json('/js/fuse_index.json'),
-                    get_json('/js/fuse_keys.json')
-                ]);
-
-                const fuseOptions = {
-                    // isCaseSensitive: false,
-                    // includeScore: false,
-                    // ignoreDiacritics: false,
-                    // shouldSort: true,
-                    // includeMatches: false,
-                    // findAllMatches: false,
-                    // minMatchCharLength: 1,
-                    // location: 0,
-                    // threshold: 0.6,
-                    // distance: 100,
-                    // useExtendedSearch: false,
-                    // ignoreLocation: false,
-                    // ignoreFieldNorm: false,
-                    // fieldNormWeight: 1,
-                    keys: keys_json,
-                };
-                return new Fuse(idx_json, fuseOptions);
-            }
-
-            let fuse;
-            (async () => {fuse = await fuse_init()})();
-        </script>
+        <script src="/js/fuse_init.js"></script>
 
+        <script>window.webroot = "<%= webroot -%>";</script>
+        <script src="/js/search.js" defer></script>
     </head>
     <body>
-        <header>
-            <h1><%= title %></h1>
-        </header>
+        <%- header_tpl({title = title}) %>
         <main>
+            <h1><%= title %></h1>
+            <section id="search">
+                <h2>Search</h2>
+                <form action="/" method="GET">
+                    <fieldset>
+                        <label class="v_hidden" for="q">Search query</label>
+                        <div class="float-left">
+                            <input
+                                    type="text" class="float-left"
+                                    placeholder="Search by title, type, etc."
+                                    name="q" id="qry_f">
+                        </div>
+                        <input class="button-primary" type="submit" value="Search">
+                    </fieldset>
+                </form>
+            </section>
+            <section id="search_results" class="hidden">
+                <h2>Search results</h2>
+                <ul id="result_list"></ul>
+            </section>
             <section id="obj_list">
                 <h2>Recent artifacts</h2>
                 <ul>