From 985e054178e84f6a68a0b1ad281254442e2f84af Mon Sep 17 00:00:00 2001 From: BuzCarter Date: Wed, 13 Sep 2023 07:54:17 -0700 Subject: [PATCH] #29: capture artist and optional render "meta" HTML (& #27 -- more to dos, but close 'nuf for jazz) --- demo/index.html | 6 +-- src/js/configs/settings.ts | 37 +++++++++++----- src/js/htmlBeast/classes/HTMLHandles.ts | 9 +++- src/js/htmlBeast/handles.ts | 9 +++- src/js/htmlBeast/page.ts | 22 ++++++++-- src/js/htmlBeast/song.ts | 58 ++++++++++++++----------- 6 files changed, 92 insertions(+), 49 deletions(-) diff --git a/demo/index.html b/demo/index.html index 3a1bb13..9ebf96f 100644 --- a/demo/index.html +++ b/demo/index.html @@ -13,14 +13,14 @@

UkeGeeks: Song Formatter Demos

-
+
-{title: Making It Fit: Columns}
-{artist: Buz C.}
+{title: A Song Markup Sampler (this is Song Title)}
+{artist: Capability R. Brown}
 {subtitle: A "coloring inside the lines" exercise}
 
 {c:Intro}
diff --git a/src/js/configs/settings.ts b/src/js/configs/settings.ts
index 98a1ff8..88cca6b 100644
--- a/src/js/configs/settings.ts
+++ b/src/js/configs/settings.ts
@@ -77,27 +77,40 @@ const Settings = Object.seal({
     },
   } as FretBox,
 
+  /* eslint-disable key-spacing */
   /**
-   * ID's of key HTML page elements
+   * ID's of key HTML page elements, the "buckets" into which generated
+   * HTML markup will be placed
+   * @todo sync naming with the `wrapClasses`
+   * @see `wrapClasses`
    */
   ids: {
-    /* eslint-disable key-spacing */
-    songText: 'ukeSongText', // element holding the song's text
-    canvas: 'ukeChordsCanvas', // canvas
-    container: 'ukeSongContainer', // wraps BOTH Song Text and Chord Canvas
-    /* eslint-enable key-spacing */
+    /** "Meta" includes song Title, Subtitle, Album, Artist */
+    songMeta:     'ukeSongMeta',
+    /** element holding the song's text */
+    songText:     'ukeSongText',
+    /** Chord Diagrams */
+    canvas:       'ukeChordsCanvas',
+    /** wraps BOTH Song Text and Chord Canvas */
+    container:    'ukeSongContainer',
   } as StringDict,
 
   /**
-   * CSS Class names used to find page elements-- be careful if renaming!
+   * Magic CSS Class names used to find page elements-- be careful if renaming!
+   * @todo update with `js-` prefix!
+   * @see `ids`
    */
   wrapClasses: {
-    /* eslint-disable key-spacing */
-    wrap: '.ugs-song-wrap', // wraps BOTH Song Text and Chord Canvas
-    diagrams: '.ugs-diagrams-wrap', // canvas
-    text: '.ugs-source-wrap', // element holding the song's text
-    /* eslint-enable key-spacing */
+    /** "Meta" includes song Title, Subtitle, Album, Artist */
+    songMeta:     'ugs-song-meta',
+    /** wraps BOTH Song Text and Chord Canvas */
+    wrap:         '.ugs-song-wrap',
+    /** Chord Diagrams */
+    diagrams:     '.ugs-diagrams-wrap',
+    /** element holding the song's text */
+    text:         '.ugs-source-wrap',
   } as StringDict,
+  /* eslint-enable key-spacing */
 
   /**
    * Options (Features) you can turn on or off
diff --git a/src/js/htmlBeast/classes/HTMLHandles.ts b/src/js/htmlBeast/classes/HTMLHandles.ts
index 4642fc8..fa7d341 100644
--- a/src/js/htmlBeast/classes/HTMLHandles.ts
+++ b/src/js/htmlBeast/classes/HTMLHandles.ts
@@ -1,13 +1,18 @@
 /**
- * DOM Element object
+ * DOM Elements, the "buckets" into which generated HTML markup will be placed
  */
 export default class HTMLHandles {
-  constructor(wrap: HTMLElement, diagrams: HTMLElement, text: HTMLElement) {
+  constructor(wrap: HTMLElement, diagrams: HTMLElement, text: HTMLElement, meta?: HTMLElement | null) {
     this.wrap = wrap;
     this.diagrams = diagrams;
     this.text = text;
+    if (meta) {
+      this.meta = meta;
+    }
   }
 
+  meta?: HTMLElement;
+
   wrap: HTMLElement;
 
   diagrams: HTMLElement;
diff --git a/src/js/htmlBeast/handles.ts b/src/js/htmlBeast/handles.ts
index 707db37..68c9547 100644
--- a/src/js/htmlBeast/handles.ts
+++ b/src/js/htmlBeast/handles.ts
@@ -5,13 +5,17 @@ import { settings } from '../configs';
 export function getHandlesFromClass(wrap: HTMLElement): HTMLHandles | null {
   const diagrams = wrap.querySelectorAll(settings.wrapClasses.diagrams);
   const text = wrap.querySelectorAll(settings.wrapClasses.text);
-  if ((diagrams === undefined) || (diagrams.length < 1) || (text === undefined) || (text.length < 1)) {
+  const songMeta = wrap.querySelectorAll(settings.wrapClasses.songMeta);
+
+  if (!diagrams.length || !text.length) {
     return null;
   }
+
   return new HTMLHandles(
     wrap,
     diagrams[0] as HTMLElement,
     text[0] as HTMLElement,
+    (songMeta.length && songMeta[0] as HTMLElement) || null,
   );
 }
 
@@ -20,8 +24,9 @@ export function getHandlesFromId(): HTMLHandles | null {
   const wrap = document.getElementById(ids.container);
   const diagrams = document.getElementById(ids.canvas);
   const text = document.getElementById(ids.songText);
+  const meta = document.getElementById(ids.songMeta);
 
   return wrap && diagrams && text
-    ? new HTMLHandles(wrap, diagrams, text)
+    ? new HTMLHandles(wrap, diagrams, text, meta)
     : null;
 }
diff --git a/src/js/htmlBeast/page.ts b/src/js/htmlBeast/page.ts
index da52d87..3a889f1 100644
--- a/src/js/htmlBeast/page.ts
+++ b/src/js/htmlBeast/page.ts
@@ -1,5 +1,5 @@
 import { settings } from '../configs';
-import { Song, parseCPM } from '../cpmImporter';
+import { BlockTypes, Song, parseCPM } from '../cpmImporter';
 
 import { getErrors, init, show, showInline } from './referenceDiagrams';
 import HTMLHandles from './classes/HTMLHandles';
@@ -13,13 +13,27 @@ let errList: string[] = [];
  * read Music, find chords, generate HTML version of song
  */
 export function processSong(handles: HTMLHandles | null): Song | null {
-  const { text, wrap } = handles || {};
-  if (!handles || !handles.diagrams || !text || !wrap) {
+  const { meta, text, wrap } = handles || {};
+  if (!handles?.diagrams || !text || !wrap) {
     return null;
   }
 
+  const metaBlockTypes = [
+    BlockTypes.Title,
+    BlockTypes.Subtitle,
+    BlockTypes.Album,
+    BlockTypes.Artist,
+    BlockTypes.UkeGeeksMeta,
+  ];
+
   const song = parseCPM(text.innerHTML);
-  text.innerHTML = songToHTML(song);
+  text.innerHTML = songToHTML(song, { exclude: metaBlockTypes });
+  if (meta) {
+    meta.innerHTML = songToHTML(song, {
+      include: metaBlockTypes,
+      blocksOnly: true,
+    });
+  }
 
   init(handles);
   show(song.chordNames);
diff --git a/src/js/htmlBeast/song.ts b/src/js/htmlBeast/song.ts
index e29d66d..2c57f26 100644
--- a/src/js/htmlBeast/song.ts
+++ b/src/js/htmlBeast/song.ts
@@ -7,20 +7,23 @@ import {
 } from '../cpmImporter';
 import { generateTabSvg } from '../imageRenderer';
 
-// type guards
+interface SongBlockOptions {
+  include?: BlockTypes[]
+  exclude?: BlockTypes[]
+  /** if `true` bypass Column Wrap & Chord HTML */
+  blocksOnly?: boolean
+}
+
+// TS type guards
 const isSongBlock = (block: BaseSongBlock): block is SongBlock => block.type !== BlockTypes.TabBlock;
 const isTabBlock = (block: BaseSongBlock): block is TabBlock => !isSongBlock(block);
 
-function songBlocksToHTML(songBlocks: SongBlockArray, options?: {
-  include?: BlockTypes[]
-  exclude?: BlockTypes[]
-}): string {
-  const nl = '\n';
+function songBlocksToHTML(songBlocks: SongBlockArray, options?: SongBlockOptions): string {
   let nextType;
 
   return songBlocks
     .filter(({ type }) => {
-      if (!options) {
+      if (!options || type === BlockTypes.TextBlock) {
         return true;
       }
       if (Array.isArray(options.include)) {
@@ -35,7 +38,7 @@ function songBlocksToHTML(songBlocks: SongBlockArray, options?: {
       if (isTabBlock(block)) {
         html += `
`;
         html += generateTabSvg(block);
-        html += `
${nl}`; + html += '
\n'; return html; } if (!isSongBlock(block)) { @@ -46,32 +49,32 @@ function songBlocksToHTML(songBlocks: SongBlockArray, options?: { const firstLine = (typeof lines[0] === 'string' && lines[0]) || ''; switch (type) { case BlockTypes.Title: - html += `

${firstLine}

${nl}`; + html += `

${firstLine}

\n`; break; case BlockTypes.Subtitle: - html += `

${firstLine}

${nl}`; + html += `

${firstLine}

\n`; break; case BlockTypes.Album: - html += `

${firstLine}

${nl}`; + html += `

${firstLine}

\n`; break; case BlockTypes.Artist: - html += `

${firstLine}

${nl}`; + html += `

${firstLine}

\n`; break; case BlockTypes.UkeGeeksMeta: - html += `

${firstLine}

${nl}`; + html += `

${firstLine}

\n`; break; case BlockTypes.Comment: - html += `
${firstLine}
${nl}`; + html += `
${firstLine}
\n`; break; case BlockTypes.NewPage: - html += `
${nl}`; + html += `
\n`; break; case BlockTypes.ChordText: case BlockTypes.PlainText: case BlockTypes.ChordOnlyText: { - // TODO: beware undefined's!!! - // Repack the text, only open/close
 tags when type changes
-        // problem: exacerbates WebKit browsers' first chord position bug.
+          // TODO: beware undefined's!!!
+          // Repack the text, only open/close 
 tags when type changes
+          // problem: exacerbates WebKit browsers' first chord position bug.
           if (!firstLine) {
           // prevent empty blocks (usually caused by comments mixed in header tags)
             return html;
@@ -83,18 +86,18 @@ function songBlocksToHTML(songBlocks: SongBlockArray, options?: {
           const currentType = type;
           const lastType = ((i - 1) >= 0) ? songBlocks[i - 1].type : BlockTypes.Undefined;
           nextType = ((i + 1) < songBlocks.length) ? nextType = songBlocks[i + 1].type : BlockTypes.Undefined;
-          html += lastType !== currentType ? `
` : nl;
+          html += lastType !== currentType ? `
` : '\n';
           html += firstLine;
-          html += nextType !== currentType ? `
${nl}` : ''; + html += nextType !== currentType ? '
\n' : ''; } break; case BlockTypes.ChorusBlock: - html += `
${nl}`; - html += songBlocksToHTML(lines as SongBlockArray); - html += `
${nl}`; + html += `
\n`; + html += songBlocksToHTML(lines as SongBlockArray, options); + html += '
\n'; break; case BlockTypes.TextBlock: - html += songBlocksToHTML(lines as SongBlockArray); + html += songBlocksToHTML(lines as SongBlockArray, options); break; case BlockTypes.ColumnBreak: html += `
`; @@ -106,9 +109,12 @@ function songBlocksToHTML(songBlocks: SongBlockArray, options?: { } /** Convert passed in song to HTML (text) block */ -export function songToHTML(song: Song) { +export function songToHTML(song: Song, options?: SongBlockOptions) { const { songBlocks: tempSongBlocks } = song; - let html = songBlocksToHTML(tempSongBlocks); + let html = songBlocksToHTML(tempSongBlocks, options); + if (options?.blocksOnly) { + return html; + } if (song.columnCount > 1) { html = '' + `
`