« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/common-util/sugar.js25
-rw-r--r--src/common-util/wiki-data.js88
-rw-r--r--src/content/dependencies/generateTrackArtworkColumn.js2
-rw-r--r--src/static/css/miscellany.css2
-rw-r--r--src/static/css/search.css2
-rw-r--r--src/static/css/tooltips.css8
-rw-r--r--src/static/js/client/index.js2
-rw-r--r--src/static/js/client/sidebar-search.js85
8 files changed, 141 insertions, 73 deletions
diff --git a/src/common-util/sugar.js b/src/common-util/sugar.js
index 354cf5cc..c988156c 100644
--- a/src/common-util/sugar.js
+++ b/src/common-util/sugar.js
@@ -417,6 +417,31 @@ export function escapeRegex(string) {
   return string.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
 }
 
+// Adapted from here: https://emnudge.dev/notes/multiline-regex/
+// ...with a lot of changes
+export function re(...args) {
+  let flags, parts;
+  if (args.length === 2) {
+    flags = args[0];
+    parts = args[1];
+  } else if (args.length === 1) {
+    flags = '';
+    parts = args[0];
+  } else {
+    throw new Error(`Expected 1 or 2 arguments`);
+  }
+
+  const source = parts
+    .flat(Infinity)
+    .map(item =>
+      (item instanceof RegExp
+        ? item.source
+        : item.toString()))
+    .join('');
+
+  return new RegExp(source, flags);
+}
+
 export function splitKeys(key) {
   return key.split(/(?<=(?<!\\)(?:\\\\)*)\./);
 }
diff --git a/src/common-util/wiki-data.js b/src/common-util/wiki-data.js
index 4c7f66f4..ff325b7a 100644
--- a/src/common-util/wiki-data.js
+++ b/src/common-util/wiki-data.js
@@ -1,6 +1,6 @@
 // Utility functions for interacting with wiki data.
 
-import {accumulateSum, chunkByConditions, empty, unique} from './sugar.js';
+import {accumulateSum, chunkByConditions, empty, re, unique} from './sugar.js';
 import {sortByDate} from './sort.js';
 
 // This is a duplicate binding of filterMultipleArrays that's included purely
@@ -76,42 +76,48 @@ export function compareKebabCase(name1, name2) {
 //   by slashes or dashes (only valid orders are MM/DD/YYYY and YYYY/MM/DD)
 //
 const dateRegex = groupName =>
-  String.raw`(?<${groupName}>` +
-    String.raw`[a-zA-Z]+ [0-9]{1,2}, [0-9]{4,4}|` +
-    String.raw`[0-9]{1,2} [^,]*[0-9]{4,4}|` +
-    String.raw`[0-9]{1,4}[-/][0-9]{1,4}[-/][0-9]{1,4}` +
-  String.raw`)`;
-
-const contentEntryHeadingRegexRaw =
-  String.raw`^(?:` +
-    String.raw`(?:` +
-      String.raw`<i>(?<artists>.+?):<\/i>` +
-      String.raw`(?: \((?<annotation1>.*)\))?` +
-    String.raw`)` +
-    String.raw`|` +
-    String.raw`(?:` +
-      String.raw`@@ (?<annotation2>.*)` +
-    String.raw`)` +
-  String.raw`)$`;
+  re([
+    `(?<${groupName}>`,
+      /[a-zA-Z]+ [0-9]{1,2}, [0-9]{4,4}/, '|',
+      /[0-9]{1,2} [^,]*[0-9]{4,4}/, '|',
+      /[0-9]{1,4}[-/][0-9]{1,4}[-/][0-9]{1,4}/,
+    `)`,
+  ]);
 
 const contentEntryHeadingRegex =
-  new RegExp(contentEntryHeadingRegexRaw, 'gm');
-
-const contentEntryAnnotationTailRegexRaw =
-  String.raw`(?:, |^)` +
-
-  String.raw`(?:(?<dateKind>sometime|throughout|around) )?` +
-  String.raw`${dateRegex('date')}` +
-  String.raw`(?: ?- ?${dateRegex('secondDate')})?` +
-
-  String.raw`(?: ?(?<= )` +
-    String.raw`(?<accessKind>captured|accessed) ${dateRegex('accessDate')}` +
-  String.raw`)?` +
-
-  String.raw`$`;
+  re('gm', [
+    '^(?:',
+      '(?:',
+        /<i>(?<artists>.+?):<\/i>/,
+        /(?: \((?<annotation1>.*)\))?/,
+      ')',
+      '|',
+      '(?:',
+        /@@ (?<annotation2>.*)/,
+      ')',
+    ')$',
+  ]);
 
 const contentEntryAnnotationTailRegex =
-  new RegExp(contentEntryAnnotationTailRegexRaw);
+  re([
+    /(?:, |^)/,
+
+    /(?:(?<dateKind>sometime|throughout|around) )?/,
+    dateRegex('date'),
+
+    '(?:',
+      ' ?',
+      '-',
+      ' ?',
+      dateRegex('secondDate'),
+    ')?',
+
+    '(?: ?(?<= )',
+      /(?<accessKind>captured|accessed)/,
+      ' ',
+      dateRegex('accessDate'),
+    ')?',
+  ]);
 
 export function* matchContentEntries(sourceText) {
   let workingEntry = null;
@@ -593,7 +599,21 @@ export function* matchMarkdownLinks(markdownSource, {marked}) {
 }
 
 export function* matchInlineLinks(source) {
-  const plausibleLinkRegexp = /\b[a-z]*:\/\/[^ ]*?(?=(?:[,.!?]*)(?:\s|$))/gm;
+  const plausibleLinkRegexp =
+    re('gmi', [
+      /\b[a-z]*:\/\//,
+      /.*?/,
+
+      '(?=',
+        // Ordinary in-sentence punctuation doesn't terminate the
+        // un-greedy URL match above, but it shouldn't be counted
+        // as part of the link either, if it's at the end.
+        /(?:[,.!?]*)/,
+
+        // Actual terminators.
+        /(?:\s|$|<br>)/,
+      ')',
+    ]);
 
   let plausibleMatch = null;
   while (plausibleMatch = plausibleLinkRegexp.exec(source)) {
diff --git a/src/content/dependencies/generateTrackArtworkColumn.js b/src/content/dependencies/generateTrackArtworkColumn.js
index feaed604..77ce0071 100644
--- a/src/content/dependencies/generateTrackArtworkColumn.js
+++ b/src/content/dependencies/generateTrackArtworkColumn.js
@@ -21,7 +21,7 @@ export default {
       relations.albumCover?.slots({
         showOriginDetails: true,
         showArtTagDetails: true,
-        showReferenceDetails: true,
+        showReferenceDetails: false,
       }),
 
       relations.trackCovers.map(cover =>
diff --git a/src/static/css/miscellany.css b/src/static/css/miscellany.css
index 70120a33..dcdd72ca 100644
--- a/src/static/css/miscellany.css
+++ b/src/static/css/miscellany.css
@@ -478,7 +478,9 @@
     font-weight: normal;
     color: var(--primary-color);
   }
+}
 
+@layer interaction {
   summary > span:hover {
     cursor: pointer;
     text-decoration: underline;
diff --git a/src/static/css/search.css b/src/static/css/search.css
index f421803b..a79fb20a 100644
--- a/src/static/css/search.css
+++ b/src/static/css/search.css
@@ -191,6 +191,8 @@
 @layer layout {
   .wiki-search-context-container {
     padding: 2px 12px 4px;
+    padding-left: calc(12px + 1.2ch);
+    text-indent: -1.2ch;
   }
 }
 
diff --git a/src/static/css/tooltips.css b/src/static/css/tooltips.css
index 644430b7..116c9181 100644
--- a/src/static/css/tooltips.css
+++ b/src/static/css/tooltips.css
@@ -416,10 +416,6 @@
     display: inline-block;
   }
 
-  .content-tooltip-guy:not(.has-link) .hoverable {
-    cursor: default;
-  }
-
   .content-tooltip-guy.has-link .text-with-tooltip-interaction-cue {
     text-decoration-color: var(--primary-color);
   }
@@ -438,6 +434,10 @@
 }
 
 @layer interaction {
+  .content-tooltip-guy:not(.has-link) .hoverable {
+    cursor: default;
+  }
+
   .content-tooltip-guy .hoverable a {
     text-decoration-color: transparent;
     text-decoration-style: dotted;
diff --git a/src/static/js/client/index.js b/src/static/js/client/index.js
index 16ebe89f..cd617bea 100644
--- a/src/static/js/client/index.js
+++ b/src/static/js/client/index.js
@@ -133,7 +133,7 @@ for (const module of modules) {
             break;
 
           case 'boolean':
-            formatRead = Boolean;
+            formatRead = value => value === 'true' ? true : false;
             formatWrite = String;
             break;
 
diff --git a/src/static/js/client/sidebar-search.js b/src/static/js/client/sidebar-search.js
index 8b29cf63..c39c38bc 100644
--- a/src/static/js/client/sidebar-search.js
+++ b/src/static/js/client/sidebar-search.js
@@ -38,6 +38,9 @@ export const info = {
   searchLabel: null,
   searchInput: null,
 
+  contextContainer: null,
+  contextBackLink: null,
+
   progressRule: null,
   progressContainer: null,
   progressLabel: null,
@@ -46,9 +49,6 @@ export const info = {
   failedRule: null,
   failedContainer: null,
 
-  contextContainer: null,
-  contextBackLink: null,
-
   filterContainer: null,
   albumFilterLink: null,
   artistFilterLink: null,
@@ -127,6 +127,7 @@ export const info = {
     activeQueryContextPageName: {type: 'string'},
     activeQueryContextPagePathname: {type: 'string'},
     activeQueryContextPageColor: {type: 'string'},
+    zapActiveQueryContext: {type: 'boolean'},
 
     activeQueryResults: {
       type: 'json',
@@ -163,6 +164,8 @@ export function* bindSessionStorage() {
     yield 'activeQueryContextPageName';
     yield 'activeQueryContextPagePathname';
     yield 'activeQueryContextPageColor';
+    yield 'zapActiveQueryContext';
+
     yield 'activeQueryResults';
     yield 'activeFilterType';
     yield 'resultsScrollOffset';
@@ -302,6 +305,25 @@ export function addInternalListeners() {
 export function mutatePageContent() {
   if (!info.searchBox) return;
 
+  // Context section
+
+  info.contextContainer =
+    document.createElement('div');
+
+  info.contextContainer.classList.add('wiki-search-context-container');
+
+  info.contextBackLink =
+    document.createElement('a');
+
+  info.contextContainer.appendChild(
+    templateContent(info.backString, {
+      page: info.contextBackLink,
+    }));
+
+  cssProp(info.contextContainer, 'display', 'none');
+
+  info.searchBox.appendChild(info.contextContainer);
+
   // Progress section
 
   info.progressRule =
@@ -355,25 +377,6 @@ export function mutatePageContent() {
   info.searchBox.appendChild(info.failedRule);
   info.searchBox.appendChild(info.failedContainer);
 
-  // Context section
-
-  info.contextContainer =
-    document.createElement('div');
-
-  info.contextContainer.classList.add('wiki-search-context-container');
-
-  info.contextBackLink =
-    document.createElement('a');
-
-  info.contextContainer.appendChild(
-    templateContent(info.backString, {
-      page: info.contextBackLink,
-    }));
-
-  cssProp(info.contextContainer, 'display', 'none');
-
-  info.searchBox.appendChild(info.contextContainer);
-
   // Filter section
 
   info.filterContainer =
@@ -750,6 +753,20 @@ function recordActiveQueryContext() {
   const {session} = info;
 
   if (document.documentElement.dataset.urlKey === 'localized.home') {
+    session.activeQueryContextPageName = null;
+    session.activeQueryContextPagePathname = null;
+    session.activeQueryContextPageColor = null;
+    session.zapActiveQueryContext = true;
+    return;
+  }
+
+  // Zapping means subsequent searches don't record context.
+  if (session.zapActiveQueryContext) {
+    return;
+  }
+
+  // We also don't overwrite existing context.
+  if (session.activeQueryContextPagePathname) {
     return;
   }
 
@@ -780,11 +797,22 @@ function clearSidebarSearch() {
 
   state.searchStage = null;
 
+  clearActiveQuery();
+
+  hideSidebarSearchResults();
+}
+
+function clearActiveQuery() {
+  const {session} = info;
+
   session.activeQuery = null;
   session.activeQueryResults = null;
   session.resultsScrollOffset = null;
 
-  hideSidebarSearchResults();
+  session.activeQueryContextPageName = null;
+  session.activeQueryContextPagePathname = null;
+  session.activeQueryContextPageColor = null;
+  session.zapActiveQueryContext = false;
 }
 
 function clearSidebarFilter() {
@@ -1537,16 +1565,7 @@ function considerRecallingRecentSidebarSearch() {
 }
 
 function forgetRecentSidebarSearch() {
-  const {session} = info;
-
-  session.activeQuery = null;
-
-  session.activeQueryContextPageName = null;
-  session.activeQueryContextPagePathname = null;
-  session.activeQueryContextPageColor = null;
-
-  session.activeQueryResults = null;
-
+  clearActiveQuery();
   clearSidebarFilter();
 }