'."\n";
- } elseif (preg_match('/^end_of_(.*)/', $metadata->getName()) === 1) {
+ return $content.'
\n";
} else {
- $name = preg_replace('/[\W_\-]/', '', mb_strtolower($metadata->getName()));
- return '
\n";
+ if ($metadata->getName() === 'key' && null != $this->notation) {
+ $value = $this->notation->convertChordRootToNotation($metadata->getValue() ?? '');
+ } else {
+ $value = $metadata->getValue();
+ }
+ $type = $metadata->getNameSlug();
+ $output = '
';
+ if ($metadata->isNameNecessary()) {
+ $output .= ''.$metadata->getHumanName().': ';
+ $output .= ''.$value.'';
+ } else {
+ $output .= $value;
+ }
+ $output .= "
\n";
+ return $output;
}
}
private function getLyricsHtml(Lyrics $lyrics): string
{
- $line = '
'."\n";
- foreach ($lyrics->getBlocks() as $block) {
+ $classes = ['chordpro-line'];
+ if ($lyrics->hasInlineChords()) {
+ $classes[] = 'chordpro-line-inline-chords';
+ }
+ if (!$lyrics->hasChords()) {
+ $classes[] = 'chordpro-line-text-only';
+ }
+ if (!$lyrics->hasText()) {
+ $classes[] = 'chordpro-line-chords-only';
+ }
+ $line = '
'."\n";
+ $lineChords = '';
+ $lineText = '';
+ foreach ($lyrics->getBlocks() as $num => $block) {
$originalChords = [];
$chords = [];
@@ -96,22 +120,51 @@ private function getLyricsHtml(Lyrics $lyrics): string
$originalChord = implode('/', $originalChords);
$text = $this->blankChars($block->getText());
- $line .= '
' .
- ''.$chord.'' .
- ''.$text.'' .
- '';
+ if ($lyrics->hasInlineChords()) {
+ if ($num === 0) {
+ $lineText = '
'.$text.'
';
+ } else {
+ $lineChords .= '
'.$chord.'';
+ }
+ } elseif ($lyrics->hasChords() && $lyrics->hasText()) {
+ $line .= '
' .
+ ''.$chord.'' .
+ ''.$text.'' .
+ '';
+ } elseif ($lyrics->hasChords()) {
+ $line .= '
' .
+ ''.$chord.'' .
+ '';
+
+ } elseif ($lyrics->hasText()) {
+ $line .= '
' .
+ ''.$text.'' .
+ '';
+ }
}
+
+ if ($lyrics->hasInlineChords()) {
+ $line .= '
';
+ $line .= '
' . $lineChords . '
';
+ $line .= $lineText;
+ $line .= '
';
+ }
+
$line .= "\n
\n";
return $line;
}
private function getLyricsOnlyHtml(Lyrics $lyrics): string
{
- $line = '
'."\n";
+ $text = '';
foreach ($lyrics->getBlocks() as $block) {
- $line .= ltrim($block->getText());
+ $text .= ltrim($block->getText());
+ }
+ $text = rtrim($text);
+ if ($text === '') {
+ return '';
+ } else {
+ return '
'."\n".rtrim($text)."\n
\n";
}
- $line .= "\n
\n";
- return $line;
}
}
diff --git a/src/Formatter/JSONFormatter.php b/src/Formatter/JSONFormatter.php
index 186cc87..dfc3dc3 100644
--- a/src/Formatter/JSONFormatter.php
+++ b/src/Formatter/JSONFormatter.php
@@ -54,11 +54,32 @@ private function getMetadataJSON(Metadata $metadata): array
if (in_array($metadata->getName(), $this->ignoreMetadata, true)) {
return [];
}
- return [
+
+ if ($metadata->isSectionStart()) {
+ $metadataItem = [
+ 'type' => 'section_start',
+ 'sectionType' => $metadata->getSectionType(),
+ ];
+ if (null !== $metadata->getValue()) {
+ $metadataItem['label'] = $metadata->getValue();
+ }
+ } elseif ($metadata->isSectionEnd()) {
+ $metadataItem = [
+ 'type' => 'section_end',
+ 'sectionType' => $metadata->getSectionType(),
+ ];
+ } else {
+ $metadataItem = [
'type' => 'metadata',
'name' => $metadata->getName(),
'value' => $metadata->getValue(),
- ];
+ ];
+ if ($metadata->getHumanName() != $metadata->getName()) {
+ $metadataItem['humanName'] = $metadata->getHumanName();
+ }
+ }
+
+ return $metadataItem;
}
/**
@@ -80,14 +101,26 @@ private function getLyricsJSON(Lyrics $lyrics): array
$originalChords[] = $slicedChord->getOriginalName();
}
}
- $chord = implode('/', $chords).' ';
- $originalChord = implode('/', $originalChords).' ';
+ $chord = implode('/', $chords);
+ $originalChord = implode('/', $originalChords);
$text = $block->getText();
- $return[] = ['chord' => trim($chord), 'text' => $text, 'originalChord' => trim($originalChord)];
+ $blockArray = [];
+ if ($text !== '') {
+ $blockArray['text'] = rtrim($text);
+ }
+ $chord = trim($chord);
+ if ($chord !== '') {
+ $blockArray['chord'] = $chord;
+ $blockArray['originalChord'] = $originalChord;
+ }
+ if ($block->isLineEnd()) {
+ $blockArray['lineEnd'] = true;
+ }
+ $return[] = $blockArray;
}
return [
- 'type' => 'line',
+ 'type' => $lyrics->hasInlineChords() ? 'line_inline' : 'line',
'blocks' => $return,
];
}
@@ -97,13 +130,18 @@ private function getLyricsJSON(Lyrics $lyrics): array
*/
private function getLyricsOnlyJSON(Lyrics $lyrics): array
{
- $return = '';
+ $text = '';
foreach ($lyrics->getBlocks() as $block) {
- $return .= ltrim($block->getText());
+ $text .= ltrim($block->getText());
+ }
+ $text = rtrim($text);
+ if ($text === '') {
+ return [];
+ } else {
+ return [
+ 'type' => 'line',
+ 'text' => $text,
+ ];
}
- return [
- 'type' => 'line',
- 'text' => $return,
- ];
}
}
diff --git a/src/Formatter/MonospaceFormatter.php b/src/Formatter/MonospaceFormatter.php
index 4cc2a51..5cb233b 100644
--- a/src/Formatter/MonospaceFormatter.php
+++ b/src/Formatter/MonospaceFormatter.php
@@ -16,11 +16,13 @@ public function format(Song $song, array $options = []): string
{
$this->setOptions($options);
- $monospace = '';
+ $lines = [];
foreach ($song->getLines() as $line) {
- $monospace .= $this->getLineMonospace($line);
+ $lines[] = $this->getLineMonospace($line);
}
- return $monospace;
+
+ $this->transformInlineChords($lines);
+ return implode("", $lines);
}
private function getLineMonospace(Line $line): string
@@ -43,14 +45,18 @@ private function getMetadataMonospace(Metadata $metadata): string
return '';
}
- $match = [];
- if (preg_match('/^start_of_(.*)/', $metadata->getName(), $match) === 1) {
- $content = (null !== $metadata->getValue()) ? $metadata->getValue()."\n" : mb_strtoupper($match[1]) . "\n";
+ if ($metadata->isSectionStart()) {
+ $type = $metadata->getSectionType();
+ $content = (null !== $metadata->getValue()) ? mb_strtoupper($metadata->getValue())."\n" : mb_strtoupper($type) . "\n";
return $content;
- } elseif (preg_match('/^end_of_(.*)/', $metadata->getName()) === 1) {
- return "\n";
+ } elseif ($metadata->isSectionEnd()) {
+ return '';
} else {
- return $metadata->getValue()."\n";
+ if ($metadata->isNameNecessary()) {
+ return $metadata->getHumanName().': '.$metadata->getValue()."\n";
+ } else {
+ return $metadata->getValue()."\n";
+ }
}
}
@@ -69,8 +75,10 @@ private function generateBlank(int $count): string
private function getLyricsMonospace(Lyrics $lyrics): string
{
- $lineChords = '';
+ $lineChords = [];
+ $lineChordsWithBlanks = '';
$lineTexts = '';
+ $lineTextsWithBlanks = '';
foreach ($lyrics->getBlocks() as $block) {
$chords = [];
@@ -85,16 +93,59 @@ private function getLyricsMonospace(Lyrics $lyrics): string
$chord = implode('/', $chords);
$text = $block->getText();
+ $textWithBlanks = $text;
if (mb_strlen($text) < mb_strlen($chord)) {
- $text = $text.$this->generateBlank(mb_strlen($chord) - mb_strlen($text));
+ $textWithBlanks = $text.$this->generateBlank(mb_strlen($chord) - mb_strlen($text));
}
- $lineChords .= $chord.$this->generateBlank(mb_strlen($text) - mb_strlen($chord));
+ $lineChordsWithBlanks .= $chord.$this->generateBlank(mb_strlen($text) - mb_strlen($chord));
+ $lineChords[] = $chord;
$lineTexts .= $text;
+ $lineTextsWithBlanks .= $textWithBlanks;
}
- return $lineChords."\n".$lineTexts."\n";
+ $output = '';
+ if ($lyrics->hasInlineChords()) {
+ $output .= '~'.$lineTexts."~".implode(' ', $lineChords)."\n";
+ } else {
+ if ($lyrics->hasChords() && $lyrics->hasText()) {
+ $output .= $lineChordsWithBlanks."\n";
+ $output .= $lineTextsWithBlanks."\n";
+ } elseif ($lyrics->hasChords()) {
+ $output .= implode(' ', $lineChords)."\n";
+ } elseif ($lyrics->hasText()) {
+ $output .= $lineTexts."\n";
+ }
+ }
+ return $output;
+ }
+
+ /**
+ * @param string[] $lines
+ */
+ private function transformInlineChords(array &$lines): void
+ {
+ $linesToFix = [];
+ $longest = 0;
+ foreach ($lines as $num => $line) {
+ $match = [];
+ if (preg_match('/^~(.+)~(.+)/', $line, $match) === 1) {
+ $linesToFix[$num] = [
+ 'text' => trim($match[1]),
+ 'chords' => trim($match[2]),
+ ];
+ if (mb_strlen($match[1]) > $longest) {
+ $longest = mb_strlen($match[1]);
+ }
+ }
+ }
+
+ $inlineChordPosition = $longest + 4;
+ foreach ($linesToFix as $num => $lineToFix) {
+ $lines[$num] = $lineToFix['text'].$this->generateBlank($inlineChordPosition - mb_strlen($lineToFix['text']));
+ $lines[$num] .= $lineToFix['chords']."\n";
+ }
}
private function getLyricsOnlyMonospace(Lyrics $lyrics): string
@@ -103,6 +154,6 @@ private function getLyricsOnlyMonospace(Lyrics $lyrics): string
foreach ($lyrics->getBlocks() as $block) {
$texts .= ltrim($block->getText());
}
- return $texts."\n";
+ return ($texts !== '') ? rtrim($texts)."\n" : '';
}
}
diff --git a/src/Line/Lyrics.php b/src/Line/Lyrics.php
index 7e8ad79..7bb793f 100644
--- a/src/Line/Lyrics.php
+++ b/src/Line/Lyrics.php
@@ -12,7 +12,7 @@ class Lyrics extends Line
/**
* @param \ChordPro\Block[] $blocks The blocks of the line.
*/
- public function __construct(private array $blocks)
+ public function __construct(private array $blocks = [], private bool $hasChords = false, private bool $hasText = false, private bool $hasInlineChords = false)
{
}
@@ -25,4 +25,19 @@ public function getBlocks(): array
{
return $this->blocks;
}
+
+ public function hasChords(): bool
+ {
+ return $this->hasChords;
+ }
+
+ public function hasText(): bool
+ {
+ return $this->hasText;
+ }
+
+ public function hasInlineChords(): bool
+ {
+ return $this->hasInlineChords;
+ }
}
diff --git a/src/Line/Metadata.php b/src/Line/Metadata.php
index fb71ccd..f113143 100644
--- a/src/Line/Metadata.php
+++ b/src/Line/Metadata.php
@@ -4,6 +4,8 @@
namespace ChordPro\Line;
+use PhpParser\Node\Expr\BinaryOp\BooleanOr;
+
/**
* A class that represents a metadata line in a song.
*/
@@ -21,6 +23,11 @@ public function getName(): string
return $this->name;
}
+ public function getNameSlug(): string
+ {
+ return $this->slugify($this->name);
+ }
+
public function getValue(): ?string
{
return $this->value;
@@ -54,4 +61,83 @@ private function convertToFullName(string $name): string
default => $name,
};
}
+
+ /**
+ * Get the human readable name of the metadata.
+ */
+ public function getHumanName(): string
+ {
+ return match($this->name) {
+ 'title' => 'Title',
+ 'subtitle' => 'Subtitle',
+ 'sorttitle' => 'Sort title',
+ 'comment' => 'Comment',
+ 'comment_italic' => 'Comment',
+ 'comment_box' => 'Comment',
+ 'key' => 'Key',
+ 'time' => 'Time',
+ 'tempo' => 'Tempo',
+ 'duration' => 'Duration',
+ 'capo' => 'Capo',
+ 'artist' => 'Artist',
+ 'composer' => 'Composer',
+ 'lyricist' => 'Lyricist',
+ 'album' => 'Album',
+ 'year' => 'Year',
+ 'copyright' => 'Copyright',
+ 'meta' => 'Meta',
+ default => $this->name,
+ };
+ }
+
+ public function isNameNecessary(): bool
+ {
+ return match($this->name) {
+ 'title' => false,
+ 'subtitle' => false,
+ 'comment' => false,
+ 'comment_italic' => false,
+ 'comment_box' => false,
+ 'artist' => false,
+ default => true,
+ };
+ }
+
+ public function isSectionStart(): bool
+ {
+ return str_starts_with($this->name, 'start_of_');
+ }
+
+ public function isSectionEnd(): bool
+ {
+ return str_starts_with($this->name, 'end_of_');
+ }
+
+ public function getSectionType(): string
+ {
+ $match = [];
+ if (preg_match('/^start_of_(.*)/', $this->name, $match) === 1) {
+ return $this->slugify($match[1]);
+ } elseif (preg_match('/^end_of_(.*)/', $this->name, $match) === 1) {
+ return $this->slugify($match[1]);
+ } else {
+ return '';
+ }
+ }
+
+ private function slugify(string $text): string
+ {
+ $text = preg_replace('~[^\pL\d]+~u', '-', $text);
+ $text = iconv('utf-8', 'us-ascii//TRANSLIT', (string) $text);
+ $text = preg_replace('~[^-\w]+~', '', (string) $text);
+ $text = trim((string) $text, '-');
+ $text = preg_replace('~-+~', '-', $text);
+ $text = strtolower((string) $text);
+
+ if ($text === '') {
+ return 'n-a';
+ }
+ return $text;
+ }
+
}
diff --git a/src/Parser.php b/src/Parser.php
index 7a1f9ed..6865d2b 100644
--- a/src/Parser.php
+++ b/src/Parser.php
@@ -23,22 +23,24 @@ class Parser
public function parse(string $text, array $sourceNotations = []): Song
{
$lines = [];
- $split = preg_split('~\R~', $text);
+ $split = preg_split("/\\r\\n|\\r|\\n/", $text);
if ($split !== false) {
foreach ($split as $line) {
- $line = trim($line);
- switch (substr($line, 0, 1)) {
+ $line = preg_replace('/^\s+/', '', $line);
+ $line = preg_replace('/\s+$/', '', (string) $line);
+
+ switch (substr((string) $line, 0, 1)) {
case "{":
- $lines[] = $this->parseMetadata($line);
+ $lines[] = $this->parseMetadata((string) $line);
break;
case "#":
- $lines[] = new Comment(trim(substr($line, 1)));
+ $lines[] = new Comment(trim(substr((string) $line, 1)));
break;
case "":
$lines[] = new EmptyLine();
break;
default:
- $lines[] = $this->parseLyrics($line, $sourceNotations);
+ $lines[] = $this->parseLyrics((string) $line, $sourceNotations);
}
}
}
@@ -82,6 +84,40 @@ private function parseMetadata(string $line): Metadata
private function parseLyrics(string $line, array $sourceNotations = []): Lyrics
{
$blocks = [];
+ $possiblyEmptyBlocks = [];
+
+ $hasText = false;
+ $hasChords = false;
+
+ // First, check for ~ symbol.
+ $match = [];
+ if (preg_match('/^([^\[\]]+)~(.+)/', $line, $match) === 1) {
+ $matchChords = [];
+ $lineText = $match[1];
+ $result = preg_match_all('/\[([^\[\]]+)\]/', $match[2], $matchChords);
+ if (is_numeric($result) && $result > 0) {
+ $lineChords = $matchChords[1];
+ $blocks[] = new Block(
+ chords: [],
+ text: trim($lineText)
+ );
+ foreach ($lineChords as $chord) {
+ $blocks[] = new Block(
+ chords: Chord::fromSlice($chord, $sourceNotations),
+ text: '',
+ lineEnd: true
+ );
+ }
+ return new Lyrics(
+ blocks: $blocks,
+ hasInlineChords: true,
+ hasChords: true,
+ hasText: true,
+ );
+ }
+
+ }
+
$explodedLine = explode('[', $line);
foreach($explodedLine as $num => $lineFragment) {
if ($lineFragment !== '') {
@@ -89,19 +125,28 @@ private function parseLyrics(string $line, array $sourceNotations = []): Lyrics
// If the fragment consists of only a chord without text.
if (isset($chordWithText[1]) && $chordWithText[1] == '') {
+ $hasChords = true;
$blocks[] = new Block(
chords: Chord::fromSlice($chordWithText[0], $sourceNotations),
text: ''
);
}
- // If first line begins with text and not a chord.
+ // If first block begins with text and not a chord.
elseif ($num == 0 && count($chordWithText) == 1) {
$blocks[] = new Block(
chords: [],
text: $chordWithText[0],
);
- // If there is a space after "]", threat it as separate blocks.
- } elseif (substr($chordWithText[1], 0, 1) == " ") {
+ if (preg_match('/\S/', $chordWithText[0]) === 1) {
+ $hasText = true;
+ } else {
+ // Save the block as possibly empty, so we can remove it later.
+ $possiblyEmptyBlocks[] = array_key_last($blocks);
+ }
+
+ } elseif (isset($chordWithText[1]) && substr($chordWithText[1], 0, 1) == " ") {
+ // If there is a space after "]", threat it as two separate blocks.
+ $hasChords = true;
$blocks[] = new Block(
chords: Chord::fromSlice($chordWithText[0], $sourceNotations),
text: ''
@@ -110,15 +155,45 @@ private function parseLyrics(string $line, array $sourceNotations = []): Lyrics
chords: [],
text: $chordWithText[1]
);
- // If there is no space after "]", threat it as chord with text.
+ if (preg_match('/\S/', $chordWithText[1]) === 1) {
+ $hasText = true;
+ } else {
+ // Save the block as possibly empty, so we can remove it later.
+ $possiblyEmptyBlocks[] = array_key_last($blocks);
+ }
+
} else {
+ // If there is no space after "]", threat it as chord with text.
+ $hasChords = true;
$blocks[] = new Block(
chords: Chord::fromSlice($chordWithText[0], $sourceNotations),
- text: $chordWithText[1]
+ text: $chordWithText[1] ?? ''
);
+ if (preg_match('/\S/', $chordWithText[1] ?? '') === 1) {
+ $hasText = true;
+ } else {
+ // Save the block as possibly empty, so we can remove it later.
+ $possiblyEmptyBlocks[] = array_key_last($blocks);
+ }
+
}
}
}
- return new Lyrics($blocks);
+
+ // If there are only chords and no text, set text to empty string.
+ if ($hasChords && !$hasText) {
+ foreach ($possiblyEmptyBlocks as $blockKey) {
+ unset($blocks[$blockKey]);
+ }
+ } elseif (!$hasChords && !$hasText) {
+ $blocks = [];
+ }
+
+ return new Lyrics(
+ blocks: $blocks,
+ hasInlineChords: false,
+ hasChords: $hasChords,
+ hasText: $hasText,
+ );
}
}
diff --git a/tests/BlockTest.php b/tests/BlockTest.php
index d67dd74..211b927 100644
--- a/tests/BlockTest.php
+++ b/tests/BlockTest.php
@@ -12,9 +12,10 @@ public function testBlockClassAccess(): void
{
$text = 'This is a test';
$chord = new Chord('C');
- $block = new Block([$chord], $text);
+ $block = new Block([$chord], $text, true);
$this->assertSame([$chord], $block->getChords(), 'Chords are not returned');
$this->assertSame($text, $block->getText(), 'Text is not returned');
+ $this->assertTrue($block->isLineEnd());
}
}
diff --git a/tests/Formatter/HtmlFormatterTest.php b/tests/Formatter/HtmlFormatterTest.php
index 0b47f38..1e72212 100644
--- a/tests/Formatter/HtmlFormatterTest.php
+++ b/tests/Formatter/HtmlFormatterTest.php
@@ -10,61 +10,47 @@ final class HtmlFormatterTest extends TestCase
{
public function testWithChords(): void
{
- $text = "{title: Test}\n\n{sov}\n[C7]Test [D]Test2\n{eov}\n# Comment";
+ $text = file_get_contents(__DIR__ . '/../data/song2.pro');
$parser = new Parser();
$song = $parser->parse($text);
$formatter = new HtmlFormatter();
$html = $formatter->format($song);
-
- $expected = '
Test
' . "\n" .
- '
' . "\n" .
- '
' . "\n" .
- '
' . "\n" .
- 'C7Test DTest2' . "\n" .
- '
' . "\n" .
- '
' . "\n";
-
+ $expected = file_get_contents(__DIR__ . '/../data/song2.html');
$this->assertSame($expected, $html, 'HTML output is not as expected');
+ for ($i = 1; $i <= 11; $i++) {
+ $this->assertStringContainsString('Test' . $i, $html);
+ }
}
public function testWithoutChords(): void
{
- $text = "{title: Test}\n\n{sov}\n[C7]Test [D]Test2\n{eov}\n# Comment";
+ $text = file_get_contents(__DIR__ . '/../data/song2.pro');
$parser = new Parser();
$song = $parser->parse($text);
$formatter = new HtmlFormatter();
$html = $formatter->format($song, [
'no_chords' => true
]);
-
- $expected = '
Test
' . "\n" .
- '
' . "\n" .
- '
' . "\n" .
- '
' . "\n" .
- 'Test Test2' . "\n" .
- '
' . "\n" .
- '
' . "\n";
-
+ $expected = file_get_contents(__DIR__ . '/../data/song2_no_chords.html');
$this->assertSame($expected, $html, 'HTML output is not as expected');
+ for ($i = 1; $i <= 11; $i++) {
+ $this->assertStringContainsString('Test' . $i, $html);
+ }
}
public function testWithoutMetadata(): void
{
- $text = "{title: Test}\n\n{sov}\n[C7]Test [D]Test2\n{eov}\n# Comment";
+ $text = file_get_contents(__DIR__ . '/../data/song2.pro');
$parser = new Parser();
$song = $parser->parse($text);
$formatter = new HtmlFormatter();
$html = $formatter->format($song, [
'ignore_metadata' => ['title']
]);
-
- $expected = '
' . "\n" .
- '
' . "\n" .
- '
' . "\n" .
- 'C7Test DTest2' . "\n" .
- '
' . "\n" .
- '
' . "\n";
-
+ $expected = file_get_contents(__DIR__ . '/../data/song2_no_metadata.html');
$this->assertSame($expected, $html, 'HTML output is not as expected');
+ for ($i = 1; $i <= 11; $i++) {
+ $this->assertStringContainsString('Test' . $i, $html);
+ }
}
}
diff --git a/tests/Formatter/JSONFormatterTest.php b/tests/Formatter/JSONFormatterTest.php
index 94ba9b3..84ab737 100644
--- a/tests/Formatter/JSONFormatterTest.php
+++ b/tests/Formatter/JSONFormatterTest.php
@@ -10,55 +10,47 @@ final class JSONFormatterTest extends TestCase
{
public function testWithChords(): void
{
- $text = "{title: Test}\n\n{sov}\n[C7]Test [D]Test2\n{eov}\n# Comment";
+ $text = file_get_contents(__DIR__ . '/../data/song2.pro');
$parser = new Parser();
$song = $parser->parse($text);
$formatter = new JSONFormatter();
$json = $formatter->format($song);
-
- $result = json_decode($json, true);
- $this->assertSame('metadata', $result[0]['type']);
- $this->assertSame('title', $result[0]['name']);
- $this->assertSame('Test', $result[0]['value']);
- $this->assertSame('empty_line', $result[1]['type']);
- $this->assertSame('metadata', $result[2]['type']);
- $this->assertSame('start_of_verse', $result[2]['name']);
- $this->assertNull($result[2]['value']);
- $this->assertSame('line', $result[3]['type']);
- $this->assertSame(2, count($result[3]['blocks']));
- $this->assertSame('metadata', $result[4]['type']);
- $this->assertSame('end_of_verse', $result[4]['name']);
- $this->assertNull($result[4]['value']);
- $this->assertSame('comment', $result[5]['type']);
- $this->assertSame('Comment', $result[5]['content']);
+ $expected = file_get_contents(__DIR__ . '/../data/song2.json');
+ $this->assertSame($expected, $json, 'JSON output is not as expected');
+ for ($i = 1; $i <= 11; $i++) {
+ $this->assertStringContainsString('Test' . $i, $json);
+ }
}
public function testWithoutChords(): void
{
- $text = "{title: Test}\n\n{sov}\n[C7]Test [D]Test2\n{eov}\n# Comment";
+ $text = file_get_contents(__DIR__ . '/../data/song2.pro');
$parser = new Parser();
$song = $parser->parse($text);
$formatter = new JSONFormatter();
$json = $formatter->format($song, [
'no_chords' => true
]);
-
- $result = json_decode($json, true);
- $this->assertSame('line', $result[3]['type']);
- $this->assertSame('Test Test2', $result[3]['text']);
+ $expected = file_get_contents(__DIR__ . '/../data/song2_no_chords.json');
+ $this->assertSame($expected, $json, 'JSON output is not as expected');
+ for ($i = 1; $i <= 11; $i++) {
+ $this->assertStringContainsString('Test' . $i, $json);
+ }
}
- public function textWithoutMetadata(): void
+ public function testWithoutMetadata(): void
{
- $text = "{title: Test}\n\n{sov}\n[C7]Test [D]Test2\n{eov}\n# Comment";
+ $text = file_get_contents(__DIR__ . '/../data/song2.pro');
$parser = new Parser();
$song = $parser->parse($text);
$formatter = new JSONFormatter();
$json = $formatter->format($song, [
'ignore_metadata' => ['title']
]);
-
- $result = json_decode($json, true);
- $this->assertSame('empty_line', $result[0]['type']);
+ $expected = file_get_contents(__DIR__ . '/../data/song2_no_metadata.json');
+ $this->assertSame($expected, $json, 'JSON output is not as expected');
+ for ($i = 1; $i <= 11; $i++) {
+ $this->assertStringContainsString('Test' . $i, $json);
+ }
}
}
diff --git a/tests/Formatter/MonospaceFormatterTest.php b/tests/Formatter/MonospaceFormatterTest.php
index df86cfe..f02d752 100644
--- a/tests/Formatter/MonospaceFormatterTest.php
+++ b/tests/Formatter/MonospaceFormatterTest.php
@@ -10,40 +10,47 @@ final class MonospaceFormatterTest extends TestCase
{
public function testWithChords(): void
{
- $text = "{title: Test}\n\n{sov}\n[C7]Test [D]Test2\n{eov}\n# Comment";
+ $text = file_get_contents(__DIR__ . '/../data/song2.pro');
$parser = new Parser();
$song = $parser->parse($text);
$formatter = new MonospaceFormatter();
$monospace = $formatter->format($song);
- $expected = "Test\n\nVERSE\nC7 D \nTest Test2\n\n";
+ $expected = file_get_contents(__DIR__ . '/../data/song2.txt');
$this->assertSame($expected, $monospace, 'Monospace output is not as expected');
+ for ($i = 1; $i <= 11; $i++) {
+ $this->assertStringContainsString('Test' . $i, $monospace);
+ }
}
public function testWithoutChords(): void
{
- $text = "{title: Test}\n\n{sov}\n[C7]Test [D]Test2\n{eov}\n# Comment";
+ $text = file_get_contents(__DIR__ . '/../data/song2.pro');
$parser = new Parser();
$song = $parser->parse($text);
$formatter = new MonospaceFormatter();
$monospace = $formatter->format($song, [
'no_chords' => true
]);
- $expected = "Test\n\nVERSE\nTest Test2\n\n";
-
+ $expected = file_get_contents(__DIR__ . '/../data/song2_no_chords.txt');
$this->assertSame($expected, $monospace, 'Monospace output is not as expected');
+ for ($i = 1; $i <= 11; $i++) {
+ $this->assertStringContainsString('Test' . $i, $monospace);
+ }
}
public function testWithoutMetadata(): void
{
- $text = "{title: Test}\n\n{sov}\n[C7]Test [D]Test2\n{eov}\n# Comment";
+ $text = file_get_contents(__DIR__ . '/../data/song2.pro');
$parser = new Parser();
$song = $parser->parse($text);
$formatter = new MonospaceFormatter();
$monospace = $formatter->format($song, [
'ignore_metadata' => ['title']
]);
- $expected = "\nVERSE\nC7 D \nTest Test2\n\n";
-
+ $expected = file_get_contents(__DIR__ . '/../data/song2_no_metadata.txt');
$this->assertSame($expected, $monospace, 'Monospace output is not as expected');
+ for ($i = 1; $i <= 11; $i++) {
+ $this->assertStringContainsString('Test' . $i, $monospace);
+ }
}
}
diff --git a/tests/Line/LyricsTest.php b/tests/Line/LyricsTest.php
index ee3c76b..86bc917 100644
--- a/tests/Line/LyricsTest.php
+++ b/tests/Line/LyricsTest.php
@@ -15,7 +15,10 @@ public function testClassAccess(): void
$chord = new Chord('C');
$block = new Block([$chord], $text);
- $lyrics = new Lyrics([$block]);
+ $lyrics = new Lyrics([$block], true, true, true);
$this->assertSame([$block], $lyrics->getBlocks(), 'Blocks are not returned');
+ $this->assertTrue($lyrics->hasChords());
+ $this->assertTrue($lyrics->hasText());
+ $this->assertTrue($lyrics->hasInlineChords());
}
}
diff --git a/tests/Line/MetadataTest.php b/tests/Line/MetadataTest.php
index b5bd1a4..580ee7e 100644
--- a/tests/Line/MetadataTest.php
+++ b/tests/Line/MetadataTest.php
@@ -17,6 +17,10 @@ public function testClassAccess(): void
$metadata2 = new Metadata('Test Name', null);
$this->assertEquals('Test Name', $metadata2->getName());
$this->assertNull($metadata2->getValue());
+
+ $metadata3 = new Metadata('key', 'C');
+ $this->assertEquals('Key', $metadata3->getHumanName());
+ $this->assertTrue($metadata3->isNameNecessary());
}
public static function shortcutProvider(): array
@@ -47,4 +51,23 @@ public function testShortcuts($shortName, $longName): void
$this->assertEquals($longName, $metadata->getName());
}
+
+ public function testSections(): void
+ {
+ $metadata = new Metadata('start_of_Zażółć%_JAŹŃ', 'TEST');
+ $this->assertEquals('start_of_Zażółć%_JAŹŃ', $metadata->getName());
+ $this->assertEquals('zazolc-jazn', $metadata->getSectionType());
+ $this->assertEquals('start-of-zazolc-jazn', $metadata->getNameSlug());
+ $this->assertEquals('TEST', $metadata->getValue());
+ $this->assertTrue($metadata->isSectionStart());
+ $this->assertFalse($metadata->isSectionEnd());
+
+ $metadata = new Metadata('end_of_Zażółć%_JAŹŃ', null);
+ $this->assertEquals('end_of_Zażółć%_JAŹŃ', $metadata->getName());
+ $this->assertEquals('zazolc-jazn', $metadata->getSectionType());
+ $this->assertEquals('end-of-zazolc-jazn', $metadata->getNameSlug());
+ $this->assertNull($metadata->getValue());
+ $this->assertFalse($metadata->isSectionStart());
+ $this->assertTrue($metadata->isSectionEnd());
+ }
}
diff --git a/tests/ParserTest.php b/tests/ParserTest.php
index 623cbb6..3d25c19 100644
--- a/tests/ParserTest.php
+++ b/tests/ParserTest.php
@@ -78,12 +78,12 @@ public function testParse(): void
assert($line instanceof Lyrics);
$this->assertCount(4, $line->getBlocks());
- // Sing it [D]loud, sing it [G7/E]cle[A#m]ar,
+ // Sing it [D]like ą, sing it [G7/E]cle[A#m]ar,
$this->assertSame('Sing it ', $line->getBlocks()[0]->getText());
$this->assertCount(0, $line->getBlocks()[0]->getChords());
- $this->assertSame('loud, sing it ', $line->getBlocks()[1]->getText());
+ $this->assertSame('like ą, sing it ', $line->getBlocks()[1]->getText());
$this->assertCount(1, $line->getBlocks()[1]->getChords());
$this->assertSame('D', $line->getBlocks()[1]->getChords()[0]->getRootChord());
$this->assertSame('', $line->getBlocks()[1]->getChords()[0]->getExt());
diff --git a/tests/data/song1.pro b/tests/data/song1.pro
index ba8c892..0b43490 100644
--- a/tests/data/song1.pro
+++ b/tests/data/song1.pro
@@ -8,7 +8,7 @@
{c: Seriously!}
{start_of_chorus: Refrain}
-Sing it [D]loud, sing it [G7/E]cle[A#m]ar,
+Sing it [D]like ą, sing it [G7/E]cle[A#m]ar,
{end_of_chorus}
{start_of_verse}
diff --git a/tests/data/song2.html b/tests/data/song2.html
new file mode 100644
index 0000000..111cae1
--- /dev/null
+++ b/tests/data/song2.html
@@ -0,0 +1,47 @@
+
Test
+
TestSub
+
+
Key: C
+
Verse
+
+
+Test1 Test2
+
+
+C7/ETest3 DTest4
+
+
+
+Test5 E Test6
+
+
+C7D
+
+
+
+CG
+
+
+CG
+
+
+x C G
+
+
+Test7~
+
+
+
+
+C7Test8 ~ D
+
+
+Test9 ~ D
+
+
+
+
EFMaj7/E
Test11 Longer Line Tab
+
+
diff --git a/tests/data/song2.json b/tests/data/song2.json
new file mode 100644
index 0000000..5f2906a
--- /dev/null
+++ b/tests/data/song2.json
@@ -0,0 +1,216 @@
+[
+ {
+ "type": "metadata",
+ "name": "title",
+ "value": "Test",
+ "humanName": "Title"
+ },
+ {
+ "type": "metadata",
+ "name": "subtitle",
+ "value": "TestSub",
+ "humanName": "Subtitle"
+ },
+ {
+ "type": "metadata",
+ "name": "comment",
+ "value": "Comment1",
+ "humanName": "Comment"
+ },
+ {
+ "type": "metadata",
+ "name": "key",
+ "value": "C",
+ "humanName": "Key"
+ },
+ {
+ "type": "section_start",
+ "sectionType": "verse",
+ "label": "Verse"
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "Test1 Test2"
+ }
+ ]
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "Test3",
+ "chord": "C7\/E",
+ "originalChord": "C7\/E"
+ },
+ {
+ "text": "Test4",
+ "chord": "D",
+ "originalChord": "D"
+ }
+ ]
+ },
+ {
+ "type": "empty_line"
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "Test5"
+ },
+ {
+ "chord": "E",
+ "originalChord": "E"
+ },
+ {
+ "text": " Test6"
+ }
+ ]
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "chord": "C7",
+ "originalChord": "C7"
+ },
+ {
+ "chord": "D",
+ "originalChord": "D"
+ }
+ ]
+ },
+ {
+ "type": "section_end",
+ "sectionType": "verse"
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "chord": "C",
+ "originalChord": "C"
+ },
+ {
+ "chord": "G",
+ "originalChord": "G"
+ }
+ ]
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "chord": "C",
+ "originalChord": "C"
+ },
+ {
+ "chord": "G",
+ "originalChord": "G"
+ }
+ ]
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "x"
+ },
+ {
+ "chord": "C",
+ "originalChord": "C"
+ },
+ {
+ "chord": "G",
+ "originalChord": "G"
+ }
+ ]
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "Test7~"
+ }
+ ]
+ },
+ {
+ "type": "empty_line"
+ },
+ {
+ "type": "section_start",
+ "sectionType": "chorus"
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "Test8 ~",
+ "chord": "C7",
+ "originalChord": "C7"
+ },
+ {
+ "chord": "D",
+ "originalChord": "D"
+ }
+ ]
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "Test9 ~"
+ },
+ {
+ "chord": "D",
+ "originalChord": "D"
+ }
+ ]
+ },
+ {
+ "type": "line_inline",
+ "blocks": [
+ {
+ "text": "Test10"
+ },
+ {
+ "chord": "D",
+ "originalChord": "D",
+ "lineEnd": true
+ },
+ {
+ "chord": "C",
+ "originalChord": "C",
+ "lineEnd": true
+ }
+ ]
+ },
+ {
+ "type": "line_inline",
+ "blocks": [
+ {
+ "text": "Test11 Longer Line Tab"
+ },
+ {
+ "chord": "E",
+ "originalChord": "E",
+ "lineEnd": true
+ },
+ {
+ "chord": "FMaj7\/E",
+ "originalChord": "FMaj7\/E",
+ "lineEnd": true
+ }
+ ]
+ },
+ {
+ "type": "section_end",
+ "sectionType": "chorus"
+ },
+ {
+ "type": "comment",
+ "content": "Comment"
+ }
+]
\ No newline at end of file
diff --git a/tests/data/song2.pro b/tests/data/song2.pro
new file mode 100644
index 0000000..b76a343
--- /dev/null
+++ b/tests/data/song2.pro
@@ -0,0 +1,23 @@
+{title: Test}
+{subtitle: TestSub}
+{c: Comment1}
+{key: C}
+{sov: Verse}
+Test1 Test2
+[C7/E]Test3 [D]Test4
+
+Test5 [E] Test6
+[C7] [D]
+{eov}
+[C][G]
+ [C][G]
+ x [C][G]
+Test7~
+
+{soc}
+ [C7]Test8 ~ [D]
+ Test9 ~ [D
+ Test10 ~ [D][C]
+ Test11 Longer Line Tab ~ [E][FMaj7/E]
+{eoc}
+# Comment
\ No newline at end of file
diff --git a/tests/data/song2.txt b/tests/data/song2.txt
new file mode 100644
index 0000000..c4ab203
--- /dev/null
+++ b/tests/data/song2.txt
@@ -0,0 +1,25 @@
+Test
+TestSub
+Comment1
+Key: C
+VERSE
+Test1 Test2
+C7/E D
+Test3 Test4
+
+ E
+Test5 Test6
+C7 D
+C G
+C G
+ CG
+x
+Test7~
+
+CHORUS
+C7 D
+Test8 ~
+ D
+Test9 ~
+Test10 D C
+Test11 Longer Line Tab E FMaj7/E
diff --git a/tests/data/song2_no_chords.html b/tests/data/song2_no_chords.html
new file mode 100644
index 0000000..2c3416f
--- /dev/null
+++ b/tests/data/song2_no_chords.html
@@ -0,0 +1,38 @@
+
Test
+
TestSub
+
+
Key: C
+
Verse
+
+
+Test1 Test2
+
+
+Test3 Test4
+
+
+
+Test5 Test6
+
+
+
+x
+
+
+Test7~
+
+
+
+
+Test8 ~
+
+
+Test9 ~
+
+
+Test10
+
+
+Test11 Longer Line Tab
+
+
diff --git a/tests/data/song2_no_chords.json b/tests/data/song2_no_chords.json
new file mode 100644
index 0000000..c8e514d
--- /dev/null
+++ b/tests/data/song2_no_chords.json
@@ -0,0 +1,89 @@
+[
+ {
+ "type": "metadata",
+ "name": "title",
+ "value": "Test",
+ "humanName": "Title"
+ },
+ {
+ "type": "metadata",
+ "name": "subtitle",
+ "value": "TestSub",
+ "humanName": "Subtitle"
+ },
+ {
+ "type": "metadata",
+ "name": "comment",
+ "value": "Comment1",
+ "humanName": "Comment"
+ },
+ {
+ "type": "metadata",
+ "name": "key",
+ "value": "C",
+ "humanName": "Key"
+ },
+ {
+ "type": "section_start",
+ "sectionType": "verse",
+ "label": "Verse"
+ },
+ {
+ "type": "line",
+ "text": "Test1 Test2"
+ },
+ {
+ "type": "line",
+ "text": "Test3 Test4"
+ },
+ {
+ "type": "empty_line"
+ },
+ {
+ "type": "line",
+ "text": "Test5 Test6"
+ },
+ {
+ "type": "section_end",
+ "sectionType": "verse"
+ },
+ {
+ "type": "line",
+ "text": "x"
+ },
+ {
+ "type": "line",
+ "text": "Test7~"
+ },
+ {
+ "type": "empty_line"
+ },
+ {
+ "type": "section_start",
+ "sectionType": "chorus"
+ },
+ {
+ "type": "line",
+ "text": "Test8 ~"
+ },
+ {
+ "type": "line",
+ "text": "Test9 ~"
+ },
+ {
+ "type": "line",
+ "text": "Test10"
+ },
+ {
+ "type": "line",
+ "text": "Test11 Longer Line Tab"
+ },
+ {
+ "type": "section_end",
+ "sectionType": "chorus"
+ },
+ {
+ "type": "comment",
+ "content": "Comment"
+ }
+]
\ No newline at end of file
diff --git a/tests/data/song2_no_chords.txt b/tests/data/song2_no_chords.txt
new file mode 100644
index 0000000..5b7a165
--- /dev/null
+++ b/tests/data/song2_no_chords.txt
@@ -0,0 +1,17 @@
+Test
+TestSub
+Comment1
+Key: C
+VERSE
+Test1 Test2
+Test3 Test4
+
+Test5 Test6
+x
+Test7~
+
+CHORUS
+Test8 ~
+Test9 ~
+Test10
+Test11 Longer Line Tab
diff --git a/tests/data/song2_no_metadata.html b/tests/data/song2_no_metadata.html
new file mode 100644
index 0000000..3d29754
--- /dev/null
+++ b/tests/data/song2_no_metadata.html
@@ -0,0 +1,46 @@
+
TestSub
+
+
Key: C
+
Verse
+
+
+Test1 Test2
+
+
+C7/ETest3 DTest4
+
+
+
+Test5 E Test6
+
+
+C7D
+
+
+
+CG
+
+
+CG
+
+
+x C G
+
+
+Test7~
+
+
+
+
+C7Test8 ~ D
+
+
+Test9 ~ D
+
+
+
+
EFMaj7/E
Test11 Longer Line Tab
+
+
diff --git a/tests/data/song2_no_metadata.json b/tests/data/song2_no_metadata.json
new file mode 100644
index 0000000..3f0cb27
--- /dev/null
+++ b/tests/data/song2_no_metadata.json
@@ -0,0 +1,210 @@
+[
+ {
+ "type": "metadata",
+ "name": "subtitle",
+ "value": "TestSub",
+ "humanName": "Subtitle"
+ },
+ {
+ "type": "metadata",
+ "name": "comment",
+ "value": "Comment1",
+ "humanName": "Comment"
+ },
+ {
+ "type": "metadata",
+ "name": "key",
+ "value": "C",
+ "humanName": "Key"
+ },
+ {
+ "type": "section_start",
+ "sectionType": "verse",
+ "label": "Verse"
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "Test1 Test2"
+ }
+ ]
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "Test3",
+ "chord": "C7\/E",
+ "originalChord": "C7\/E"
+ },
+ {
+ "text": "Test4",
+ "chord": "D",
+ "originalChord": "D"
+ }
+ ]
+ },
+ {
+ "type": "empty_line"
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "Test5"
+ },
+ {
+ "chord": "E",
+ "originalChord": "E"
+ },
+ {
+ "text": " Test6"
+ }
+ ]
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "chord": "C7",
+ "originalChord": "C7"
+ },
+ {
+ "chord": "D",
+ "originalChord": "D"
+ }
+ ]
+ },
+ {
+ "type": "section_end",
+ "sectionType": "verse"
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "chord": "C",
+ "originalChord": "C"
+ },
+ {
+ "chord": "G",
+ "originalChord": "G"
+ }
+ ]
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "chord": "C",
+ "originalChord": "C"
+ },
+ {
+ "chord": "G",
+ "originalChord": "G"
+ }
+ ]
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "x"
+ },
+ {
+ "chord": "C",
+ "originalChord": "C"
+ },
+ {
+ "chord": "G",
+ "originalChord": "G"
+ }
+ ]
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "Test7~"
+ }
+ ]
+ },
+ {
+ "type": "empty_line"
+ },
+ {
+ "type": "section_start",
+ "sectionType": "chorus"
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "Test8 ~",
+ "chord": "C7",
+ "originalChord": "C7"
+ },
+ {
+ "chord": "D",
+ "originalChord": "D"
+ }
+ ]
+ },
+ {
+ "type": "line",
+ "blocks": [
+ {
+ "text": "Test9 ~"
+ },
+ {
+ "chord": "D",
+ "originalChord": "D"
+ }
+ ]
+ },
+ {
+ "type": "line_inline",
+ "blocks": [
+ {
+ "text": "Test10"
+ },
+ {
+ "chord": "D",
+ "originalChord": "D",
+ "lineEnd": true
+ },
+ {
+ "chord": "C",
+ "originalChord": "C",
+ "lineEnd": true
+ }
+ ]
+ },
+ {
+ "type": "line_inline",
+ "blocks": [
+ {
+ "text": "Test11 Longer Line Tab"
+ },
+ {
+ "chord": "E",
+ "originalChord": "E",
+ "lineEnd": true
+ },
+ {
+ "chord": "FMaj7\/E",
+ "originalChord": "FMaj7\/E",
+ "lineEnd": true
+ }
+ ]
+ },
+ {
+ "type": "section_end",
+ "sectionType": "chorus"
+ },
+ {
+ "type": "comment",
+ "content": "Comment"
+ }
+]
\ No newline at end of file
diff --git a/tests/data/song2_no_metadata.txt b/tests/data/song2_no_metadata.txt
new file mode 100644
index 0000000..4e8d41d
--- /dev/null
+++ b/tests/data/song2_no_metadata.txt
@@ -0,0 +1,24 @@
+TestSub
+Comment1
+Key: C
+VERSE
+Test1 Test2
+C7/E D
+Test3 Test4
+
+ E
+Test5 Test6
+C7 D
+C G
+C G
+ CG
+x
+Test7~
+
+CHORUS
+C7 D
+Test8 ~
+ D
+Test9 ~
+Test10 D C
+Test11 Longer Line Tab E FMaj7/E
diff --git a/web/example.css b/web/example.css
index 4192e3e..53c735c 100644
--- a/web/example.css
+++ b/web/example.css
@@ -1,9 +1,15 @@
body {
- font-family: Helvetica;
+ font-family: Helvetica;
}
/* Metadata */
+.chordpro-section-label,
+.chordpro-metadata {
+ font-size: 0.9em;
+ font-style: italic;
+ margin: 0.5em 0;
+}
.chordpro-title {
font-size: 2em;
font-weight: bold;
@@ -12,10 +18,6 @@ body {
font-size: 1.5em;
font-weight: light;
}
-.chordpro-comment {
- font-style: italic;
- font-size: 0.9em;
-}
.chordpro-key {
display: inline-block;
font-size: 1.5em;
@@ -27,33 +29,34 @@ body {
font-weight: bold;
margin: 1em 0;
}
+.chordpro-tab {
+ font-family: monospace;
+}
/* Lines & Chorus */
-body.chordpro-line:first-of-type {
- border-top: 1px solid #000;
- padding-top: 1em;
- margin-top: 1em;
-}
.chordpro-line {
- height: 2.5em;
+ height: 2.5em;
+}
+.chordpro-line-text-only, .chordpro-line-chords-only {
+ height: 1.5em;
}
.chordpro-chorus {
- padding-left: 10px;
- border-left: 4px solid #777;
+ padding-left: 10px;
+ border-left: 4px solid #777;
}
.chordpro-block {
- position: relative;
- display: inline-block;
+ position: relative;
+ display: inline-block;
}
.chordpro-chord {
- position: relative;
- display: block;
- padding-right: 5px;
- font-weight: bold;
- font-size: 0.9em;
+ position: relative;
+ display: inline-block;
+ padding-right: 5px;
+ font-weight: bold;
+ font-size: 0.9em;
}
.chordpro-text {
- position: relative;
- display: block;
+ position: relative;
+ display: block;
}
diff --git a/web/example.php b/web/example.php
index 705d8fd..611e044 100644
--- a/web/example.php
+++ b/web/example.php
@@ -1,23 +1,39 @@