chordsheetjs v9.0.2
ChordSheetJS
A JavaScript library for parsing and formatting chord sheets
Contents
Installation
Package managers
ChordSheetJS
is on npm, to install run:
npm install chordsheetjs
Load with import
:
import ChordSheetJS from 'chordsheetjs';
or require()
:
var ChordSheetJS = require('chordsheetjs').default;
Standalone bundle file
If you're not using a build tool, you can download and use the bundle.js
from the
latest release:
<script src="bundle.js"></script>
<script>
// ChordSheetJS is available in global namespace now
const parser = new ChordSheetJS.ChordProParser();
</script>
How to ...?
Parse chord sheet
Regular chord sheets
const chordSheet = `
Am C/G F C
Let it be, let it be, let it be, let it be
C G F C/E Dm C
Whisper words of wisdom, let it be`.substring(1);
const parser = new ChordSheetJS.ChordsOverWordsParser();
const song = parser.parse(chordSheet);
Ultimate Guitar chord sheets
const chordSheet = `
[Chorus]
Am C/G F C
Let it be, let it be, let it be, let it be
C G F C/E Dm C
Whisper words of wisdom, let it be`.substring(1);
const parser = new ChordSheetJS.UltimateGuitarParser();
const song = parser.parse(chordSheet);
Chord pro format
const chordSheet = `
{title: Let it be}
{subtitle: ChordSheetJS example version}
{start_of_chorus: Chorus}
Let it [Am]be, let it [C/G]be, let it [F]be, let it [C]be
[C]Whisper words of [G]wisdom, let it [F]be [C/E] [Dm] [C]
{end_of_chorus}`.substring(1);
const parser = new ChordSheetJS.ChordProParser();
const song = parser.parse(chordSheet);
Display a parsed sheet
Plain text format
const formatter = new ChordSheetJS.TextFormatter();
const disp = formatter.format(song);
HTML format
Table-based layout
const formatter = new ChordSheetJS.HtmlTableFormatter();
const disp = formatter.format(song);
Div-based layout
const formatter = new ChordSheetJS.HtmlDivFormatter();
const disp = formatter.format(song);
Chord pro format
const formatter = new ChordSheetJS.ChordProFormatter();
const disp = formatter.format(song);
Serialize/deserialize
Chord sheets (Song
s) can be serialized to plain JavaScript objects, which can be converted to JSON, XML etc by
third-party libraries. The serialized object can also be deserialized back into a Song
.
const serializedSong = new ChordSheetSerializer().serialize(song);
const deserialized = new ChordSheetSerializer().deserialize(serializedSong);
Add styling
The HTML formatters (HtmlTableFormatter and HtmlDivFormatter) can provide basic CSS to help with styling the output:
HtmlTableFormatter.cssString();
// .paragraph {
// margin-bottom: 1em;
// }
HtmlTableFormatter.cssString('.chordSheetViewer');
// .chordSheetViewer .paragraph {
// margin-bottom: 1em;
// }
HtmlTableFormatter.cssObject();
// '.paragraph': {
// marginBottom: '1em'
// }
Parsing and modifying chords
import { Chord } from 'chordsheetjs';
Parse
const chord = Chord.parse('Ebsus4/Bb');
Parse numeric chords (Nashville system):
const chord = Chord.parse('b1sus4/#3');
Display with #toString
Use #toString() to convert the chord to a chord string (eg Dsus/F#)
const chord = Chord.parse('Ebsus4/Bb');
chord.toString(); // --> "Ebsus4/Bb"
Clone
var chord2 = chord.clone();
Normalize
Normalizes keys B#, E#, Cb and Fb to C, F, B and E
const chord = Chord.parse('E#/B#');
normalizedChord = chord.normalize();
normalizedChord.toString(); // --> "F/C"
Switch modifier
Deprecated
Convert # to b and vice versa
const chord = parseChord('Eb/Bb');
const chord2 = chord.switchModifier();
chord2.toString(); // --> "D#/A#"
Use specific modifier
Set the chord to a specific modifier (# or b)
const chord = Chord.parse('Eb/Bb');
const chord2 = chord.useModifier('#');
chord2.toString(); // --> "D#/A#"
const chord = Chord.parse('Eb/Bb');
const chord2 = chord.useModifier('b');
chord2.toString(); // --> "Eb/Bb"
Transpose up
const chord = Chord.parse('Eb/Bb');
const chord2 = chord.transposeUp();
chord2.toString(); // -> "E/B"
Transpose down
const chord = Chord.parse('Eb/Bb');
const chord2 = chord.transposeDown();
chord2.toString(); // -> "D/A"
Transpose
const chord = Chord.parse('C/E');
const chord2 = chord.transpose(4);
chord2.toString(); // -> "E/G#"
const chord = Chord.parse('C/E');
const chord2 = chord.transpose(-4);
chord2.toString(); // -> "Ab/C"
Convert numeric chord to chord symbol
const numericChord = Chord.parse('2/4');
const chordSymbol = numericChord.toChordSymbol('E');
chordSymbol.toString(); // -> "F#/A"
Supported ChordPro directives
All directives are parsed and are added to Song.metadata
. The list below indicates whether formatters actually
use those to change the generated output.
:heavy_check_mark: = supported
:clock2: = will be supported in a future version
:heavy_multiplication_x: = currently no plans to support it in the near future
Meta-data directives
Directive | Support |
---|---|
title (short: t) | :heavy_check_mark: |
subtitle | :heavy_check_mark: |
artist | :heavy_check_mark: |
composer | :heavy_check_mark: |
lyricist | :heavy_check_mark: |
copyright | :heavy_check_mark: |
album | :heavy_check_mark: |
year | :heavy_check_mark: |
key | :heavy_check_mark: |
time | :heavy_check_mark: |
tempo | :heavy_check_mark: |
duration | :heavy_check_mark: |
capo | :heavy_check_mark: |
meta | :heavy_check_mark: |
Formatting directives
Directive | Support |
---|---|
comment (short: c) | :heavy_check_mark: |
comment_italic (short: ci) | :heavy_multiplication_x: |
comment_box (short: cb) | :heavy_multiplication_x: |
chorus | :heavy_multiplication_x: |
image | :heavy_multiplication_x: |
Environment directives
Directive | Support |
---|---|
start_of_chorus (short: soc) | :heavy_check_mark: |
end_of_chorus (short: eoc) | :heavy_check_mark: |
start_of_verse | :heavy_check_mark: |
end_of_verse | :heavy_check_mark: |
start_of_tab (short: sot) | :heavy_check_mark: |
end_of_tab (short: eot) | :heavy_check_mark: |
start_of_grid | :heavy_multiplication_x: |
end_of_grid | :heavy_multiplication_x: |
Chord diagrams
Directive | Support |
---|---|
define | :heavy_multiplication_x: |
chord | :heavy_multiplication_x: |
Fonts, sizes and colours
Directive | Support |
---|---|
textfont | :heavy_check_mark: |
textsize | :heavy_check_mark: |
textcolour | :heavy_check_mark: |
chordfont | :heavy_check_mark: |
chordsize | :heavy_check_mark: |
chordcolour | :heavy_check_mark: |
tabfont | :heavy_multiplication_x: |
tabsize | :heavy_multiplication_x: |
tabcolour | :heavy_multiplication_x: |
Output related directives
Directive | Support |
---|---|
new_page (short: np) | :heavy_multiplication_x: |
new_physical_page (short: npp) | :heavy_multiplication_x: |
column_break (short: cb) | :heavy_multiplication_x: |
grid (short: g) | :heavy_multiplication_x: |
no_grid (short: ng) | :heavy_multiplication_x: |
titles | :heavy_multiplication_x: |
columns (short: col) | :heavy_multiplication_x: |
Custom extensions
Directive | Support |
---|---|
x_ | :heavy_check_mark: |
API docs
Note: all classes, methods and constants that are documented here can be considered public API and will only be subject to breaking changes between major versions.
Classes
Constants
Functions
ChordLyricsPair
Kind: global class
- ChordLyricsPair
- new ChordLyricsPair(chords, lyrics, annotation)
- .chords : string
- .lyrics : string
- .annotation : string
- .isRenderable() ⇒ boolean
- .clone() ⇒ ChordLyricsPair
new ChordLyricsPair(chords, lyrics, annotation)
Param | Type | Default | Description |
---|---|---|---|
chords | string | The chords | |
lyrics | string | null | null | The lyrics |
annotation | string | null | null | The annotation |
chordLyricsPair.chords : string
Kind: instance property of ChordLyricsPair
chordLyricsPair.lyrics : string
Kind: instance property of ChordLyricsPair
chordLyricsPair.annotation : string
Kind: instance property of ChordLyricsPair
chordLyricsPair.isRenderable() ⇒ boolean
Kind: instance method of ChordLyricsPair
chordLyricsPair.clone() ⇒ ChordLyricsPair
Kind: instance method of ChordLyricsPair
Comment
Kind: global class
- Comment
- .isRenderable() ⇒ boolean
- .clone() ⇒ Comment
comment.isRenderable() ⇒ boolean
Kind: instance method of Comment
comment.clone() ⇒ Comment
Kind: instance method of Comment
Line
Kind: global class
- Line
- .isEmpty() ⇒ boolean
- .addItem(item)
- .hasRenderableItems() ⇒ boolean
- .clone() ⇒ Line
- .isBridge() ⇒ boolean
- .isChorus() ⇒ boolean
- .isGrid() ⇒ boolean
- .isTab() ⇒ boolean
- .isVerse() ⇒ boolean
.hasContent() ⇒ boolean
line.isEmpty() ⇒ boolean
Kind: instance method of Line
line.addItem(item)
Kind: instance method of Line
Param | Type | Description |
---|---|---|
item | ChordLyricsPair | Tag | The item to be added |
line.hasRenderableItems() ⇒ boolean
Kind: instance method of Line
line.clone() ⇒ Line
Kind: instance method of Line
line.isBridge() ⇒ boolean
Kind: instance method of Line
line.isChorus() ⇒ boolean
Kind: instance method of Line
line.isGrid() ⇒ boolean
Kind: instance method of Line
line.isTab() ⇒ boolean
Kind: instance method of Line
line.isVerse() ⇒ boolean
Kind: instance method of Line
line.hasContent() ⇒ boolean
Deprecated
Kind: instance method of Line
Metadata
Kind: global class
- Metadata
- .get(prop) ⇒ Array.<String> | String
- .clone() ⇒ Metadata
metadata.get(prop) ⇒ Array.<String> | String
Kind: instance method of Metadata
Returns: Array.<String> | String - the metadata value(s). If there is only one value, it will return a String,
else it returns an array of strings.
Param | Description |
---|---|
prop | the property name |
metadata.clone() ⇒ Metadata
Kind: instance method of Metadata
Returns: Metadata - the cloned Metadata object
Paragraph
Kind: global class
- Paragraph
- .contents ⇒ string
- .label ⇒ string | null
- .type ⇒ string
- .isLiteral() ⇒ boolean
- .hasRenderableItems() ⇒ boolean
paragraph.contents ⇒ string
Kind: instance property of Paragraph
paragraph.label ⇒ string | null
Kind: instance property of Paragraph
paragraph.type ⇒ string
Kind: instance property of Paragraph
paragraph.isLiteral() ⇒ boolean
Kind: instance method of Paragraph
See: contents
paragraph.hasRenderableItems() ⇒ boolean
Kind: instance method of Paragraph
See: Line.hasRenderableItems
Song
Kind: global class
- Song
- new Song(metadata)
- .bodyLines ⇒ Array.<Line>
- .bodyParagraphs ⇒ Array.<Paragraph>
- .paragraphs : Array.<Paragraph>
- .expandedBodyParagraphs : Array.<Paragraph>
- .clone() ⇒ Song
- .setKey(key) ⇒ Song
- .setCapo(capo) ⇒ Song
- .transpose(delta, [options]) ⇒ Song
- .transposeUp([options]) ⇒ Song
- .transposeDown([options]) ⇒ Song
- .changeKey(newKey) ⇒ Song
- .changeMetadata(name, value)
- .mapItems(func) ⇒ Song
- .mapLines(func) ⇒ Song
new Song(metadata)
Param | Type | Description |
---|---|---|
metadata | Object | Metadata | predefined metadata |
song.bodyLines ⇒ Array.<Line>
Kind: instance property of Song
Returns: Array.<Line> - The song body lines
song.bodyParagraphs ⇒ Array.<Paragraph>
Kind: instance property of Song
See: bodyLines
song.paragraphs : Array.<Paragraph>
Kind: instance property of Song
song.expandedBodyParagraphs : Array.<Paragraph>
Kind: instance property of Song
song.clone() ⇒ Song
Kind: instance method of Song
Returns: Song - The cloned song
song.setKey(key) ⇒ Song
Kind: instance method of Song
Returns: Song - The changed song
Param | Type | Description |
---|---|---|
key | number | null | the key. Passing null will: remove the current key from metadata remove any key directive |
song.setCapo(capo) ⇒ Song
Kind: instance method of Song
Returns: Song - The changed song
Param | Type | Description |
---|---|---|
capo | number | null | the capo. Passing null will: remove the current key from metadata remove any capo directive |
song.transpose(delta, options) ⇒ Song
Kind: instance method of Song
Returns: Song - The transposed song
Param | Type | Default | Description |
---|---|---|---|
delta | number | The number of semitones (positive or negative) to transpose with | |
options | Object | {} | options |
options.normalizeChordSuffix | boolean | false | whether to normalize the chord suffixes after transposing |
song.transposeUp(options) ⇒ Song
Kind: instance method of Song
Returns: Song - The transposed song
Param | Type | Default | Description |
---|---|---|---|
options | Object | {} | options |
options.normalizeChordSuffix | boolean | false | whether to normalize the chord suffixes after transposing |
song.transposeDown(options) ⇒ Song
Kind: instance method of Song
Returns: Song - The transposed song
Param | Type | Default | Description |
---|---|---|---|
options | Object | {} | options |
options.normalizeChordSuffix | boolean | false | whether to normalize the chord suffixes after transposing |
song.changeKey(newKey) ⇒ Song
Kind: instance method of Song
Returns: Song - The changed song
Param | Type | Description |
---|---|---|
newKey | string | The new key. |
song.changeMetadata(name, value)
Kind: instance method of Song
Param | Type | Description |
---|---|---|
name | string | The directive name |
value | string | null | The value to set, or null to remove the directive |
song.mapItems(func) ⇒ Song
Kind: instance method of Song
Returns: Song - the changed song
Param | Type | Description |
---|---|---|
func | MapItemsCallback | the callback function |
Example
// transpose all chords:
song.mapItems((item) => {
if (item instanceof ChordLyricsPair) {
return item.transpose(2, 'D');
}
return item;
});
song.mapLines(func) ⇒ Song
Kind: instance method of Song
Returns: Song - the changed song
Param | Type | Description |
---|---|---|
func | MapLinesCallback | the callback function |
Example
// remove lines with only Tags:
song.mapLines((line) => {
if (line.items.every(item => item instanceof Tag)) {
return null;
}
return line;
});
Tag
Kind: global class
- Tag
- .name : string
- .originalName : string
- .value : string
- .hasValue() ⇒ boolean
- .isRenderable() ⇒ boolean
- .hasRenderableLabel()
- .isMetaTag() ⇒ boolean
- .clone() ⇒ Tag
tag.name : string
Kind: instance property of Tag
tag.originalName : string
Kind: instance property of Tag
tag.value : string
Kind: instance property of Tag
tag.hasValue() ⇒ boolean
Kind: instance method of Tag
tag.isRenderable() ⇒ boolean
Kind: instance method of Tag
tag.hasRenderableLabel()
Kind: instance method of Tag
tag.isMetaTag() ⇒ boolean
Kind: instance method of Tag
tag.clone() ⇒ Tag
Kind: instance method of Tag
Returns: Tag - The cloned tag
ChordProFormatter
Kind: global class
chordProFormatter.format(song) ⇒ string
Kind: instance method of ChordProFormatter
Returns: string - The ChordPro string
Param | Type | Description |
---|---|---|
song | Song | The song to be formatted |
ChordsOverWordsFormatter
Kind: global class
chordsOverWordsFormatter.format(song) ⇒ string
Kind: instance method of ChordsOverWordsFormatter
Returns: string - the chord sheet
Param | Type | Description |
---|---|---|
song | Song | The song to be formatted |
Formatter
Kind: global class
new Formatter(configuration)
Param | Type | Default | Description |
---|---|---|---|
configuration | Object | {} | options |
configuration.evaluate | boolean | false | Whether or not to evaluate meta expressions. For more info about meta expressions, see: https://bit.ly/2SC9c2u |
configuration.metadata | object | {} | |
configuration.metadata.separator | string | "\", \"" | The separator to be used when rendering a metadata value that has multiple values. See: https://bit.ly/2SC9c2u |
configuration.key | Key | string | The key to use for rendering. The chord sheet will be transposed from the song's original key (as indicated by the {key} directive) to the specified key. Note that transposing will only work if the original song key is set. | |
configuration.expandChorusDirective | boolean | false | Whether or not to expand {chorus} directives by rendering the last defined chorus inline after the directive. |
configuration.useUnicodeModifiers | boolean | false | Whether or not to use unicode flat and sharp symbols. |
configuration.normalizeChords | boolean | true | Whether or not to automatically normalize chords |
HtmlDivFormatter
Kind: global class
HtmlFormatter
Kind: global class
- HtmlFormatter
- .cssObject ⇒ Object.<string, Object.<string, string>>
- .format(song) ⇒ string
- .cssString(scope) ⇒ string
htmlFormatter.cssObject ⇒ Object.<string, Object.<string, string>>
Kind: instance property of HtmlFormatter
Returns: Object.<string, Object.<string, string>> - the CSS object
htmlFormatter.format(song) ⇒ string
Kind: instance method of HtmlFormatter
Returns: string - The HTML string
Param | Type | Description |
---|---|---|
song | Song | The song to be formatted |
htmlFormatter.cssString(scope) ⇒ string
Kind: instance method of HtmlFormatter
Returns: string - the CSS string
Param | Description |
---|---|
scope | the CSS scope to use, for example .chordSheetViewer |
HtmlTableFormatter
Kind: global class
TextFormatter
Kind: global class
textFormatter.format(song) ⇒ string
Kind: instance method of TextFormatter
Returns: string - the chord sheet
Param | Type | Description |
---|---|---|
song | Song | The song to be formatted |
ChordProParser
Kind: global class
chordProParser.parse(chordProChordSheet) ⇒ Song
Kind: instance method of ChordProParser
Returns: Song - The parsed song
Param | Type | Description |
---|---|---|
chordProChordSheet | string | the ChordPro chord sheet |
ChordSheetParser
Deprecated
Kind: global class
new ChordSheetParser(options)
Param | Type | Default | Description |
---|---|---|---|
options | Object | {} | options |
options.preserveWhitespace | boolean | true | whether to preserve trailing whitespace for chords |
chordSheetParser.parse(chordSheet, options) ⇒ Song
Kind: instance method of ChordSheetParser
Returns: Song - The parsed song
Param | Type | Default | Description |
---|---|---|---|
chordSheet | string | The ChordPro chord sheet | |
options | Object | {} | Optional parser options |
options.song | Song | The Song to store the song data in |
ChordsOverWordsParser
Kind: global class
chordsOverWordsParser.parse(chordsOverWordsSheet) ⇒ Song
Kind: instance method of ChordsOverWordsParser
Returns: Song - The parsed song
Param | Type | Description |
---|---|---|
chordsOverWordsSheet | string | the chords over words sheet |
ParserWarning
Kind: global class
parserWarning.toString() ⇒ string
Kind: instance method of ParserWarning
Returns: string - The string warning
PegBasedParser
Kind: global class
pegBasedParser.warnings : Array.<ParserWarning>
Kind: instance property of PegBasedParser
UltimateGuitarParser
Kind: global class
new UltimateGuitarParser(options)
Param | Type | Default | Description |
---|---|---|---|
options | Object | {} | options |
options.preserveWhitespace | boolean | true | whether to preserve trailing whitespace for chords |
Chord
Kind: global class
- Chord
- instance
- .clone() ⇒ Chord
- .toChordSymbol([referenceKey]) ⇒ Chord
- .toChordSymbolString([referenceKey]) ⇒ string
- .isChordSymbol() ⇒ boolean
- .toChordSolfege([referenceKey]) ⇒ Chord
- .toChordSolfegeString([referenceKey]) ⇒ string
- .isChordSolfege() ⇒ boolean
- .toNumeric([referenceKey]) ⇒ Chord
- .toNumeral([referenceKey]) ⇒ Chord
- .toNumeralString([referenceKey]) ⇒ string
- .isNumeric() ⇒ boolean
- .toNumericString([referenceKey]) ⇒ string
- .isNumeral() ⇒ boolean
- .toString([configuration]) ⇒ string
- .normalize([key], [options]) ⇒ Chord
- .useModifier(newModifier) ⇒ Chord
- .transposeUp() ⇒ Chord
- .transposeDown() ⇒ Chord
- .transpose(delta) ⇒ Chord
- static
- .parse(chordString) ⇒ Chord | null
- instance
chord.clone() ⇒ Chord
Kind: instance method of Chord
chord.toChordSymbol(referenceKey) ⇒ Chord
Kind: instance method of Chord
Returns: Chord - the chord symbol
Param | Type | Default | Description |
---|---|---|---|
referenceKey | Key | string | null | the reference key. The key is required when converting a numeric or numeral. |
chord.toChordSymbolString(referenceKey) ⇒ string
Kind: instance method of Chord
Returns: string - the chord symbol string
See: {toChordSymbol}
Param | Type | Default | Description |
---|---|---|---|
referenceKey | Key | string | null | the reference key. The key is required when converting a numeric or numeral. |
chord.isChordSymbol() ⇒ boolean
Kind: instance method of Chord
chord.toChordSolfege(referenceKey) ⇒ Chord
Kind: instance method of Chord
Returns: Chord - the chord solfege
Param | Type | Default | Description |
---|---|---|---|
referenceKey | Key | string | null | the reference key. The key is required when converting a numeric or numeral. |
chord.toChordSolfegeString(referenceKey) ⇒ string
Kind: instance method of Chord
Returns: string - the chord solfege string
See: {toChordSolfege}
Param | Type | Default | Description |
---|---|---|---|
referenceKey | Key | string | null | the reference key. The key is required when converting a numeric or numeral. |
chord.isChordSolfege() ⇒ boolean
Kind: instance method of Chord
chord.toNumeric(referenceKey) ⇒ Chord
Kind: instance method of Chord
Returns: Chord - the numeric chord
Param | Type | Default | Description |
---|---|---|---|
referenceKey | Key | string | null | the reference key. The key is required when converting a chord symbol |
chord.toNumeral(referenceKey) ⇒ Chord
Kind: instance method of Chord
Returns: Chord - the numeral chord
Param | Type | Default | Description |
---|---|---|---|
referenceKey | Key | string | null | the reference key. The key is required when converting a chord symbol |
chord.toNumeralString(referenceKey) ⇒ string
Kind: instance method of Chord
Returns: string - the numeral chord string
See: {toNumeral}
Param | Type | Default | Description |
---|---|---|---|
referenceKey | Key | string | null | the reference key. The key is required when converting a chord symbol |
chord.isNumeric() ⇒ boolean
Kind: instance method of Chord
chord.toNumericString(referenceKey) ⇒ string
Kind: instance method of Chord
Returns: string - the numeric chord string
See: {toNumeric}
Param | Type | Default | Description |
---|---|---|---|
referenceKey | Key | string | null |
10 days ago
23 days ago
25 days ago
2 months ago
2 months ago
5 months ago
5 months ago
9 months ago
8 months ago
9 months ago
10 months ago
6 months ago
8 months ago
6 months ago
6 months ago
9 months ago
11 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 years ago