1// abc2svg - ABC to SVG translator
2// @source: https://chiselapp.com/user/moinejf/repository/abc2svg
3// Copyright (C) 2014-2020 Jean-Francois Moine - LGPL3+
4//grid.js-module to insert a chord grid before or after a tune
5abc2svg.grid={pl:'<path class="stroke" stroke-width="1" d="M',block_gen:function(of,s){if(s.subtype!="grid"){of(s)
6return}
7var abc=this,img,cls,cfmt=abc.cfmt(),grid=cfmt.grid
8function build_grid(chords,bars,font,wmx){var i,k,l,nr,bar,w,hr,x0,x,y,yl,lc='',cells=[],nc=grid.n
9function set_chords(){var i,ch,pch='-'
10for(i=0;i<chords.length;i++){ch=chords[i]
11if(!ch[0])
12ch[0]=pch
13if(ch.length==0)
14continue
15if(ch.length==1){pch=ch[0]
16continue}
17if(ch.length==2){ch[2]=ch[1];ch[1]=null;pch=ch[2]
18continue}
19if(ch.length==3){pch=ch[2]
20continue}
21if(!ch[2])
22ch[2]=ch[1]||ch[0];pch=ch[3]}}
23function build_cell(cell,x,y,yl,hr){if(cell.length>1){abc.out_svg(abc2svg.grid.pl)
24abc.out_sxsy(x-wmx/2,' ',yl)
25abc.out_svg('l'+
26wmx.toFixed(1)+' -'+hr.toFixed(1)+'"/>\n')
27if(cell[1]){abc.out_svg(abc2svg.grid.pl)
28abc.out_sxsy(x-wmx/2,' ',yl+hr)
29abc.out_svg('l'+
30(wmx/2).toFixed(1)+' '+(hr/2).toFixed(1)+'"/>\n')
31abc.set_font('gs')
32abc.xy_str(x-wmx/3,y,cell[0])
33abc.xy_str(x,y+hr/3,cell[1])}else{abc.set_font('gs')
34abc.xy_str(x-wmx*.2,y+hr/4,cell[0])}
35if(cell.length>=3){if(cell[3]){abc.out_svg(abc2svg.grid.pl)
36abc.out_sxsy(x,' ',yl+hr/2)
37abc.out_svg('l'+
38(wmx/2).toFixed(1)+' '+(hr/2).toFixed(1)+'"/>\n')
39abc.set_font('gs')
40abc.xy_str(x,y-hr/3,cell[2])
41abc.xy_str(x+wmx/3,y,cell[3])}else{abc.set_font('gs')
42abc.xy_str(x+wmx*.2,y-hr/4,cell[2])}}}else{abc.set_font('grid')
43abc.xy_str(x,y,cell[0])}}
44set_chords()
45if(!grid.ls){cells=chords}else{bar=bars;bars=[]
46for(i=0;i<grid.ls.length;i++){l=grid.ls[i]
47if(l.indexOf('-')<0)
48l=[l,l]
49else
50l=l.split('-')
51for(k=l[0]-1;k<l[1];k++){if(!chords[k])
52break
53cells.push(chords[k]);bars.push(bar[k])}}}
54if(nc<0)
55nc=-nc
56if(nc<3)
57nc=cells.length%6==0?6:8
58if(nc>cells.length)
59nc=cells.length;hr=font.size*2
60if(wmx<hr*1.5)
61wmx=hr*1.5
62x0=img.width-img.lm-img.rm
63w=wmx*nc
64if(w>x0){nc/=2;w/=2}
65yl=-1
66y=-1+font.size*.6
67nr=0
68x0=(x0/cfmt.scale-w)/2
69for(i=0;i<cells.length;i++){if(i==0||(grid.repbrk&&(bars[i].slice(-1)==':'||bars[i][0]==':'))||k>=nc){y-=hr
70yl-=hr
71x=x0+wmx/2
72k=0
73nr++}
74k++
75build_cell(cells[i],x,y,yl,hr)
76x+=wmx}
77abc.out_svg('<path class="stroke" stroke-width="1" d="\n')
78y=-1
79for(i=0;i<=nr;i++){abc.out_svg('M')
80abc.out_sxsy(x0,' ',y)
81abc.out_svg('h'+w.toFixed(1)+'\n')
82y-=hr}
83x=x0
84for(i=0;i<=nc;i++){abc.out_svg('M')
85abc.out_sxsy(x,' ',-1)
86abc.out_svg('v'+(hr*nr).toFixed(1)+'\n')
87x+=wmx}
88abc.out_svg('"/>\n')
89y=-1+font.size*.7
90x=x0
91for(i=0;i<bars.length;i++){bar=bars[i]
92if(bar[0]==':'){abc.out_svg('<text class="'+cls+'" x="')
93abc.out_sxsy(x-5,'" y="',y)
94abc.out_svg('" style="font-weight:bold;font-size:'+
95(font.size*1.5).toFixed(1)+'px">:</text>\n')}
96if(i==0||(grid.repbrk&&(bars[i].slice(-1)==':'||bars[i][0]==':'))||k>=nc){y-=hr;x=x0
97k=0}
98k++
99if(bar.slice(-1)==':'){abc.out_svg('<text class="'+cls+'" x="')
100abc.out_sxsy(x+5,'" y="',y)
101abc.out_svg('" style="font-weight:bold;font-size:'+
102(font.size*1.5).toFixed(1)+'px">:</text>\n')}
103x+=wmx}
104abc.vskip(hr*nr+6)}
105var p_voice,n,font,f2
106img=abc.get_img()
107if(!cfmt.gridfont)
108abc.param_set_font("gridfont","serif 16")
109font=abc.get_font('grid')
110if(font.class)
111font.class+=' mid'
112else
113font.class='mid'
114cls=abc.font_class(font)
115abc.param_set_font("gsfont",font.name+' '+(font.size*.7).toFixed(1))
116f2=cfmt.gsfont
117if(font.weight)
118f2.weight=font.weight
119if(font.style)
120f2.style=font.style
121f2.class=font.class
122abc.add_style("\n.mid {text-anchor:middle}")
123abc.blk_flush()
124build_grid(s.chords,s.bars,font,s.wmx)
125abc.blk_flush()},output_music:function(of){var C=abc2svg.C,abc=this,tsfirst=abc.get_tsfirst(),voice_tb=abc.get_voice_tb(),grid=abc.cfmt().grid
126function get_beat(s){var beat=C.BLEN/4
127if(!s.a_meter[0]||s.a_meter[0].top[0]=='C'||!s.a_meter[0].bot)
128return beat
129beat=C.BLEN/s.a_meter[0].bot[0]|0
130if(s.a_meter[0].bot[0]==8&&s.a_meter[0].top[0]%3==0)
131beat=C.BLEN/8*3
132return beat}
133function build_chords(sb){var s,i,w,bt,rep,bars=[],chords=[],chord=[],beat=get_beat(voice_tb[0].meter),wm=voice_tb[0].meter.wmeasure,cur_beat=0,beat_i=0,wmx=0
134bars.push('|')
135for(s=tsfirst;s;s=s.ts_next){while(s.time>cur_beat){if(beat_i<3)
136beat_i++
137cur_beat+=beat}
138switch(s.type){case C.NOTE:case C.REST:if(!s.a_gch)
139break
140for(i=0;i<s.a_gch.length;i++){if(s.a_gch[i].type=='g'){if(!chord[beat_i]){chord[beat_i]=s.a_gch[i].text
141w=abc.strwh(chord[beat_i])[0]
142if(w>wmx)
143wmx=w}
144break}}
145break
146case C.BAR:bt=grid.norep?'|':s.bar_type
147if(s.time<wm){if(chord.length){chords.push(chord)
148bars.push(bt)}else{bars[0]=bt}}else{if(!s.bar_num)
149break
150chords.push(chord)
151bars.push(bt)}
152chord=[]
153cur_beat=s.time
154beat_i=0
155if(bt.indexOf(':')>=0)
156rep=true
157while(s.ts_next&&s.ts_next.type==C.BAR)
158s=s.ts_next
159break
160case C.METER:beat=get_beat(s)
161wm=s.wmeasure
162break}}
163if(chord.length){bars.push('')
164chords.push(chord)}
165if(!chords.length)
166return
167wmx+=abc.strwh(rep?'    ':'  ')[0]
168sb.chords=chords
169sb.bars=bars
170sb.wmx=wmx}
171if(grid){var C=abc2svg.C,tsfirst=this.get_tsfirst(),voice_tb=this.get_voice_tb(),p_v=voice_tb[this.get_top_v()],s={type:C.BLOCK,subtype:'grid',dur:0,time:0,p_v:p_v,v:p_v.v,st:p_v.st}
172build_chords(s)
173if(!s.chords){}else if(grid.nomusic){this.set_tsfirst(s)}else if(grid.n<0){for(var s2=tsfirst;s2.ts_next;s2=s2.ts_next);s.time=s2.time
174s.prev=p_v.last_sym.prev
175s.prev.next=s
176s.next=p_v.last_sym
177p_v.last_sym.prev=s
178s.ts_prev=s2.ts_prev
179s.ts_prev.ts_next=s
180s.ts_next=s2
181s2.ts_prev=s
182if(s2.seqst){s.seqst=true
183s2.seqst=false}}else{s.next=p_v.sym
184s.ts_next=tsfirst
185tsfirst.ts_prev=s
186this.set_tsfirst(s)
187p_v.sym.prev=s
188p_v.sym=s}}
189of()},set_fmt:function(of,cmd,parm){if(cmd=="grid"){if(!parm)
190parm="1";parm=parm.split(/\s+/)
191var grid={n:Number(parm.shift())}
192if(isNaN(grid.n)){if(parm.length){this.syntax(1,this.errs.bad_val,"%%grid")
193return}
194grid.n=1}
195while(parm.length){var item=parm.shift()
196if(item=="norepeat")
197grid.norep=true
198else if(item=="nomusic")
199grid.nomusic=true
200else if(item=="repbrk")
201grid.repbrk=true
202else if(item.slice(0,8)=="include=")
203grid.ls=item.slice(8).split(',')}
204this.cfmt().grid=grid
205return}
206of(cmd,parm)},set_hooks:function(abc){abc.block_gen=abc2svg.grid.block_gen.bind(abc,abc.block_gen)
207abc.output_music=abc2svg.grid.output_music.bind(abc,abc.output_music);abc.set_format=abc2svg.grid.set_fmt.bind(abc,abc.set_format)}}
208abc2svg.modules.hooks.push(abc2svg.grid.set_hooks);abc2svg.modules.grid.loaded=true
209