Skip to content

Commit

Permalink
#29: capture artist and optional render "meta" HTML (& #27 -- more to…
Browse files Browse the repository at this point in the history
… dos, but close 'nuf for jazz)
  • Loading branch information
buzcarter committed Sep 13, 2023
1 parent e4b1818 commit 985e054
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 49 deletions.
6 changes: 3 additions & 3 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
<h1>UkeGeeks: Song Formatter Demos</h1>
</hgroup>
</header>
<div class="metaInfo"></div>
<div id="ukeSongMeta" class="metaInfo"></div>
<div id="ukeSongContainer" class="ugsLayoutTwoColumn ugs-song-wrap">
<aside id="ukeChordsCanvas" class="ugs-diagrams-wrap ugs-grouped">
</aside>
<section id="ukeSongText" class="ugs-source-wrap">
<pre>
{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}
Expand Down
37 changes: 25 additions & 12 deletions src/js/configs/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 7 additions & 2 deletions src/js/htmlBeast/classes/HTMLHandles.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
9 changes: 7 additions & 2 deletions src/js/htmlBeast/handles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
}

Expand All @@ -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;
}
22 changes: 18 additions & 4 deletions src/js/htmlBeast/page.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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);
Expand Down
58 changes: 32 additions & 26 deletions src/js/htmlBeast/song.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -35,7 +38,7 @@ function songBlocksToHTML(songBlocks: SongBlockArray, options?: {
if (isTabBlock(block)) {
html += `<pre class="${Styles.Tabs}">`;
html += generateTabSvg(block);
html += `</pre>${nl}`;
html += '</pre>\n';
return html;
}
if (!isSongBlock(block)) {
Expand All @@ -46,32 +49,32 @@ function songBlocksToHTML(songBlocks: SongBlockArray, options?: {
const firstLine = (typeof lines[0] === 'string' && lines[0]) || '';
switch (type) {
case BlockTypes.Title:
html += `<h1 class="${Styles.Title}">${firstLine}</h1>${nl}`;
html += `<h1 class="${Styles.Title}">${firstLine}</h1>\n`;
break;
case BlockTypes.Subtitle:
html += `<h2 class="${Styles.Subtitle}">${firstLine}</h2>${nl}`;
html += `<h2 class="${Styles.Subtitle}">${firstLine}</h2>\n`;
break;
case BlockTypes.Album:
html += `<h3 class="${Styles.Album}">${firstLine}</h3>${nl}`;
html += `<h3 class="${Styles.Album}">${firstLine}</h3>\n`;
break;
case BlockTypes.Artist:
html += `<h3 class="${Styles.Artist}">${firstLine}</h3>${nl}`;
html += `<h3 class="${Styles.Artist}">${firstLine}</h3>\n`;
break;
case BlockTypes.UkeGeeksMeta:
html += `<h3 class="${Styles.UgsMeta}">${firstLine}</h3>${nl}`;
html += `<h3 class="${Styles.UgsMeta}">${firstLine}</h3>\n`;
break;
case BlockTypes.Comment:
html += `<h6 class="${Styles.Comment}">${firstLine}</h6>${nl}`;
html += `<h6 class="${Styles.Comment}">${firstLine}</h6>\n`;
break;
case BlockTypes.NewPage:
html += `<hr class="${Styles.NewPage}" />${nl}`;
html += `<hr class="${Styles.NewPage}" />\n`;
break;
case BlockTypes.ChordText:
case BlockTypes.PlainText:
case BlockTypes.ChordOnlyText: {
// TODO: beware undefined's!!!
// Repack the text, only open/close <pre> tags when type changes
// problem: exacerbates WebKit browsers' first chord position bug.
// TODO: beware undefined's!!!
// Repack the text, only open/close <pre> 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;
Expand All @@ -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 ? `<pre class="${preClasses}">` : nl;
html += lastType !== currentType ? `<pre class="${preClasses}">` : '\n';
html += firstLine;
html += nextType !== currentType ? `</pre>${nl}` : '';
html += nextType !== currentType ? '</pre>\n' : '';
}
break;
case BlockTypes.ChorusBlock:
html += `<div class="${Styles.Chorus}">${nl}`;
html += songBlocksToHTML(lines as SongBlockArray);
html += `</div>${nl}`;
html += `<div class="${Styles.Chorus}">\n`;
html += songBlocksToHTML(lines as SongBlockArray, options);
html += '</div>\n';
break;
case BlockTypes.TextBlock:
html += songBlocksToHTML(lines as SongBlockArray);
html += songBlocksToHTML(lines as SongBlockArray, options);
break;
case BlockTypes.ColumnBreak:
html += `</div><div class="${Styles.Column}">`;
Expand All @@ -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 = ''
+ `<div class="${Styles.ColumnWrap} ${Styles.ColumnCount}${song.columnCount}">`
Expand Down

0 comments on commit 985e054

Please sign in to comment.