JSFiddle - React, Tailwind, and code Playground

by Ryan Duffy

HTML

<!--
verse 1
   D    Bm  Em7     A9      Em7   D        Bm7  Em7
At last          my love has come along,

  A7+    A7        D       Bm7      Em7
My lonely days are over

    Gdim    A7      D     Bm    Gdim    A9
And life is like a song.

verse 2

A7  D   Bm  Em7       A9    Em7       D     Bm7     Em7
At last          the skies above are blue;

   A7+      A7         D        Bm7      Em7
My heart was wrapped in clover

    Gdim    A7        D       Gm     Gdim       D
The night I looked at you.


Bridge

  A7    D7 G7+      Gdim   DM7              D6
I found a dream that I can speak to,

    C+      Bm6    Fdim    F#m
A dream that I can call my own,

  Bm6      Dm6       E7       AM7     A6
I found a thrill to press my cheek to,

D  Bm7       Cdim     A       Em7     A7
A thrill I've never known.

verse 3

A7  D     Bm  Em7     A9        Em7       D    Bm7  Em7
You smiled          and then the spell was cast,

    A7+    A7     D          Bm7      Em7
And here we are in heaven,

            Gdim A7  D      Bm     Em    Gdim    (A7)
For you are mine at last.

                        Gdim A7 D6     Gm    Gdim     D6
(last time) For you are mine at last.
-->

CSS

textarea {
    height:200px;
    width:300px;
}

JavaScript

enyo.kind({
    name:"com.tiqtech.accord.Parser",
    kind:"Component",
    parseDelay:null,
    re:{
        chord:/([A-G][#b]?(add|aug|dim|m|maj|min|sus)?\d{0,2}\+?(\/[A-G][#b]?)?)/ig,
        chords:/^\s*([A-G][#b]?(add|aug|dim|m|maj|min|sus)?\d{0,2}\+?(\/[A-G][#b]?)?\s*)+(\(.*\))?$/ig,
        section:/^(chorus|intro|introduction|bridge|pre\-?chorus|verse(\s+\d)?|break|instrumental)\s*(\(.*\))?$/ig
    },
    published:{
        value:""
    },
    events:{
        onParseComplete:""
    },
    valueChanged:function() {
        if(this.parseDelay) clearTimeout(this.parseDelay);
        this.parseDelay = setTimeout(enyo.bind(this, "parse"), 400);
    },
    parse:function() {
        this.parseDelay = undefined;
        var lines = this.value.split("\n");
        this.nextLine(lines);
    },
    nextLine:function(remaining, parsed) {
        if(this.parseDelay) return;    // kick out if user has typed
        
        parsed = parsed || [];
        
        if(remaining.length === 0) {
            this.parseComplete(parsed);
        } else {
            enyo.asyncMethod(this, "processLine", remaining, parsed);
        }
    },
    parseComplete:function(parsed) {
        if(!parsed || parsed.length === 0) return;
        
        var song = [],
            sections = [],
            buffer = null;
       
        for(var i in parsed) {
            var p = parsed[i];
            if(!(p.type && p.value)) continue;
            
            switch(p.type) {
                case "section":
                    song.push({name:p.value, content:[]});
                    break;
                case "chord":
                    if(buffer) {
                        song[song.length-1].content.push(this.multiplex("", buffer));
                    }
                    buffer = p.value;
                    break;
                case "text":
                    song[song.length-1].content.push(this.multiplex(p.value, buffer));
                    buffer = null;
           ...