xref: /plugin/chordsheets/js/jtab.js (revision dc3f041fdc446c5a8dbc11835bd33c9ae76656e5)
1/**
2 * JTab - Javascript/CSS Guitar Chord and Tab Notation for the Web.
3 * Version 1.3.1
4 * Written by Paul Gallagher (http://tardate.com), 2009. (original version and maintainer)
5 * Contributions:
6 *   Jason Ong (https://github.com/jasonong)
7 *   Bruno Bornsztein (https://github.com/bborn)
8 *   Binary Bit LAN (https://github.com/binarybitlan)
9 * See:
10 *   http://jtab.tardate.com : more information on availability, configuration and use.
11 *   http://github.com/tardate/jtab/tree/master : source code repository, wiki, documentation
12 *
13 * This library also depends on the following two libraries that must be loaded for it to work:
14 *   jQuery - http://www.jquery.com/
15 *   Raphael - http://raphaeljs.com/
16 *
17 *
18 * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
19 * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
20 * any later version.
21 *
22 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
23 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
24 * details.
25 *
26 * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
27 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 */
29
30//
31// define the jtab class
32//
33
34var jtab = {
35  Version: '1.3.1',
36  element_count: 0, //TODO:
37  Strings: {
38    AboutDialog: '<html><head><title>About jTab</title></head><body style=""><p style="">jTab version: {V}</p><p><a href="http://jtab.tardate.com" target="_blank">http://jtab.tardate.com</a></p><p><input type="button" class="close" value="OK" onClick="window.close()"/></p></body></html>'
39  },
40  Chords: {
41    // chord data - currently explicit representation for 6 string guitar, standard tuning only, and
42    // each chord is an array of alternate positions
43    //   0   : 1st (open) position
44    //   1   : 1st barre position, generally at 12/13/14th fret
45    //         - minimum, only required for CAGED chords where open strings are used in the 1st (open) position
46    //           since the main purpose of this is to provide barre fingering positions for CAGED-based chords
47    //   2.. : alternative positions/fingerings
48    // each position is an array comprising: 1. base fret (0==nut); 2. 6x note definitions (strings 6,5,4,3,2,1)
49    // each note is an array: (fret position), (left hand fingering if applicable 1,2,3,4,T)
50    // fret position: -1 = muted/not played; 0 = open; 1,2,3... = fret position
51    C: [[0, [-1], [3, 3], [2, 2], [0], [1, 1], [0]], [12, [-1, -1], [15, 4], [14, 3], [12, 1], [13, 2], [12, 1]]],
52    Cm: [[0, [-1], [3, 4], [1, 2], [0], [1, 1], [-1]], [12, [-1, -1], [15, 4], [13, 3], [12, 1], [13, 2], [-1, -1]]],
53    C6: [[0, [-1], [0], [2, 2], [2, 3], [1, 1], [3, 4]], [12, [-1, -1], [12, 1], [14, 3], [14, 3], [13, 2], [15, 4]]],
54    Cm6: [[0, [-1], [-1], [1, 1], [2, 3], [1, 2], [3, 4]], []],
55    C69: [[0, [-1], [3, 2], [2, 1], [2, 1], [3, 3], [3, 4]], []],
56    C7: [[0, [-1], [3, 3], [2, 2], [3, 4], [1, 1], [0]], [12, [-1, -1], [15, 3], [14, 2], [15, 4], [13, 1], [12,]]],
57    Cm7: [[0, [-1], [-1], [1, 1], [3, 3], [1, 1], [3, 4]], []],
58    Cmaj7: [[0, [-1], [3, 3], [2, 2], [0], [0], [0]], [12, [-1, -1], [15, 4], [14, 3], [12, 1], [12, 1], [12, 1]]],
59    C7b5: [[2, [-1], [3, 1], [4, 3], [3, 2], [5, 4], [-1]], []],
60    "C7#5": [[0, [-1], [-1], [2, 2], [3, 3], [1, 1], [4, 4]], []],
61    "Cm7b5": [[0, [-1], [3, 1], [4, 3], [3, 2], [4, 4], [-1]], []],
62    C7b9: [[0, [-1], [3, 3], [2, 1], [3, 4], [2, 2], [0]], []],
63    C9: [[0, [-1], [3, 2], [2, 1], [3, 3], [3, 4], [-1]], []],
64    Cm9: [[0, [-1], [3, 2], [1, 1], [3, 3], [3, 3], [3, 3]], []],
65    Cmaj9: [[0, [-1], [3, 3], [0], [0], [0], [0]], [12, [-1], [15, 3], [12, 1], [12, 1], [12, 1], [12, 1]]],
66    Cadd9: [[0, [-1], [3, 2], [2, 1], [0], [3, 3], [0]], [12, [-1], [15, 3], [14, 2], [12, 1], [15, 4], [12, 1]]],
67    C13: [[2, [-1], [3, 1], [5, 2], [3, 1], [5, 3], [5, 4]], []],
68    Csus2: [[2, [-1], [3, 1], [5, 3], [5, 4], [3, 1], [3, 1]], []],
69    Csus4: [[2, [-1], [3, 1], [5, 2], [5, 3], [6, 4], [3, 1]], []],
70    Cdim: [[0, [-1], [3, 3], [4, 4], [2, 2], [1, 1], [-1]], []],
71    Cdim7: [[0, [-1], [-1], [1, 1], [2, 3], [1, 2], [2, 4]], []],
72    Caug: [[0, [-1], [-1], [2, 2], [1, 1], [1, 1], [4, 4]], []],
73
74    "C#": [[0, [-1], [4, 4], [3, 4], [1, 1], [2, 2], [1, 1]], []],
75    "C#m": [[0, [-1], [-1], [2, 2], [1, 1], [2, 3], [0]], []],
76    "C#6": [[0, [-1], [-1], [3, 2], [3, 3], [2, 1], [4, 4]], []],
77    "C#m6": [[0, [-1], [4, 3], [2, 1], [3, 2], [2, 1], [4, 4]], []],
78    "C#69": [[2, [-1], [4, 2], [3, 1], [3, 1], [4, 3], [4, 4]], []],
79    "C#7": [[0, [-1], [-1], [3, 2], [4, 3], [2, 1], [4, 4]], []],
80    "C#m7": [[0, [-1], [-1], [2, 1], [4, 3], [2, 1], [4, 4]], []],
81    "C#maj7": [[0, [-1], [4, 4], [3, 3], [1, 1], [1, 1], [1, 1]], []],
82    "C#7b5": [[0, [3, 2], [0], [3, 3], [4, 4], [2, 1], [-1]], []],
83    "C#7#5": [[0, [-1], [4, 3], [3, 2], [2, 1], [0], [-1]], []],
84    "C#m7b5": [[0, [-1], [2, 1], [2, 2], [0], [2, 3], [0]], []],
85    "C#7b9": [[0, [-1], [4, 2], [3, 1], [4, 3], [3, 1], [4, 4]], []],
86    "C#9": [[3, [-1], [4, 1], [6, 2], [6, 3], [4, 1], [4, 1]], []],
87    "C#m9": [[0, [0], [2, 2], [1, 1], [1, 1], [2, 3], [0]], []],
88    "C#maj9": [[0, [-1], [4, 4], [1, 1], [1, 1], [1, 1], [1, 1]], []],
89    "C#add9": [[0, [1, 1], [-1], [1, 1], [1, 1], [2, 2], [1, 1]], []],
90    "C#13": [[3, [-1], [4, 1], [6, 2], [4, 1], [6, 3], [6, 4]], []],
91    "C#sus2": [[0, [-1], [-1], [3, 3], [3, 4], [1, 1], [1, 1]], []],
92    "C#sus4": [[0, [-1], [-1], [3, 2], [3, 3], [4, 4], [1, 1]], []],
93    "C#dim": [[0, [-1], [-1], [2, 1], [3, 3], [2, 2], [3, 4]], []],
94    "C#dim7": [[0, [-1], [-1], [2, 1], [3, 3], [2, 2], [3, 4]], []],
95    "C#aug": [[0, [-1], [4, 4], [3, 3], [2, 1], [2, 2], [-1]], []],
96
97
98    D: [[0, [-1], [0], [0], [2, 1], [3, 3], [2, 2]], [12, [-1, -1], [12, 1], [12, 1], [14, 3], [15, 4], [14, 2]]],
99    Dm: [[0, [-1], [0], [0], [2, 2], [3, 3], [1, 1]], [12, [-1, -1], [12, 1], [12, 1], [14, 3], [15, 4], [13, 2]]],
100    D6: [[0, [-1], [0], [0], [2, 2], [0], [2, 3]], [12, [-1, -1], [12, 1], [12, 1], [14, 3], [12, 1], [14, 4]]],
101    Dm6: [[0, [-1], [2, 2], [0], [2, 3], [0], [1, 1]], [12, [-1, -1], [14, 3], [12, 1], [14, 4], [12, 1], [1, 2]]],
102    D69: [[3, [-1], [5, 2], [4, 1], [4, 1], [5, 3], [5, 4]], []],
103    D7: [[0, [-1], [0], [0], [2, 2], [1, 1], [2, 3]], [12, [-1, -1], [12, 1], [12, 1], [14, 3], [13, 2], [14, 4]]],
104    Dm7: [[0, [-1], [-1], [0], [2, 2], [1, 1], [1, 1]], [12, [-1, -1], [-1, -1], [12, 1], [14, 4], [13, 2], [13, 3]]],
105    Dmaj7: [[0, [-1], [5, 4], [4, 3], [2, 1], [2, 1], [2, 1]], []],
106    D7b5: [[4, [-1], [5, 1], [6, 3], [5, 2], [7, 4], [-1]], []],
107    "D7#5": [[0, [-1], [-1], [0], [4, 3], [1, 1], [2, 2]], [12, [-1, -1], [-1, -1], [12, 1], [16, 4], [13, 2], [14, 3]]],
108    Dm7b5: [[0, [-1], [-1], [0], [1, 1], [1, 1], [1, 1]], [12, [-1, -1], [-1, -1], [12, 1], [12, 2], [12, 3], [12, 4]]],
109    D7b9: [[3, [-1], [5, 2], [4, 1], [5, 3], [4, 1], [5, 4]], []],
110    D9: [[0, [2, 2], [-1], [0], [2, 3], [1, 1], [0]], [12, [14, 4], [-1, -1], [12, 1], [14, 3], [13, 2], [12, 1]]],
111    Dm9: [[0, [-1], [-1], [3, 3], [2, 2], [1, 1], [0]], [12, [-1, -1], [-1, -1], [15, 4], [14, 3], [13, 2], [12, 1]]],
112    Dmaj9: [[0, [-1], [5, 4], [2, 1], [2, 1], [2, 1], [2, 1]], []],
113    Dadd9: [[0, [-1], [-1], [0], [2, 1], [3, 2], [0]], [12, [-1, -1], [-1, -1], [12, 1], [14, 3], [15, 4], [12, 1]]],
114    D13: [[4, [-1], [5, 1], [7, 2], [5, 1], [7, 3], [7, 4]], []],
115    Dsus2: [[0, [-1], [-1], [0], [2, 1], [3, 3], [0]], [12, [-1, -1], [-1, -1], [12, 1], [14, 3], [15, 4], [12, 1]]],
116    Dsus4: [[0, [-1], [-1], [0], [2, 1], [3, 3], [3, 4]], [12, [-1, -1], [-1, -1], [12, 1], [14, 2], [15, 3], [15, 4]]],
117    Ddim: [[0, [-1], [-1], [0], [1, 1], [0], [1, 2]], [12, [-1, -1], [-1, -1], [12, 1], [13, 2], [12, 1], [13, 3]]],
118    Ddim7: [[0, [-1], [2, 3], [0], [1, 1], [0], [1, 2]], [12, [-1, -1], [14, 4], [12, 1], [13, 2], [12, 1], [13, 3]]],
119    Daug: [[0, [-1], [-1], [0], [3, 2], [3, 3], [2, 1]], [12, [-1, -1], [-1, -1], [12, 1], [15, 3], [15, 4], [14, 2]]],
120
121    "Eb": [[0, [-1], [-1], [5, 4], [3, 1], [4, 3], [3, 2]], []],
122    "Ebm": [[0, [-1], [-1], [4, 3], [3, 2], [4, 4], [2, 1]], []],
123    "Eb6": [[0, [-1], [3, 1], [5, 3], [3, 1], [4, 2], [3, 1]], []],
124    "Ebm6": [[0, [-1], [-1], [1, 1], [3, 3], [1, 1], [2, 2]], []],
125    "Eb69": [[0, [-1], [-1], [1, 1], [0], [1, 2], [1, 3]], []],
126    "Eb7": [[0, [-1], [-1], [1, 1], [3, 3], [2, 2], [3, 4]], []],
127    "Ebm7": [[0, [-1], [-1], [1, 1], [3, 4], [2, 2], [2, 3]], []],
128    "Ebmaj7": [[0, [-1], [-1], [1, 1], [3, 2], [3, 3], [3, 4]], []],
129    "Eb7b5": [[0, [-1], [-1], [1, 1], [2, 2], [2, 3], [4, 4]], []],
130    "Eb7#5": [[0, [-1], [-1], [1, 1], [4, 4], [2, 2], [3, 3]], []],
131    "Ebm7b5": [[0, [-1], [-1], [1, 1], [2, 2], [2, 3], [2, 4]], []],
132    "Eb7b9": [[0, [-1], [-1], [1, 1], [0], [2, 3], [0]], []],
133    "Eb9": [[0, [3, 4], [1, 1], [1, 1], [0], [2, 1], [1, 1]], []],
134    "Ebm9": [[0, [2, 2], [1, 1], [1, 1], [3, 4], [2, 3], [1, 1]], []],
135    "Ebmaj9": [[0, [-1], [-1], [1, 1], [0], [3, 4], [1, 2]], []],
136    "Ebadd9": [[0, [3, 4], [2, 2], [2, 2], [0], [-1], [1, 1]], []],
137    "Eb13": [[4, [-1], [6, 2], [5, 1], [6, 3], [8, 4], [8, 4]], []],
138    "Ebsus2": [[0, [1, 1], [1, 1], [1, 1], [3, 3], [-1], [1, 1]], []],
139    "Ebsus4": [[0, [-1], [-1], [1, 1], [3, 3], [4, 4], [4, 4]], []],
140    "Ebdim": [[0, [-1], [-1], [1, 1], [2, 3], [1, 2], [2, 4]], []],
141    "Ebdim7": [[0, [-1], [-1], [1, 1], [2, 3], [1, 2], [2, 4]], []],
142    "Ebaug": [[0, [3, 3], [2, 2], [1, 1], [0], [0], [3, 4]], []],
143
144    E: [[0, [0], [2, 2], [2, 3], [1, 1], [0], [0]], [12, [12, 1], [14, 3], [14, 4], [13, 2], [12, 1], [12, 1]]],
145    Em: [[0, [0], [2, 2], [2, 3], [0], [0], [0]], [12, [12, 1], [14, 3], [14, 4], [12, 1], [12, 1], [12, 1]]],
146    E6: [[0, [0], [2, 2], [2, 3], [1, 1], [2, 4], [0]], [12, [12, 1], [14, 3], [14, 3], [13, 2], [14, 4], [12, 1]]],
147    Em6: [[0, [0], [2, 1], [2, 2], [0], [2, 3], [0]], [12, [12, 1], [14, 2], [14, 3], [12, 1], [14, 4], [12, 1]]],
148    E69: [[0, [-1], [2, 2], [2, 2], [1, 1], [2, 3], [2, 3]], []],
149    E7: [[0, [0], [2, 2], [0], [1, 1], [0], [0]], [12, [12, 1], [14, 3], [12, 1], [13, 2], [12, 1], [12, 1]]],
150    Em7: [[0, [0], [2, 2], [2, 3], [0], [3, 4], [0]], [12, [12, 1], [14, 2], [14, 3], [12, 1], [15, 4], [12, 1]]],
151    Emaj7: [[0, [0], [2, 3], [1, 1], [1, 2], [0], [0]], [12, [12, 1], [14, 4], [13, 2], [13, 3], [12, 1], [12, 1]]],
152    E7b5: [[0, [-1], [1, 1], [0], [1, 2], [3, 4], [0]], [12, [-1, -1], [13, 2], [12, 1], [13, 3], [15, 4], [12, 1]]],
153    "E7#5": [[0, [0], [3, 4], [0], [1, 1], [1, 2], [-1]], [12, [12, 1], [15, 4], [12, 1], [13, 3], [13, 2], [-1]]],
154    Em7b5: [[0, [-1], [-1], [2, 1], [3, 2], [3, 3], [3, 4]], []],
155    E7b9: [[0, [0], [2, 3], [0], [1, 1], [0], [1, 2]], [12, [12, 1], [14, 4], [12, 1], [13, 3], [12, 1], [13, 2]]],
156    E9: [[0, [0], [2, 2], [0], [1, 1], [0], [2, 3]], [12, [12, 1], [14, 3], [12, 1], [13, 2], [12, 1], [14, 4]]],
157    Em9: [[0, [0], [2, 1], [0], [0], [0], [2, 2]], [12, [12, 1], [14, 2], [12, 1], [12, 1], [12, 1], [14, 4]]],
158    Emaj9: [[0, [0], [2, 2], [1, 1], [1, 1], [0], [2, 4]], [12, [12, 1], [14, 3], [13, 2], [13, 2], [12, 1], [14, 4]]],
159    Eadd9: [[0, [2, 2], [2, 3], [2, 4], [1, 1], [0], [0]], [12, [14, 3], [14, 3], [14, 4], [13, 2], [12, 1], [12, 1]]],
160    E13: [[0, [0], [2, 2], [0], [1, 1], [2, 3], [0]], [12, [12, 1], [14, 3], [12, 1], [13, 2], [14, 4], [12, 1]]],
161    Esus2: [[0, [-1], [2, 1], [4, 3], [4, 4], [-1], [0]], [12, [-1, -1], [14, 2], [16, 3], [16, 4], [-1], [12, 1]]],
162    Esus4: [[0, [0], [2, 2], [2, 3], [2, 4], [0], [0]], [12, [12, 1], [14, 2], [14, 3], [14, 4], [12, 1], [12, 1]]],
163    Edim: [[0, [-1], [1, 1], [2, 2], [3, 4], [2, 3], [-1]], []],
164    Edim7: [[0, [-1], [-1], [2, 1], [3, 3], [2, 2], [3, 4]], []],
165    Eaug: [[0, [0], [3, 4], [2, 3], [1, 1], [1, 2], [0]], [12, [12, 1], [15, 4], [14, 3], [13, 2], [13, 2], [12, 1]]],
166
167    F: [[0, [1, 1], [3, 3], [3, 4], [2, 2], [1, 1], [1, 1]], []],
168    Fm: [[0, [1, 1], [3, 3], [3, 4], [1, 1], [1, 1], [1, 1]], []],
169    F6: [[0, [-1], [3, 2], [3, 3], [2, 1], [3, 4], [-1]], []],
170    Fm6: [[0, [-1], [-1], [0], [1, 1], [1, 1], [1, 1]], []],
171    F69: [[0, [1, 1], [0], [0], [0], [1, 2], [1, 3]], []],
172    F7: [[0, [1, 1], [3, 3], [1, 1], [2, 2], [1, 1], [1, 1]], []],
173    Fm7: [[0, [1, 1], [3, 3], [3, 4], [1, 1], [4, 4], [1, 1]], []],
174    Fmaj7: [[0, [1, 1], [-1], [2, 3], [2, 4], [1, 2], [-1]], []],
175    F7b5: [[0, [1, 1], [-1], [1, 2], [2, 3], [0], [-1]], []],
176    "F7#5": [[0, [1, 1], [-1], [1, 2], [2, 3], [2, 4], [-1]], []],
177    Fm7b5: [[2, [-1], [-1], [3, 1], [4, 2], [4, 3], [4, 4]], []],
178    F7b9: [[0, [-1], [-1], [3, 2], [2, 1], [4, 4], [2, 1]], []],
179    F9: [[0, [3, 3], [0], [3, 4], [2, 2], [1, 1], [1, 1]], []],
180    Fm9: [[0, [-1], [-1], [1, 1], [1, 1], [1, 1], [3, 4]], []],
181    Fmaj9: [[0, [0], [0], [3, 3], [0], [1, 1], [3, 4]], []],
182    Fadd9: [[0, [-1], [-1], [3, 3], [2, 2], [1, 1], [3, 4]], []],
183    F13: [[0, [1, 1], [0], [1, 2], [3, 4], [3, 4], [3, 4]], []],
184    Fsus2: [[0, [-1], [3, 3], [3, 4], [0], [1, 1], [1, 1]], []],
185    Fsus4: [[0, [1, 1], [3, 2], [3, 3], [3, 4], [1, 1], [1, 1]], []],
186    Fdim: [[0, [-1], [-1], [0], [1, 1], [0], [1, 2]], []],
187    Fdim7: [[0, [-1], [2, 3], [0], [1, 1], [0], [1, 2]], []],
188    Faug: [[0, [1, 1], [-1], [3, 4], [2, 2], [2, 3], [1, 1]], []],
189
190    "F#": [[0, [2, 1], [4, 3], [4, 4], [3, 2], [2, 1], [2, 1]], []],
191    "F#m": [[0, [2, 1], [4, 3], [4, 4], [2, 1], [2, 1], [2, 1]], []],
192    "F#6": [[0, [2, 1], [4, 3], [-1], [3, 2], [4, 4], [-1]], []],
193    "F#m6": [[0, [-1], [-1], [1, 1], [2, 2], [2, 3], [2, 4]], []],
194    "F#69": [[0, [2, 2], [1, 1], [1, 1], [1, 1], [2, 3], [2, 4]], []],
195    "F#7": [[0, [2, 1], [4, 3], [2, 1], [3, 2], [2, 1], [2, 1]], []],
196    "F#m7": [[0, [2, 1], [4, 3], [4, 4], [2, 1], [5, 4], [2, 1]], []],
197    "F#maj7": [[0, [2, 1], [-1], [3, 3], [3, 4], [2, 2], [-1]], []],
198    "F#7b5": [[0, [2, 2], [-1], [2, 3], [3, 4], [1, 1], [-1]], []],
199    "F#7#5": [[0, [2, 2], [-1], [2, 3], [3, 4], [3, 4], [-1]], []],
200    "F#m7b5": [[0, [-1], [-1], [2, 2], [2, 3], [1, 1], [2, 4]], []],
201    "F#7b9": [[0, [2, 2], [1, 1], [2, 3], [0], [2, 4], [0]], []],
202    "F#9": [[0, [2, 1], [4, 3], [2, 1], [3, 2], [2, 1], [4, 4]], []],
203    "F#m9": [[0, [3, 3], [0], [3, 4], [1, 1], [2, 3], [2, 3]], []],
204    "F#maj9": [[0, [2, 1], [-1], [3, 2], [3, 3], [-1], [4, 4]], []],
205    "F#add9": [[0, [2, 2], [1, 1], [-1], [1, 1], [2, 3], [2, 4]], []],
206    "F#13": [[0, [2, 1], [4, 3], [2, 1], [3, 2], [4, 4], [2, 1]], []],
207    "F#sus2": [[0, [2, 2], [-1], [-1], [1, 1], [2, 3], [2, 4]], []],
208    "F#sus4": [[0, [2, 1], [4, 2], [4, 3], [4, 4], [2, 1], [2, 1]], []],
209    "F#dim": [[0, [-1], [-1], [1, 1], [2, 3], [1, 2], [2, 4]], []],
210    "F#dim7": [[0, [-1], [-1], [1, 1], [2, 3], [1, 2], [2, 4]], []],
211    "F#aug": [[0, [2, 1], [-1], [4, 4], [3, 2], [3, 3], [2, 1]], []],
212
213    G: [[0, [3, 3], [2, 2], [0], [0], [0], [3, 4]], [12, [15, 3], [14, 2], [12, 1], [12, 1], [12, 1], [15, 4]]],
214    Gm: [[0, [3, 2], [1, 1], [0], [0], [3, 3], [3, 4]], [12, [15, 3], [13, 2], [12, 1], [12, 1], [15, 4], [15, 4]]],
215    G6: [[0, [3, 3], [2, 2], [0], [0], [0], [0]], [12, [15, 4], [14, 3], [12, 1], [12, 1], [12, 1], [12, 1]]],
216    Gm6: [[0, [-1], [-1], [2, 1], [3, 3], [3, 3], [3, 3]], []],
217    G69: [[0, [3, 3], [2, 1], [0], [2, 2], [0], [0]], [12, [15, 4], [14, 3], [12, 1], [14, 2], [12, 1], [12, 1]]],
218    G7: [[0, [3, 3], [2, 2], [0], [0], [0], [1, 1]], [12, [15, 4], [14, 3], [12, 1], [12, 1], [12, 1], [13, 2]]],
219    Gm7: [[0, [-1], [1, 1], [3, 3], [0], [3, 4], [-1]], [12, [-1], [13, 2], [15, 3], [12, 1], [15, 4], [-1]]],
220    Gmaj7: [[0, [3, 3], [2, 2], [0], [0], [0], [2, 1]], [12, [15, 3], [14, 2], [12, 1], [12, 1], [12, 1], [14, 4]]],
221    "G7b5": [[0, [3, 2], [-1], [3, 3], [4, 4], [2, 1], [-1]], [], [4, [-1], [-1], [5, 1], [6, 2], [6, 3], [7, 4]]],
222    "G7#5": [[0, [3, 1], [-1], [3, 2], [4, 3], [4, 4], [-1]], []],
223    Gm7b5: [[2, [3, 1], [4, 2], [3, 1], [3, 1], [6, 4], [3, 1]], []],
224    G7b9: [[0, [3, 4], [2, 3], [0], [1, 1], [0], [1, 2]], [12, [15, 4], [14, 3], [12, 1], [13, 2], [12, 1], [13,]]],
225    G9: [[0, [3, 3], [-1], [0], [2, 2], [0], [1, 1]], [12, [15, 4], [-1], [12, 1], [14, 3], [12, 1], [13, 2]]],
226    Gm9: [[2, [3, 1], [5, 3], [3, 1], [3, 1], [3, 1], [5, 4]], []],
227    Gmaj9: [[0, [3, 3], [-1], [0], [2, 1], [0], [2, 2]], [12, [15, 4], [-1], [12, 1], [14, 3], [12, 1], [14, 2]]],
228    Gadd9: [[0, [3, 2], [0], [0], [0], [0], [3, 3]], [12, [15, 3], [12, 1], [12, 1], [12, 1], [12, 1], [15, 4]]],
229    G13: [[0, [3, 2], [2, 1], [3, 3], [0], [0], [0]], [12, [15, 3], [14, 2], [15, 3], [12, 1], [12, 1], [12, 1]]],
230    Gsus2: [[0, [3, 1], [0], [0], [0], [3, 3], [3, 4]], [12, [15, 2], [12, 1], [12, 1], [12, 1], [15, 3], [15, 4]]],
231    Gsus4: [[2, [3, 1], [5, 2], [5, 3], [5, 4], [3, 1], [3, 1]], []],
232    Gdim: [[2, [-1], [-1], [5, 2], [6, 4], [5, 3], [3, 1]], []],
233    Gdim7: [[0, [-1], [-1], [2, 1], [3, 3], [2, 2], [3, 4]], []],
234    Gaug: [[0, [3, 1], [-1], [5, 4], [4, 2], [4, 3], [3, 1]], []],
235
236    "G#": [[0, [4, 4], [3, 3], [1, 1], [1, 1], [1, 1], [-1]], []],
237    "G#m": [[0, [-1], [2, 2], [1, 1], [1, 1], [4, 4], [4, 4]], []],
238    "G#6": [[0, [-1], [-1], [1, 1], [1, 1], [1, 1], [1, 1]], []],
239    "G#m6": [[0, [-1], [-1], [1, 1], [1, 2], [0], [1, 3]], []],
240    "G#69": [[0, [4, 2], [3, 1], [3, 1], [3, 1], [4, 3], [4, 4]], []],
241    "G#7": [[0, [-1], [-1], [1, 1], [1, 1], [1, 1], [2, 2]], []],
242    "G#m7": [[3, [4, 1], [6, 3], [4, 1], [4, 1], [7, 4], [4, 1]], []],
243    "G#maj7": [[0, [-1], [-1], [1, 1], [1, 1], [1, 1], [3, 3]], []],
244    "G#7b5": [[2, [4, 2], [-1], [4, 3], [5, 4], [3, 2], [-1]], []],
245    "G#7#5": [[3, [4, 1], [-1], [4, 2], [5, 3], [5, 4], [-1]], []],
246    "G#m7b5": [[0, [4, 1], [5, 2], [4, 1], [4, 1], [7, 4], [4, 1]], []],
247    "G#7b9": [[0, [4, 3], [3, 2], [4, 4], [2, 1], [-1], [-1]], []],
248    "G#9": [[0, [2, 2], [1, 1], [1, 1], [1, 1], [1, 1], [2, 4]], []],
249    "G#m9": [[0, [2, 2], [1, 1], [1, 1], [1, 1], [0], [2, 4]], []],
250    "G#maj9": [[2, [4, 2], [3, 1], [5, 4], [3, 1], [4, 3], [-1]], []],
251    "G#add9": [[0, [-1], [1, 1], [1, 1], [1, 1], [1, 1], [-1]], []],
252    "G#13": [[0, [4, 3], [3, 2], [4, 4], [1, 1], [1, 1], [1, 1]], []],
253    "G#sus2": [[0, [-1], [1, 1], [1, 2], [1, 3], [-1], [-1]], []],
254    "G#sus4": [[0, [-1], [-1], [1, 1], [1, 1], [2, 2], [4, 4]], []],
255    "G#dim": [[0, [-1], [-1], [0], [1, 1], [0], [1, 2]], []],
256    "G#dim7": [[0, [-1], [-1], [0], [1, 1], [0], [1, 2]], []],
257    "G#aug": [[0, [0, -1], [3, 4], [2, 3], [1, 1], [1, 2], [0, -1]], []],
258
259    A: [[0, [-1], [0], [2, 2], [2, 1], [2, 3], [0]], [12, [-1, -1], [12, 1], [14, 2], [14, 3], [14, 4], [12, 1]]],
260    Am: [[0, [-1], [0], [2, 2], [2, 3], [1, 1], [0]], [12, [-1, -1], [12, 1], [14, 3], [14, 4], [13, 2], [12, 1]]],
261    A6: [[0, [-1], [0], [2, 1], [2, 1], [2, 1], [2, 1]], [12, [-1, -1], [12, 1], [14, 3], [14, 3], [14, 3], [14, 3]]],
262    Am6: [[0, [-1], [0], [2, 2], [2, 3], [1, 1], [2, 4]], [12, [-1, -1], [12, 1], [14, 3], [14, 3], [13, 2], [14, 4]]],
263    A69: [[3, [5, 2], [4, 1], [4, 1], [4, 1], [5, 3], [5, 4]], []],
264    A7: [[0, [-1], [0], [2, 2], [0], [2, 3], [0]], [12, [-1, -1], [12, 1], [14, 2], [12, 1], [14, 3], [12, 1]]],
265    Am7: [[0, [-1], [0], [2, 2], [0], [1, 1], [0]], [12, [-1, -1], [12, 1], [14, 3], [12, 1], [13, 2], [12, 1]]],
266    Amaj7: [[0, [-1], [0], [2, 2], [1, 1], [2, 3], [0]], [12, [-1, -1], [12, 1], [14, 3], [13, 2], [14, 4], [12, 1]]],
267    A7b5: [[3, [5, 2], [-1], [5, 3], [6, 4], [4, 1], [-1]], []],
268    "A7#5": [[4, [5, 1], [-1], [5, 2], [6, 3], [6, 4], [-1]], []],
269    Am7b5: [[0, [-1], [-1], [1, 1], [2, 3], [1, 2], [3, 4]], []],
270    A7b9: [[0, [-1], [0], [2, 1], [3, 3], [2, 2], [3, 4]], [12, [-1, -1], [12, ''], [14, 1], [15, 3], [14, 2], [15, 4]]],
271    A9: [[0, [-1], [0], [2, 1], [4, 4], [2, 2], [3, 3]], [12, [-1], [12,], [14, 1], [16, 4], [14, 2], [15, 3]]],
272    Am9: [[0, [-1], [0], [1, 1], [1, 1], [1, 1], [3, 4]], [12, [-1], [12, 1], [13, 2], [13, 2], [13, 2], [15, 4]]],
273    Amaj9: [[0, [-1], [0], [2, 1], [4, 3], [2, 1], [4, 4]], [12, [-1], [12,], [14, 1], [16, 3], [14, 1], [16, 4]]],
274    Aadd9: [[0, [-1], [0], [2, 2], [2, 3], [0], [0]], [12, [-1], [12, 1], [14, 3], [14, 4], [12, 1], [12, 1]]],
275    A13: [[0, [-1], [0], [2, 1], [0], [2, 2], [3, 3]], [12, [-1], [12, 1], [14, 2], [12, 1], [14, 3], [15, 4]]],
276    Asus2: [[0, [-1], [0], [2, 1], [2, 2], [0], [0]], [12, [-1], [12, 1], [14, 3], [14, 4], [12, 1], [12, 1]]],
277    Asus4: [[0, [-1], [0], [2, 1], [2, 2], [3, 3], [0]], [12, [-1], [12, 1], [14, 2], [14, 3], [15, 4], [12, 1]]],
278    Adim: [[0, [-1], [0], [1, 1], [2, 3], [1, 2], [-1]], [12, [-1], [12, 1], [13, 2], [14, 4], [13, 3], [-1]]],
279    Adim7: [[0, [-1], [-1], [1, 1], [2, 3], [1, 2], [2, 4]], []],
280    Aaug: [[0, [-1], [0], [3, 4], [2, 2], [2, 3], [1, 1]], [12, [-1], [12,], [15, 4], [14, 2], [14, 3], [13, 1]]],
281
282    "Bb": [[0, [-1], [1, 1], [3, 3], [3, 3], [3, 3], [1, 1]], []],
283    "Bbm": [[0, [-1], [1, 1], [3, 3], [3, 4], [2, 2], [1, 1]], []],
284    "Bb6": [[0, [-1], [1, 1], [3, 3], [3, 3], [3, 3], [3, 3]], []],
285    "Bbm6": [[0, [-1], [1, 1], [3, 3], [0], [2, 2], [-1]], []],
286    "Bb69": [[0, [-1], [1, 1], [0], [0], [1, 2], [1, 3]], []],
287    "Bb7": [[0, [-1], [1, 1], [3, 3], [1, 1], [4, 4], [1, 1]], []],
288    "Bbm7": [[0, [-1], [1, 1], [3, 3], [1, 1], [2, 2], [1, 1]], []],
289    "Bbmaj7": [[0, [-1], [1, 1], [3, 3], [2, 2], [3, 4], [1, 1]], []],
290    "Bb7b5": [[4, [6, 2], [-1], [6, 3], [7, 4], [5, 1], [-1]], []],
291    "Bb7#5": [[5, [6, 1], [-1], [6, 2], [7, 3], [7, 4], [-1]], []],
292    "Bbm7b5": [[0, [-1], [1, 1], [-1], [1, 2], [2, 3], [-1]], []],
293    "Bb7b9": [[6, [-1], [-1], [8, 2], [7, 1], [9, 4], [7, 1]], []],
294    "Bb9": [[0, [1, 1], [1, 2], [0], [1, 3], [1, 3], [1, 3]], []],
295    "Bbm9": [[5, [-1], [-1], [-1], [6, 1], [6, 1], [8, 4]], []],
296    "Bbmaj9": [[0, [-1], [1, 1], [0], [2, 4], [1, 2], [1, 3]], []],
297    "Bbadd9": [[0, [1, 1], [1, 1], [0], [3, 4], [1, 1], [1, 1]], []],
298    "Bb13": [[0, [-1], [1, 1], [0], [1, 2], [3, 4], [3, 4]], []],
299    "Bbsus2": [[0, [-1], [1, 1], [3, 3], [3, 4], [1, 1], [1, 1]], []],
300    "Bbsus4": [[0, [-1], [1, 1], [3, 2], [3, 3], [4, 4], [1, 1]], []],
301    "Bbdim": [[0, [-1], [1, 1], [2, 2], [3, 4], [2, 3], [-1]], []],
302    "Bbdim7": [[0, [-1], [-1], [2, 1], [3, 3], [2, 2], [3, 4]], []],
303    "Bbaug": [[0, [-1], [1, 1], [4, 4], [3, 2], [3, 3], [2, 1]], []],
304
305    B: [[0, [-1], [2, 1], [4, 3], [4, 3], [4, 3], [2, 1]], []],
306    Bm: [[0, [-1], [2, 1], [4, 3], [4, 4], [3, 2], [2, 1]], []],
307    B6: [[0, [-1], [2, 1], [4, 3], [4, 3], [4, 3], [4, 3]], []],
308    Bm6: [[0, [-1], [-1], [4, 2], [4, 3], [3, 1], [4, 4]], []],
309    B69: [[0, [-1], [2, 2], [1, 1], [1, 1], [2, 3], [2, 4]], []],
310    B7: [[0, [-1], [2, 2], [1, 1], [2, 3], [0], [2, 4]], []],
311    Bm7: [[0, [-1], [2, 2], [0], [2, 3], [0], [2, 4]], []],
312    Bmaj7: [[0, [-1], [2, 1], [4, 3], [3, 2], [4, 4], [2, 1]], []],
313    B7b5: [[5, [7, 2], [-1], [7, 3], [8, 4], [6, 1], [-1]], []],
314    "B7#5": [[0, [-1], [2, 2], [1, 1], [2, 3], [0], [3, 4]], []],
315    "Bm7b5": [[0, [-1], [2, 2], [0], [2, 3], [0], [1, 1]], []],
316    B7b9: [[0, [-1], [2, 2], [1, 1], [2, 3], [1, 1], [2, 4]], []],
317    B9: [[6, [7, 1], [9, 3], [7, 1], [8, 2], [7, 1], [9, 4]], []],
318    Bm9: [[0, [-1], [2, 1], [0], [2, 2], [2, 3], [2, 4]], []],
319    Bmaj9: [[0, [-1], [2, 2], [1, 1], [3, 4], [2, 3], [2, 3]], []],
320    Badd9: [[0, [-1], [2, 1], [4, 3], [4, 4], [2, 1], [2, 1]], []],
321    B13: [[0, [-1], [2, 2], [1, 1], [2, 3], [0], [4, 4]], []],
322    Bsus2: [[0, [-1], [2, 1], [4, 3], [4, 4], [2, 1], [2, 1]], []],
323    Bsus4: [[0, [-1], [2, 1], [4, 2], [4, 3], [5, 4], [2, 1]], []],
324    Bdim: [[0, [-1], [2, 1], [3, 2], [4, 4], [3, 3], [-1]], []],
325    Bdim7: [[0, [-1], [-1], [0], [1, 1], [0], [1, 2]], []],
326    Baug: [[0, [-1], [2, 1], [5, 4], [4, 2], [4, 3], [3, 1]], []]
327  },
328  WesternScale: {
329    BaseNotes: { // for each: array[ translated western scale note, caged base, base fret ]
330      'C': ['C', 'C', 0],
331      'C#': ['C#', 'C', 1],
332      'Db': ['C#', 'C', 1],
333      'D': ['D', 'D', 0],
334      'D#': ['Eb', 'D', 1],
335      'Eb': ['Eb', 'D', 1],
336      'E': ['E', 'E', 0],
337      'F': ['F', 'E', 1],
338      'F#': ['F#', 'E', 2],
339      'Gb': ['F#', 'E', 2],
340      'G': ['G', 'G', 0],
341      'G#': ['G#', 'G', 1],
342      'Ab': ['G#', 'G', 1],
343      'A': ['A', 'A', 0],
344      'A#': ['Bb', 'A', 1],
345      'Bb': ['Bb', 'A', 1],
346      'B': ['B', 'A', 2]
347    },
348    BaseIntervals: ['C', 'C#', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'G#', 'A', 'Bb', 'B']
349  },
350  ChordList: function () {
351    var list = [];
352    for (var key in jtab.Chords) {
353      list.push(key);
354    }
355    return list;
356  },
357  /*
358   * Usage: jtab.AddChord("ChordName", Chord-Array)
359   * Example of Add: jtab.AddChord("Dsus4l", [ [ 0, [-1 ],  [-1 ],  [3,2],  [2,1],  [3,3],  [3,4] ], [ 12, [-1,-1],  [-1,-1],  [12,1],  [14,2],  [15,3],  [15,4] ] ]);
360   * Example of Update: jtab.AddChord("A", [ [ 0, [-1],  [0  ],  [2,3],  [2,2],  [2,1],  [0  ] ], [ 12, [-1,-1], [12,1], [14,2], [14,3], [14,4], [12,1] ] ]);
361   */
362  AddChord: function (chordName, chord) {
363    this.Chords[chordName] = chord;
364  }
365};
366
367//
368// define Array utility functions
369//
370
371Array.prototype.max_chars = function () {
372  var max = this[0].length;
373  var len = this.length;
374  for (var i = 1; i < len; i++) if (this[i].length > max) max = this[i].length;
375  return max;
376}
377
378
379//
380// define jtabChord class
381// public members:
382//  isValid        = whether valid chord defined
383//  isCaged        = whether chord is CAGED type
384//  isCustom       = whether chord is a custom fingering
385//  fullChordName  = full chord name, including position e.g. D#m7:3
386//  chordName      = chord name, without position e.g. D#m7
387//  baseName       = translated chord name (B <-> #), without position e.g. Ebm7
388//  rootNote       = root note e.g. D#
389//  rootExt        = root note extension e.g. m7
390//  cagedBaseShape = caged base shape e.g. D
391//  cagedBaseFret  = caged base fret e.g. 0
392//  cagedPos       = caged position e.g. 3
393//
394
395function jtabChord(token) {
396
397  this.scale = jtab.WesternScale;
398  this.baseNotes = this.scale.BaseNotes;
399  this.baseChords = jtab.Chords;
400  this.chordArray = null;
401  this.isValid = false;
402
403  this.fullChordName = token;
404  this.isCustom = (this.fullChordName.match(/\%/) != null)
405  this.isCaged = (this.fullChordName.match(/\:/) != null)
406
407  if (this.isCaged) {
408    var parts = this.fullChordName.split(':');
409    this.chordName = parts[0];
410    this.cagedPos = parts[1];
411  } else if (this.isCustom) {
412    var parts = this.fullChordName.match(/\[(.+?)\]/);
413    if (parts) {
414      this.chordName = parts[1];
415    } else {
416      this.chordName = '';
417    }
418  } else {
419    this.chordName = this.fullChordName;
420    this.cagedPos = 1;
421  }
422  this.rootExt = this.chordName.replace(/^[A-G#b]{1,2}/, '');
423  this.rootNote = this.chordName.substr(0, this.chordName.length - this.rootExt.length);
424  var baseNoteInfo = this.baseNotes[this.rootNote];
425  if (baseNoteInfo) {
426    this.baseName = baseNoteInfo[0] + this.rootExt;
427    this.cagedBaseShape = baseNoteInfo[1];
428    this.cagedBaseFret = baseNoteInfo[2];
429  } else {
430    this.cagedBaseShape = '';
431    this.cagedBaseFret = 0;
432  }
433
434  if ((this.isCaged) && (this.cagedPos > 1)) {
435    this.setCagedChordArray();
436  } else if (this.isCustom) {
437    this.setCustomChordArray();
438  } else {
439    this.setChordArray(this.baseName);
440  }
441}
442
443jtabChord.prototype.setCustomChordArray = function () {
444  this.chordArray = new Array();
445  this.chordArray = this.parseCustomChordArrayFromToken();
446};
447
448jtabChord.prototype.parseCustomChordArrayFromToken = function () {
449  notes = this.fullChordName.replace(/(\%|\[.+\])/g, '');
450  pairs = notes.split('.');
451  if (pairs.length < 6) {
452    this.isValid = false;
453    return;
454  }
455  this.isValid = true;
456
457  array = [];
458  for (var i = 0; i < pairs.length; i++) {
459    pair = pairs[i].split('/')
460    if (pair[0].match(/X/)) {
461      pair = [-1]
462    }
463    array.push(pair)
464  }
465
466  fingeredFrets = jQuery.grep(array, function (pair) {
467    return (pair.length != 1);
468  }).map(function (pair) {
469    return parseInt(pair[0]);
470  }).map(function (i) {
471    if ((i != 0) || (i != -1)) {
472      return i;
473    } else {
474      return null;
475    }
476  })
477
478  fingeredFrets = jQuery.grep(fingeredFrets, function (n) {
479    return (n);
480  });
481
482  min = Math.min.apply(Math, fingeredFrets);
483
484  array.unshift(min - 1);
485  return array;
486};
487
488jtabChord.prototype.setChordArray = function (chordName) { // clones chord array (position 0) from chord ref data into this object
489  this.chordArray = new Array();
490  if (this.baseChords[chordName] === undefined) {
491    this.isValid = false;
492    return;
493  }
494  this.isValid = true;
495  var modelRef = this.baseChords[chordName][0];
496  this.chordArray[0] = modelRef[0]
497  for (var i = 1; i < modelRef.length; i++) {
498    this.chordArray[i] = modelRef[i];    // TODO: this.chordArray[i] = modelRef[i].clone();
499  }
500};
501
502jtabChord.prototype.setCagedChordArray = function () {
503  if (!this.cagedBaseShape.match(/[CAGED]/)) return;
504  var caged_index = "CAGED".indexOf(this.cagedBaseShape) + 1;
505  var fret_widths = [3, 2, 3, 2, 2];
506  var starting_fret = this.cagedBaseFret;
507
508  for (var i = 1; i < this.cagedPos; i++) {
509    var index = (caged_index - 1) % 5;
510    caged_index = (caged_index >= 5) ? 1 : caged_index + 1;
511    starting_fret += fret_widths[index];
512  }
513
514  var modelChord = "CAGED".charAt(caged_index - 1) + this.rootExt;
515  this.setChordArray(modelChord);
516  this.shiftChordArray(starting_fret, modelChord);
517};
518
519jtabChord.prototype.shiftChordArray = function (atFret, modelChord) {
520  var initFret = this.chordArray[0];
521  if (atFret != initFret) {
522    var use_caged_fingering = ((this.isCaged) && (this.cagedPos > 0) && (!(this.baseChords[modelChord][1][0] === undefined)));
523
524    this.chordArray[0] = atFret - 1;
525    for (var i = 1; i < this.chordArray.length; i++) {
526      var fret = (this.chordArray[i][0] >= 0) ? this.chordArray[i][0] + atFret - initFret : this.chordArray[i][0];
527      var finger = (use_caged_fingering) ? this.baseChords[modelChord][1][i][1] : this.chordArray[i][1];
528      this.chordArray[i] = [fret, finger];
529    }
530  }
531};
532
533Raphael.fn.tabtype = 0;  // 0 = none, 1 = tab & chord, 2 = chord, 3 = tab
534Raphael.fn.has_chord = false;
535Raphael.fn.has_tab = false;
536
537Raphael.fn.debug = false;
538Raphael.fn.scale = 1;
539Raphael.fn.margin_top = 36;
540Raphael.fn.margin_bottom = 10;
541Raphael.fn.margin_left = 16;
542Raphael.fn.margin_right = 10;
543
544Raphael.fn.current_offset = Raphael.fn.margin_left;
545
546Raphael.fn.string_spacing = 16;
547Raphael.fn.strings_drawn = 6;
548Raphael.fn.fret_spacing = 16;
549Raphael.fn.frets_drawn = 4;
550Raphael.fn.note_radius = 7;
551
552Raphael.fn.fret_width = Raphael.fn.string_spacing * (Raphael.fn.strings_drawn - 1);
553Raphael.fn.fret_height = Raphael.fn.fret_spacing * (Raphael.fn.frets_drawn + 0.5);
554Raphael.fn.chord_width = Raphael.fn.margin_left + Raphael.fn.fret_width + Raphael.fn.string_spacing + Raphael.fn.margin_right;
555Raphael.fn.chord_height = Raphael.fn.margin_top + Raphael.fn.fret_height + Raphael.fn.margin_bottom;
556
557Raphael.fn.tab_current_string = 0; // 1,2,3,4,5,6 or 0 = not set
558Raphael.fn.tab_margin_top = 10;
559Raphael.fn.tab_top = Raphael.fn.chord_height + Raphael.fn.tab_margin_top;
560Raphael.fn.tab_spacing = Raphael.fn.fret_spacing;
561Raphael.fn.tab_height = Raphael.fn.tab_spacing * 5;
562Raphael.fn.tab_char_width = 8;
563
564Raphael.fn.total_height = Raphael.fn.tab_top + Raphael.fn.tab_height + Raphael.fn.margin_bottom;
565
566Raphael.fn.color = "#000";
567Raphael.fn.fingering_text_color = "#fff";
568Raphael.fn.tab_text_color = "#000";
569
570
571// debug helper - puts grid marks on the rendered image
572Raphael.fn.debug_grid = function (width) {
573  // h ticks
574  this.path(this.svg_params(this.current_offset, 0, 0, 4)).attr({ stroke: this.color, "stroke-width": 0.2 })
575  this.path(this.svg_params(this.current_offset + this.margin_left, 0, 0, 2)).attr({ stroke: this.color, "stroke-width": 0.2 })
576  this.path(this.svg_params(this.current_offset + width - this.margin_right, 0, 0, 2)).attr({ stroke: this.color, "stroke-width": 0.2 })
577  // v ticks
578  if (this.tabtype == 3) {
579    this.path(this.svg_params(this.current_offset, this.tab_margin_top, 2, 0)).attr({ stroke: this.color, "stroke-width": 0.2 })
580  } else {
581    this.path(this.svg_params(this.current_offset, this.margin_top, 2, 0)).attr({ stroke: this.color, "stroke-width": 0.2 })
582  }
583}
584
585
586// step the current position for drawing
587Raphael.fn.increment_offset = function (width) {
588  w = (width === undefined) ? this.chord_width : width;
589  if (this.debug) this.debug_grid(w);
590  this.current_offset += w;
591  this.setSize(this.current_offset, this.total_height);
592}
593
594Raphael.fn.svg_params = function (x, y, l1, l2) {
595  // http://www.w3.org/TR/SVG/paths.html#PathData --helpful reading
596  var move_line_to = "m" + x + " " + y + "l" + l1 + " " + l2
597  if (arguments.length == 4) return move_line_to
598}
599
600// draw the fretboard
601Raphael.fn.chord_fretboard = function (position, chord_name) {
602  var fret_left = this.current_offset + this.margin_left;
603  // conventional fret labels
604  var fret_labels = ['', '', '', 'III', '', 'V', '', 'VII', '', 'IX', '', '', 'XII', '', '', 'XV', '', 'XVII', '', 'XIX', '', 'XXI', ''];
605  // alternative friendly fret labels. Currently disabled, maybe bring these back as a configurable option?
606  // var fret_labels = [ '', '1fr', '2fr', '3fr', '4fr', '5fr', '6fr', '7fr', '8fr', '9fr', '10fr', '11fr', '12fr', '13fr', '14fr', '15fr', '16fr', '17fr', '18fr', '19fr', '20fr', '21fr', '' ];
607
608  this.text( // chord name
609    fret_left + 2.5 * this.string_spacing,
610    this.margin_top - 20,
611    chord_name).attr({ fill: this.tab_text_color, "font-size": "20px" });
612
613  var stroke_width = position == 0 ? 3 : 0  // nut
614  var chord_fretboard_path = this.path(this.svg_params(fret_left, this.margin_top, this.string_spacing * (this.strings_drawn - 1), 0))
615  chord_fretboard_path.attr({ stroke: this.color, "stroke-width": stroke_width })
616
617  for (var i = 0; i <= this.frets_drawn; i++) { // frets
618
619    this.path(this.svg_params(fret_left, this.margin_top + (i * this.fret_spacing), this.string_spacing * (this.strings_drawn - 1), 0))
620
621    pos = (fret_labels[position + i] === undefined) ? '' : fret_labels[position + i];
622
623    if (pos.length > 0) { // draw fret position
624      this.text(
625        fret_left + this.fret_width + this.string_spacing * 1.0,
626        this.margin_top + ((i - 0.5) * this.fret_spacing),
627        pos).attr({ stroke: this.tab_text_color, "font-size": "12px" });
628    }
629  }
630  for (var i = 0; i < this.strings_drawn; i++) {
631    this.path(this.svg_params(fret_left + (i * this.string_spacing), this.margin_top, 0, this.fret_spacing * (this.frets_drawn + 0.5)))  // strings
632  }
633  this.tab_extend(this.chord_width); // extend the tab if present
634}
635
636
637// draw a stroke (/)
638Raphael.fn.stroke = function () {
639
640  if (this.has_tab) {
641    var width = this.tab_char_width * 3;
642    // extend tab
643    this.tab_extend(width);
644    //  stroke
645    var stroke_path = this.path(this.svg_params(this.current_offset + this.tab_char_width, this.tab_top + (3.5 * this.tab_spacing), this.tab_char_width, - 2 * this.tab_spacing))
646    stroke_path.attr({ stroke: this.tab_text_color, "stroke-width": 4 })
647
648    this.increment_offset(width);
649  } else if (this.has_chord) {
650    var dx = this.string_spacing;
651    var dy = 2 * this.fret_spacing;
652    this.path(this.svg_params(this.current_offset + this.margin_left,
653      this.margin_top + this.fret_spacing + dy, dx, -dy)).attr({ stroke: this.tab_text_color, "stroke-width": 4 })
654
655    this.increment_offset(this.margin_left + dx + this.margin_right);
656  }
657}
658
659
660// draw a bar
661Raphael.fn.bar = function () {
662
663  if (this.has_tab) {
664    var width = this.tab_char_width * 2;
665    // extend tab
666    this.tab_extend(width);
667    var bar_stroke = this.path(this.svg_params(this.current_offset + this.tab_char_width, this.tab_top, 0, this.tab_height))
668    this.increment_offset(width);
669
670  } else if (this.has_chord) {
671    var fret_left = this.current_offset + this.margin_left;
672    var bar_stroke = this.path(this.svg_params(this.current_offset + this.margin_left, this.margin_top, 0, 0, this.fret_height))
673    this.increment_offset(this.margin_left + this.margin_right);
674  }
675  bar_stroke.attr({ stroke: this.color, "stroke-width": 1 })
676
677}
678
679
680// draw double bar
681Raphael.fn.doublebar = function () {
682  if (this.has_tab) {
683    var width = this.tab_char_width + 8;
684    // extend tab
685    this.tab_extend(width);
686    //  bar
687    var path_1 = this.path(this.svg_params(this.current_offset + this.tab_char_width, this.tab_top, 0, this.tab_height))
688    var path_2 = this.path(this.svg_params(this.current_offset + this.tab_char_width + 6, this.tab_top, 0, this.tab_height))
689    this.increment_offset(width);
690
691  } else if (this.has_chord) {
692    var left = this.current_offset + this.margin_left;
693
694    var path_1 = this.path(this.svg_params(left, this.margin_top, 0, this.fret_height))
695    var path_2 = this.path(this.svg_params(left + 6, this.margin_top, 0, this.fret_height))
696
697    this.increment_offset(this.margin_left + 6 + this.margin_right);
698  }
699  path_1.attr({ stroke: this.color, "stroke-width": 1 })
700  path_2.attr({ stroke: this.color, "stroke-width": 4 })
701}
702
703
704// draw a note in a chord
705Raphael.fn.chord_note = function (position, string_number, note) {
706  // NB: internal string_number in chords counts from low to high
707  var fret_number = note[0];
708  var fret_left = this.current_offset + this.margin_left;
709
710  if (fret_number < 0) {
711    // muted/not played
712    this.text(fret_left + (string_number - 1) * this.string_spacing, this.margin_top - 8, "x").attr({ stroke: this.tab_text_color, "font-size": "9px" });
713  } else if (fret_number == 0) {
714    // open
715    this.text(fret_left + (string_number - 1) * this.string_spacing, this.margin_top - 8, "o").attr({ stroke: this.tab_text_color, "font-size": "9px" });
716  } else {
717    var fret_dy = (fret_number - position - 0.5) * this.fret_spacing;
718    //var circle =
719    this.circle(
720      fret_left + (string_number - 1) * this.string_spacing,
721      this.margin_top + fret_dy, this.note_radius).attr({ stroke: this.color, fill: this.color });
722    if (!(note[1] === undefined)) {
723      this.text(fret_left + (string_number - 1) * this.string_spacing,
724        this.margin_top + fret_dy, note[1]).attr({ fill: this.fingering_text_color, "font-size": "12px" });
725    }
726  }
727
728  if (this.has_tab && fret_number >= 0) {
729    this.draw_tab_note((this.strings_drawn - string_number + 1), fret_number, this.margin_left + this.string_spacing * 2.5);
730  }
731}
732
733
734// extend the tab drawing area
735Raphael.fn.tab_extend = function (width) {
736  if (this.has_tab == false) return;
737  for (var i = 0; i < this.strings_drawn; i++) {
738    this.path(this.svg_params(this.current_offset, this.tab_top + (i * this.tab_spacing), width, 0)).attr({ stroke: this.color })
739  }
740}
741
742
743// start the tab
744Raphael.fn.tab_start = function () {
745  if (this.has_tab == false) return;
746  var width = this.tab_char_width * 3;
747  //  start bar
748  this.path(this.svg_params(this.current_offset, this.tab_top, 0, this.tab_height)).attr({ stroke: this.color, "stroke-width": 1 })
749
750  // extend tab
751  this.tab_extend(width);
752
753  //write TAB
754  this.text(this.current_offset + this.tab_char_width, this.tab_top + this.tab_spacing * 1.5, "T").attr({ stroke: this.color, "font-size": "14px" });
755  this.text(this.current_offset + this.tab_char_width, this.tab_top + this.tab_spacing * 2.5, "A").attr({ stroke: this.color, "font-size": "14px" });
756  this.text(this.current_offset + this.tab_char_width, this.tab_top + this.tab_spacing * 3.5, "B").attr({ stroke: this.color, "font-size": "14px" });
757  this.increment_offset(width);
758
759}
760
761
762// draw an individual note in the tab
763Raphael.fn.draw_tab_note = function (string_number, token, left_offset) {
764  // NB: internal string_number in tab counts from high to low
765  this.text(this.current_offset + left_offset,
766    this.tab_top + this.tab_spacing * (string_number - 1),
767    token).attr({ fill: this.color, "font-size": "16px" });
768}
769
770// gets string number from token $[1-6|EADGBe]
771Raphael.fn.get_string_number = function (token) {
772  var string_number = null;
773  if (token.match(/^\$[1-6]/) != null) {
774    string_number = token.substr(1, 1);
775  } else if (token.match(/^\$[EADGBe]/) != null) {
776    string_number = 6 - "EADGBe".indexOf(token.substr(1, 1));
777  }
778  return string_number;
779}
780
781
782// identify if full chord of notes specified i.e. A:1 = X02220 or C:4 = 8.10.10.9.8.8
783// returns:
784//   false = not a full chord representation
785//   array = array of notes (low to high)
786Raphael.fn.get_fullchord_notes = function (token) {
787  var rc = false;
788  if (token.match(/[^\.xX0-9]/) != null) {
789    rc = false;
790  } else {
791    if (token.match(/\./) != null) {
792      rc = token.split('.');
793    } else {
794      rc = token.split('');
795    }
796    if (rc.length != 6) rc = false;
797  }
798  return rc;
799}
800
801
802// draw a token on the tab
803Raphael.fn.tab_note = function (token) {
804  if (this.has_tab == false) return;
805
806  if (token.match(/\$/) != null) { // contains a string specifier
807    if (token.match(/\./) != null) { // is a multi-string specifier
808      var parts = token.split(".");
809      var width = 2;
810      for (var i = 0; i < parts.length; i++) { // get the max length of the multi-string specifiers
811        if (parts[i].length > width) width = parts[i].length;
812      }
813      width *= this.tab_char_width + 1;
814      this.tab_extend(width);
815      for (var i = 0; i < parts.length; i++) {
816        var part = parts[i];
817        var string_number = this.get_string_number(part);
818        if (string_number != null) {
819          this.tab_current_string = string_number;
820        } else if (this.tab_current_string > 0) {
821          this.draw_tab_note(this.tab_current_string, part, width * 0.5);
822        }
823      }
824      this.increment_offset(width);
825
826    } else { // just a string setting
827      this.tab_current_string = this.get_string_number(token);
828    }
829  } else {
830    var fullchord_notes = this.get_fullchord_notes(token);
831    if (fullchord_notes) {
832      var max_chars = fullchord_notes.max_chars();
833      var width = this.tab_char_width * (max_chars + 2);
834      this.tab_extend(width);
835      for (var i = 0; i < fullchord_notes.length; i++) {
836        this.draw_tab_note(6 - i, fullchord_notes[i], width * 0.5);
837      }
838      this.increment_offset(width);
839    } else if (this.tab_current_string > 0) { // else draw literal, but only if a current string selected
840      var width = this.tab_char_width * (token.length + 2);
841      this.tab_extend(width);
842      this.draw_tab_note(this.tab_current_string, token, width * 0.5);
843      this.increment_offset(width);
844    }
845  }
846}
847
848
849// main drawing routine entry point: to render a token - chord or tab
850Raphael.fn.render_token = function (token) {
851
852  var c = new jtabChord(token);
853
854  if (c.isValid) { // draw chord
855    var chord = c.chordArray;
856    // this.chord_fretboard(chord[0], c.fullChordName );
857    this.chord_fretboard(chord[0], c.chordName);
858    for (var i = 1; i < chord.length; i++) {
859      this.chord_note(chord[0], i, chord[i]);
860    }
861    this.increment_offset();
862
863  } else {
864    if (token == "/") {
865      this.stroke();
866    } else if (token == "|") {
867      this.bar();
868    } else if (token == "||") {
869      this.doublebar();
870    } else if (this.has_tab) {
871      this.tab_note(token);
872    }
873
874  }
875}
876
877
878//
879// add jtab class methods
880//
881
882
883// determine nature of the token stream
884// returns:
885//   1 : chord and tab present
886//   2 : chord only
887//   3 : tab only
888//   0 : unknown
889jtab.characterize = function (notation) {
890  var tabtype = 0;
891
892  if (notation == undefined) {
893    notation = '';
894  }
895
896  var gotCustomChord = (notation.match(/[\%]([0-4|T|X])?/) != undefined);
897  var gotNormalChord = (notation.match(/[^\$][A-G]|^[A-G]/) != undefined);
898  var gotTab = ((notation.match(/\$/) != null) || (notation.match(/[^\%][0-9|Xx|\.]{6,}/) != null));
899  var gotChord = gotNormalChord || gotCustomChord;
900
901  // set defaults - apply scaling here (TODO)
902  Raphael.fn.current_offset = Raphael.fn.margin_left;
903  if (gotChord && gotTab) { // chord and tab
904    tabtype = 1;
905    Raphael.fn.has_chord = true;
906    Raphael.fn.has_tab = true;
907    Raphael.fn.tab_top = Raphael.fn.chord_height + Raphael.fn.tab_margin_top;
908    Raphael.fn.total_height = Raphael.fn.tab_top + Raphael.fn.tab_height + Raphael.fn.margin_bottom;
909  } else if (gotChord) { // chord only
910    tabtype = 2;
911    Raphael.fn.has_chord = true;
912    Raphael.fn.has_tab = false;
913    Raphael.fn.tab_top = Raphael.fn.chord_height + Raphael.fn.tab_margin_top;
914    Raphael.fn.total_height = Raphael.fn.chord_height;
915  } else if (gotTab) { // tab only
916    tabtype = 3;
917    Raphael.fn.has_chord = false;
918    Raphael.fn.has_tab = true;
919    Raphael.fn.tab_top = Raphael.fn.tab_margin_top;
920    Raphael.fn.total_height = Raphael.fn.tab_top + Raphael.fn.tab_height + Raphael.fn.margin_bottom;
921  }
922  Raphael.fn.tabtype = tabtype;
923  return tabtype;
924}
925
926// utility function to get calculated style based on given element
927jtab.getStyle = function (element, style) {
928  var value = element.css(style);
929  if (!value) {
930    if (document.defaultView) {
931      value = document.defaultView.getComputedStyle(element[0], "").getPropertyValue(style);
932    } else if (element.currentStyle) {
933      value = element.currentStyle[style];
934    }
935  }
936
937  return value;
938}
939
940// set color pallette for the jtab rendering
941jtab.setPalette = function (element) {
942  var fgColor = jtab.getStyle(jQuery(element), 'color');
943  if (!fgColor) {
944    fgColor = '#000';
945  }
946  Raphael.fn.color = fgColor;
947  Raphael.fn.tab_text_color = fgColor;
948
949  bgColor = jtab.getStyle(jQuery(element), 'background-color');
950  if (!bgColor || (bgColor == 'transparent') || (bgColor == 'rgba(0, 0, 0, 0)')) {
951    bgColor = '#fff';
952  }
953  Raphael.fn.fingering_text_color = bgColor;
954}
955
956// Render the tab for a given +element+.
957// +element+ is a DOM node
958// +notation_text+ is the optional notation to render (if not specified, +element+ text content will be used)
959// After rendering, the +element+ will be given the additional "rendered" class.
960jtab.render = function (element, notation_text) {
961
962  var notation = notation_text || jQuery(element).text() || '';
963
964  var tabtype = jtab.characterize(notation);
965  if (tabtype == 0) return;
966
967  var rndID = "builder_" + jtab.element_count++;
968
969  // add the Raphael canvas in its own DIV. this gets around an IE6 issue with not removing previous renderings
970  var canvas_holder = jQuery('<div id="' + rndID + '"></div>').css({ height: Raphael.fn.total_height });
971
972  jQuery(element).html(canvas_holder);
973  jtab.setPalette(element);
974  canvas = Raphael(rndID, 80, Raphael.fn.total_height);
975  canvas.tab_start();
976
977  var tokens = notation.split(/\s/);
978  for (var i = 0; i < tokens.length; i++) {
979    canvas.render_token(tokens[i]);
980  }
981  jQuery(element).addClass('rendered');
982}
983
984// Render all nodes with class 'jtab'.
985// +within_scope+ is an optional selector that will restrict rendering to only those nodes contained within.
986jtab.renderimplicit = function (within_scope) {
987  jQuery('.jtab', within_scope).not('.rendered').each(function (name, index) { jtab.render(this); });
988}