abc2svg
Check-in Differences
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Difference From 433944c7887548a5 To af5bcae7ac2b432e

2020-01-01
16:36
core: fix: bad transposition with score=<note1> (without <note2>) ... (check-in: dc98412a6d user: jef tags: trunk)
14:18
New release v1.20.7 ... (check-in: af5bcae7ac user: jef tags: trunk, v1.20.7)
14:06
core: fix: bad display when m: and '%%writefields m 0' ... (check-in: 2bcf730e67 user: jef tags: trunk)
2019-02-06
14:25
batch: add 'abctopdf' ... (check-in: 2c9cf4bafe user: jef tags: trunk)
2019-02-01
15:55
New release v1.19.1 ... (check-in: 433944c788 user: jef tags: trunk, v1.19.1)
14:01
copyright update to 2019 ... (check-in: 83a7d2efe0 user: jef tags: trunk)

Changes to README.md.
︙
23
24
25
26
27
28
29
30
31

32
33

34
35
36
37
38
39
40
41












42
43
44

45
46
47

48
49
50
51
52
53
54
55
56
57
58
59
60
61

62
63
64
65








66
67
68


69
70
71
72
73


74
75

76
77
78




79


80
81
82
83
84
85

86
87

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

125
126
127
128
129
130
131

132
133
134
135
136
137
138
139
140
141
142
143
144
145


146
147
148
149
150
151
152
153
154
155
156

157
158
159
160
161
162
163

164
165
166
167
168
169
170
171

172
173
174
175
176
177
178
179
180
181
182


183
184

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

202
203
204
205
206
207
208
209
210






211
212
213
214
215
216
217
218
219
220


























23
24
25
26
27
28
29


30
31

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
80
81
82
83
84
85
86


87
88
89
90
91
92

93
94
95

96



97
98
99
100

101
102
103
104
105
106
107

108


109

110
111
112
113
114
115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141



142

143
144
145



146

147
148
149
150
151
152
153
154
155
156



157
158

159
160
161
162
163
164
165
166


167

168
169
170
171


172

173
174
175
176
177


178

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209

210
211
212
213
214
215
216
217
218

219
220
221
222
223
224
225
226
227
228
229
230
231
232
233

234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259







-
-
+

-
+








+
+
+
+
+
+
+
+
+
+
+
+


-
+



+













-
+




+
+
+
+
+
+
+
+

-
-
+
+




-
+
+

-
+
-
-
-
+
+
+
+
-
+
+





-
+
-
-
+
-













-



















-
-
-
+
-



-
-
-
+
-










-
-
-
+
+
-








-
-
+
-




-
-
+
-





-
-
+
-










+
+


+
















-
+








-
+
+
+
+
+
+









-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- `abc2svg-1.js`   
  This script is the **abc2svg** core.  
  It contains the ABC parser and the SVG generation engine.  
  It must be included in the (X)HTML header of the pages
  where ABC rendering is needed (in `<script src=` tags).

- `abcemb-1.js`   
  This script is to be used with the core in (X)HTML files.  
  It replaces the ABC sequences by SVG images of the music
  This script replaces the ABC or MEI sequences by SVG images of the music
  (the ABC sequences start on `X:` or `%abc` at start of line,
  and stop on any ML tag).  
  and stop on any ML tag - see below for MEI).  
  See the
  [%%beginml documentation](http://moinejf.free.fr/abcm2ps-doc/beginml.xhtml)
  for an example.   
  When the URL of the (X)HTML file ends with '#' followed by a string,
  only the first tune containing this string is displayed.   
  Note that, if the ABC sequence contains the characters '<', '>' or '&',
  it must be enclosed in a XML comment (starting after the first `X:` or `%abc`).

- `abcemb1-1.js`   
  This script is to be used with the core in (X)HTML files.  
  It works quite the same as the previous script, but replaces the whole
  page by the selected tune.
  When there is no selection ('#' + string at the end of the URL),
  a list of the tunes is displayed.   
  When a tune is displayed, a menu offers the edition of the ABC source and
  the automatic scroll of the music (this is done according to the tempo -
  repeats and parts are not handled).   
  See [this tune](http://moinejf.free.fr/abc/boyvin-2-2.html)
  for an example.   

- `abcemb2-1.js`   
  This script is also to be used with the core in (X)HTML files.  
  The differences with the previous script are
  The differences with the script `abcemb-1.js` are:
  - the ABC sequences are replaced in HTML elements with the class `abc`,
  - the string after '#' in the URL does a real %%select (i.e. this may
    select many tunes),
  - there is no menu nor list of tunes
  - if the ABC sequences contain the characters '<', '>' or '&',
    either these sequences must be enclosed in a XML comment, or the characters
    must be replaced by their XML counterparts ('&amp;lt;', '&amp;gt;' or '&amp;amp;').
  
- `abcdoc-1.js`   
  This script is also to be used in (X)HTML pages with the core.  
  Mainly used for ABC documentation, it lets the ABC source sequences
  in the page before the SVG images.  
  See the source of
  [abcm2ps/abc2svg features](http://moinejf.free.fr/abcm2ps-doc/features.xhtml)
  for an example.

- `play-1.js`   
  This script may be used with `abcemb-1.js` for playing the
  This script may be used with `abcemb{,1,2}-1.js` for playing the
  rendered ABC music.  
  See [this page](http://moinejf.free.fr/abcm2ps-doc/au_clair.xhtml)
  for an example.

- `abcweb2-1.js`   
  This script works the same as `abcemb2-1.js` but it must use `snd-1.js`
  for playing.
  
- `snd-1.js`   
  This script contains a other way for playing. It is used in the editor
  and it may be used with `abcweb2-1.js`.

- `follow-1.js`   
  This script may be used after `play-1.js` for highlighting the notes
  while playing.   
  This script may be used after `play-1.js` or `snd-1.js` for highlighting
  the notes while playing.   
  See [this page](http://moinejf.free.fr/abcm2ps-doc/tabac.xhtml)
  for an example.

- `edit-1.xhtml`   
  This is a simple web ABC editor/player.
  This is a simple web
  [ABC editor/player](http://moinejf.free.fr/js/edit-1.xhtml).

When looking at a ABC file in a web browser, you may also use
When looking at a ABC file in a web browser, you may also use a
this
<a href="javascript:(function(){d=document;b=d.body;b.innerHTML=%22\n%25abc-2.2\n%25%3c!--\n%22+b.textContent+%22%25--%3e\n%22;function%20f(u){s=d.createElement('script');s.setAttribute('src',u);b.appendChild(s);};f('http://moinejf.free.fr/js/abc2svg-1.js');f('http://moinejf.free.fr/js/abcemb-1.js');f('http://moinejf.free.fr/js/play-1.js');function%20t(){if(typeof%20dom_loaded=='function'){dom_loaded()}else{setTimeout(t,200)}};setTimeout(t,200)})();void(0)">bookmarklet</a>
and render the music
<a href="https://en.wikipedia.org/wiki/Bookmarklet">bookmarklet</a>
as
<a href="javascript:(function(){var%20s,n=3,d=document,b=d.body;b.innerHTML='\n%25abc-2.2\n%25%3c!--\n'+b.textContent+'%25--%3e\n';function%20f(u){s=d.createElement('script');s.src='http://moinejf.free.fr/js/'+u;s.onload=function(){if(--n==0)dom_loaded()};d.head.appendChild(s)};f('play-1.js');f('follow-1.js');f('abcemb-1.js')})();void(0)">this one</a>
for rendering all tunes, or
(create a bookmark and copy the link location into the address/location box).
<a href="javascript:(function(){var%20s,n=3,d=document,b=d.body;b.innerHTML='\n%25abc-2.2\n%25%3c!--\n'+b.textContent+'%25--%3e\n';function%20f(u){s=d.createElement('script');s.src='http://moinejf.free.fr/js/'+u;s.onload=function(){if(--n==0)dom_loaded()};d.head.appendChild(s)};f('play-1.js');f('follow-1.js');f('abcemb1-1.js')})();void(0)">this one</a>
for rendering the tunes one by one.

##### Notes:
- The music is rendered as SVG images. There is one image per
  music line / text block.  
  If you want to move these images to some other files,
  each one must contain the full CSS and defs. For that, insert
  each one must contain the full CSS and defs. For that, insert   
```
%%fullsvg x
  `        %%fullsvg x`   
```
  in the ABC file before rendering (see the
  [fullsvg documentation](http://moinejf.free.fr/abcm2ps-doc/fullsvg.xhtml)
  for more information).

- Playing uses the HTML5 audio and/or midi APIs.

- With the editor, if you want to render ABC files
  which contain `%%abc-include`, you must:
  - load the ABC file from the browse button
  - click in the include file name
  - load the include file by the same browse button  

  Then, you may edit and save both files.  
  Rendering/playing is always done from the first ABC file.  
  There may be only one included file.

- The editor comes with different ways to enter the music from the keyboard.  
  If you have a US keyboard, you may try these bookmarklets:
<a href="javascript:(function(){if(typeof%20loadjs=='function'){loadjs('abckbd-1.js')}else{alert('use%20with%20abc2svg%20editor')}})();void(0)">keyboard 1</a>
and
<a href="javascript:(function(){if(typeof%20loadjs=='function'){loadjs('abckbd2-1.js')}else{alert('use%20with%20abc2svg%20editor')}})();void(0)">keyboard 2</a>

- The .js and .xhtml file names have a suffix which is the version of
  the core interface (actually '`-1`').

### nodeJS usage

Installed via **npm**, the **abc2svg** package comes with the
command line (batch) programs `abc2svg` and `abc2odt`.

These ones may be used as **abcm2ps** to generate XHTML or ODT files.   

`abc2svg` writes to standard output:   

```
    abc2svg mytunes.abc > Out.xhtml
`        abc2svg mytunes.abc > Out.xhtml`
```

`abc2odt` output is `abc.odt` or the file specified
by the command line argument `-o`:   

```
    abc2odt my_file.abc -o my_file.odt
`        abc2odt my_file.abc -o my_file.odt`
```

### Build

If you want to build the **abc2svg** scripts in your machine,
you must first get the files from
[chisel](https://chiselapp.com/user/moinejf/repository/abc2svg),
either as a tarball or a Zip archive
(click `Timeline` and then in the top commit),
or by cloning the repository in some directory:

```
    fossil clone https://chiselapp.com/user/moinejf/repository/abc2svg abc2svg.fossil
    fossil open abc2svg.fossil
> `fossil clone https://chiselapp.com/user/moinejf/repository/abc2svg abc2svg.fossil`   
> `fossil open abc2svg.fossil`
```

Then, building is done using the tool [ninja](https://ninja-build.org/)
or [samurai](https://github.com/michaelforney/samurai).  
You may do it:

- without minification  
  This is interesting for debug purpose, the scripts being more human friendly.

```
    NOMIN=1 samu -v
  `        NOMIN=1 samu -v`
```

- in a standard way with minification  
  In this case, you need the tool `uglifyjs` which comes with nodeJS.

```
    samu -v
  `        samu -v`
```

If you also want to change or add music glyphs, you may edit the source
file `font/abc2svg.sfd`. In this case, you will need both `base64` and `fontforge`,
and run

```
    samu -v font.js
`        samu -v font.js`
```

If you cannot or don't want to install `ninja` or `samurai`, you may build
the abc2svg files by `./build` which is a shell script.

### Batch

After building the **abc2svg** scripts, you will be able to generate music
sheets from the command line as you did with `abcm2ps`, thanks to the
following shell scripts (the result goes to stdout):  

- `abcqjs` with `qjs` 
   ([QuickJS by Fabrice Bellard and Charlie Gordon](https://bellard.org/quickjs/))
- `abcjs24` with `js24` (Mozilla JavaScript shell - Spidermonkey)
- `abcjs52` with `js52` (Mozilla JavaScript shell - Spidermonkey)
- `abcjs60` with `js60` (Mozilla JavaScript shell - Spidermonkey)
- `abcjsc` with `jsc-1` (webkitgtk2)
- `abcnode` with `node` (nodeJS)
- `abcv8` with `d8` (Google libv8)

#### backend scripts

By default, the batch scripts generate (XHTML+SVG) files.   
This output may be modified by backend scripts. These ones must appear
just after the command.   
There are:

- `toabc.js`   
  This script outputs back the (selected) ABC tunes of the ABC source file.   
  Transposition is applied.   
  The resulting file does not contain the formatting parameters.   
  Example:   
  `abcjs52 toabc.js my_file.abc --select X:2 > tune_2.abc`
  `        abcqjs toabc.js my_file.abc --select X:2 > tune_2.abc`

- `toabw.js`   
  This script outputs a Abiword file (ABW+SVG) which may be read by some
  word processors (abiword, libreoffice...) and converted to many other
  formats by the batch function of abiword.   
  The abc2svg music font (`abc2svf.woff` or `abc2svg.ttf`) must be installed
  in the local system for displaying and/or converting the .abw file.   
  Example:   
  `abcv8 toabw.js my_file.abc > my_file.abw`
  `        abcv8 toabw.js my_file.abc > my_file.abw`

- `tomei.js`   
  This script outputs the music as a [MEI](https://music-encoding.org/) file.   
  Indeed, only one tune may be translated from ABC to MEI (multi-tunes ABC
  generates bad MEI).

- `toodt.js`   
  This script creates an Open Document (ODT+SVG) which may be read by most
  word processors (abiword, libreoffice...).   
  It runs only with the npm script `abc2svg` and asks for the npm module
  `jszip` to be installed.   
  The output ODT document may be specified by the command line argument `-o`
  (default `abc.odt`).   
  Example:   
  `abc2svg toodt.js my_file.abc -o my_file.odt`
  `        abc2svg toodt.js my_file.abc -o my_file.odt`

- `toparam.js`   
  This script just outputs the abc2svg parameters.

#### PDF generation

`abctopdf` is a shell script which converts ABC to PDF using one of the
previous shell scripts and the program `rsvg-convert`.   
As a constraint, the used music font must be installed and defined by
`%%musicfont <fontname>`.   
Example:   
`        abctopdf my_file.abc -o my_file.pdf`

### MEI support

As an experimental feature, an extented core `mei2svg-1.js` may be generated.
This one may handle both the ABC and [MEI](https://music-encoding.org/) notations.

In browser mode, the script `abcemb-1.js` loads either abc2svg-1.js or
mei2svg-1.js after checking the music notation type (`%abc` or `X:` is ABC,
`<mei` is MEI - see
[this tune](http://moinejf.free.fr/abc/Czerny_op603_6.html) for an example).

In batch mode, the script `abcqjs` also loads the right abc2svg core
according to the source file extension (`.abc` or `.mei`).
Changes to abc2odt.
︙
34
35
36
37
38
39
40






41
42
43
44
45
46
47
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53







+
+
+
+
+
+







	process.exit(1)
}
abc2svg.readFile = function(fname) {
    var	fs = require("fs")

	return fs.readFileSync(fname).toString()
}

abc2svg.get_mtime = function(fn) {
    var	fs = require("fs")

	return fs.statSync(fn).mtime
}

abc2svg.loadjs = function(fn, relay, onerror) {
	try {
		var geval = eval;
		geval(abc2svg.readFile(__dirname + sep + fn))
		if (relay)
			relay()
︙
Changes to abc2svg.
︙
34
35
36
37
38
39
40






41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

64
65
66
67
68
69
70







+
+
+
+
+
+

















-







	process.exit(1)
}
abc2svg.readFile = function(fname) {
    var	fs = require("fs")

	return fs.readFileSync(fname).toString()
}

abc2svg.get_mtime = function(fn) {
    var	fs = require("fs")

	return fs.statSync(fn).mtime
}

abc2svg.loadjs = function(fn, relay, onerror) {
	try {
		var geval = eval;
		geval(abc2svg.readFile(__dirname + sep + fn))
		if (relay)
			relay()
	} catch(e) {
		if (onerror)
			onerror()
		else
			printErr('Cannot read file ' + fn)
	}
}

// load the command line
m = require("abc2svg/cmdline");
global.abc = m.abc;
global.user = m.user;
global.abc_cmd = m.abc_cmd

// main
var args = process.argv
if (!args[2])
	usage();
︙
Changes to abc2svg.ttf.

cannot compute difference between binary files

Added abcjs60.

























































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/usr/bin/env js60
// abc2svg with js60 (Mozilla JavaScript shell - Spidermonkey
//		packages: debian: libmozjs-60-dev, voidlinux: mozjs60)

loadRelativeToScript("abc2svg-1.js")

// interpreter specific functions
abc2svg.print = function(str) {
	print(str)
}
abc2svg.printErr = function(str) {
	printErr(str)
}

abc2svg.quit = function() {
	quit(1)
}
abc2svg.readFile = function(fname) {
	return read(fname)
}
abc2svg.loadjs = function(fn, relay, onerror) {
	try {
		loadRelativeToScript(fn)
		if (relay)
			relay()
	} catch(e) {
		if (onerror)
			onerror()
		else
			printErr('Cannot read file ' + fn)
	}
} // loadjs()

loadRelativeToScript("cmdline.js")

// main
	var	args = scriptArgs

	if (/\.js$/.test(args[0])) {
		load(args[0])
		args.shift()
	}
	if (typeof abc2svg.abc_init != 'function')
		loadRelativeToScript("toxhtml.js")
	if (!args[0]) {
		printErr('ABC translator with js60 (SpiderMonkey)\
 and abc2svg-' + abc2svg.version + ' ' + abc2svg.vdate + '\n\
Usage:\n\
  abcjs60 [script.js] [g_options] ABC_file [f_options] [ABC_file [f_options]]*\n\
Arguments:\n\
  script.js  generation script to load - default: toxhtml.js (XHTML+SVG)\n\
  g_options  global ABC options\n\
  ABC_file   ABC file\n\
  f_options  file options')
		quit()
	}
	abc_cmd("abcjs60", args)
Changes to abcjsc.
1
2
3
4
5
6
7
8
9
10

11
12
13
14

15















16



17
18
19
20
21
22
23
24
25
26
27
28



29
30
31
32
33
34
35
1
2
3
4
5
6
7
8
9

10
11
12
13

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55









-
+



-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+












+
+
+







#!/bin/sh
# abc2svg with jsc (webkit2gtk)
progdir=`dirname $0`

exec jsc -e "var progdir='${progdir}/'

// function to do utf-8 work...
// uncommenting this function asks for also uncommenting its call in cmdline.js
//function utf_convert(fin) {
//	var	b1, b2,
//	var	b1, b2, b3,
//		fout = ''
//	for (var i = 0; i < fin.length; i++) {
//		b1 = fin.charCodeAt(i)
//		if (b1 >= 128) {
//		if (b1 >= 0x80) {
//			b2 = fin.charCodeAt(++i);
//			if (b1 >= 0xe0) {
//				b3 = fin.charCodeAt(++i);
//				if (b1 >= 0xf0) {		// >= 0x10000
//					b1 = ((b1 & 0x07) << 18) +
//						((b2 & 0x3f) << 12) +
//						((b3 & 0x3f) << 6) +
//						(fin.charCodeAt(++i) & 0x3f);
//					fout += String.fromCharCode((b1 >> 10) + 0xd7c0,
//								(b1 & 0x3ff) + 0xdc00)
//				} else {
//					fout += String.fromCharCode(((b1 & 0x0f) << 12) +
//								((b2 & 0x3f) << 6) +
//								(b3 & 0x3f))
//				}
//			} else {
//			fout += String.fromCharCode(((b1 & 0x3f) << 6) + (b2 & 0x7f))
//				fout += String.fromCharCode(((b1 & 0x1f) << 6) +
//							(b2 & 0x3f))
//			}
//		} else {
//			fout += fin[i]
//		}
//	}
//	return fout
//}

load(progdir + 'abc2svg-1.js')

// interpreter specific functions
abc2svg.print = function(str) {
	print(str)
}
abc2svg.printErr = function(str) {
	printErr(str)
}
abc2svg.quit = function() {
	quit(1)
}
abc2svg.readFile = function(fname) {
	return read(fname)
}
︙
Changes to abcnode.
︙
27
28
29
30
31
32
33






34
35
36
37
38
39
40
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46







+
+
+
+
+
+







	process.exit(1)
}
abc2svg.readFile = function(fname) {
    var	fs = require("fs");

	return fs.readFileSync(fname).toString()
}

abc2svg.get_mtime= function(fn) {
    var	fs = require("fs")

	return fs.statSync(fn).mtime
}

abc2svg.loadjs = function(fn, relay, onerror) {
	try {
		var geval = eval;
		geval(abc2svg.readFile(progdir + fn))
		if (relay)
			relay()
︙
66
67
68
69
70
71
72

73
74
75
76
72
73
74
75
76
77
78
79
80
81
82
83







+




  g_options  global ABC options\n\
  ABC_file   ABC file\n\
  f_options  file options');
		abc2svg.quit(1)
	}
	args.shift()
	args.shift()
	global.user = user;		// (for modules/page.js)
	abc_cmd("abcnode", args)
EOF

node /tmp/toto.js "$@"
Added abcqjs.




















































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
:;# shell script to convert ABC or MEI music notation to other format
# using abc2svg and quickjs (https://bellard.org/quickjs/)

test -f /tmp/abcqjs.js && exec qjs --std /tmp/abcqjs.js $0 "$@"

cat > /tmp/abcqjs.js << EOF
//javascript

    var	i,
	args = scriptArgs
	args.shift()		// remove '/tmp/abcqjs.js'
    var	core = "abc2svg-1.js",
	path = args.shift().	// the 1st argument is the script name
			match(/(.*\/).*/)

	path = path ? path[1] : ""

	i = args.length
	while (--i >= 0) {
		if (/\.mei$/.test(args[i])) {
			core = "mei2svg-1.js"
			break
		}
	}

std.loadScript(path + core)

// interpreter specific functions
function load(fn) {
	return std.loadScript(fn)
}
abc2svg.print = function(str) {
	print(str)
}
abc2svg.printErr = function(str) {
	std.err.printf("%s\n", str)
}

abc2svg.quit = function() {
	std.exit(1)
}
abc2svg.readFile = function(fname) {
    var	l, f,
	b = "",
	bt = "",
	cnt = 30

	try {
		f = std.open(fname, "r")
	} catch(e) {
		return ""
	}

	while (1) {
		if (--cnt < 0) {
			cnt = 30
			bt += b
			b = ""
		}
		l = f.getline()
		if (l == null)
			break
		b += l + "\n"
	}
	f.close()
	return bt + b
} // readfile()

abc2svg.get_mtime = function(fn) {
	return new Date(os.stat(fn)[0].mtime)
}

abc2svg.loadjs = function(fn, relay, onerror) {
	try {
		std.loadScript(path + fn)
		if (relay)
			relay()
	} catch(e) {
		if (onerror)
			onerror()
		else
			printErr('Cannot read file ' + fn)
	}
} // loadjs()

std.loadScript(path + "cmdline.js")

// main
	if (/\.js$/.test(args[0])) {
		if (args[0][0] == "/")
			std.loadScript(args[0])
		else
			std.loadScript(path + args[0])
		args.shift()
	}
	if (typeof abc2svg.abc_init != 'function')
		std.loadScript(path + "toxhtml.js")
	if (!args[0]) {
		abc2svg.printErr('ABC translator with quickjs\
 and abc2svg-' + abc2svg.version + ' ' + abc2svg.vdate + '\n\
Usage:\n\
  abcqjs [script.js] [g_options] ABC_file [f_options] [ABC_file [f_options]]*\n\
Arguments:\n\
  script.js  generation script to load - default: toxhtml.js (XHTML+SVG)\n\
  g_options  global ABC options\n\
  ABC_file   ABC file\n\
  f_options  file options')
		abc2svg.quit()
	}
	abc_cmd("abcqjs", args)
EOF
exec qjs --std /tmp/abcqjs.js $0 "$@"

The previous solution with the shebang line as
#!/usr/bin/env -S qjs --std
did not work with non-GNU versions of env
Added abctopdf.















































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/sh
#
# abctopdf - converts ABC to PDF using abc2svg
#
# Copyright (C) 2019 Jean-François Moine - License GPL3+

if [ $# -eq 0 ]; then
	cat << EOF
ABC to PDF translator using abc2svg
Usage:
  abctopdf [g_options] ABC_file [f_options] [ABC_file [f_options]]* [-o output_file]
Arguments:
  g_options   global ABC options
  ABC_file    ABC file
  f_options   file options
  output_file output file - default is "./abc.pdf"
EOF
	exit 1
fi

# choose a abc2svg batch script with an available JS interpreter
if [ ${0#./} != $0 ]; then
	bdir="./"
else
	bdir=`dirname $0`
	if [ $bdir != '.' ]; then
		bdir="$bdir/"
	else
		bdir=''
	fi
fi
# (sorted from fastest to slowest in ARM machines)
for c in qjs jsc js24 js52 js60 d8 abc2svg node end; do
	if [ $c = 'end' ]; then
		echo "$0: could not find a javascript interpreter - abort"
		exit 1
	fi
	if command -v $c >/dev/null 2>&1; then
		case $c in
		(qjs) c=abcqjs;;
		(jsc) c=abcjsc;;
		(js24) c=abcjs24;;
		(js52) c=abcjs52;;
		(js60) c=abcjs60;;
		(d8) c=abcv8;;
		(abc2svg) c=abc2svg;;
		(node) c=abcnode;;
		esac
		if command -v $c >/dev/null 2>&1; then
			abcscr=$c
			break
		fi
		if command -v $bdir$c >/dev/null 2>&1; then
			abcscr=$bdir$c
			break
		fi
	fi
done
echo "Using $abcscr"

# get the output file name (after '-o')
# default name
out='abc.pdf'
n=0
for a do
	if [ "$a" = "-o" ]; then
		n=1
	elif [ $n -eq 1 ]; then
		out=$a
		n=0
	else
		set -- "$@" "$a"
	fi
	shift
done

# check if some chromium-based browser is available
# test rsvg-convert
for c in chromium-browser vivaldi opera brave-browser epic google-chrome; do
	if command -v $c >/dev/null 2>&1; then
		echo "and $c"
		command $abcscr "$@" > /tmp/o.xhtml
		$c --headless --disable-gpu --print-to-pdf=$out /tmp/o.xhtml
		rm /tmp/o.xhtml
		exit 0
	fi
done

# check the required programs
if ! command -v rsvg-convert >/dev/null 2>&1; then
	echo "$0: program 'rsvg-convert' not found - abort"
	exit 1
fi
echo "and rsvg-convert"

# build a ABC file (with JS script) to change the font and page definitions
# this solves the following problems:
# - the maintainer of librsvg does not want to handle the CSS shorthand 'font:'
# - rsvg-convert always gets the SVG images at 72ppi, ignoring the -p/-d values
# - the sets of SVG images per page must be converted to a set of one SVG per page
cat > /tmp/fix.abc << EOF
%%pageheight 29.7cm
%%fullsvg 1
%%musicfont abc2svg
%%beginjs
    var fix_out
function font_bug(str) {
	return str.replace(/font:.*?["};]/g, function(p) {	// "
	    var	w = p.slice(5, -1).match(/[^ \t"]+|".+?"/g), // "
		l = w.length,
		r = 'font-family:' + w[--l] + ';font-size:' + w[--l]
		while (--l >= 0) {
			switch (w[l]) {
			case 'italic':
				r += ';font-style:italic'
				break
			case 'oblique':
				r += ';font-style:oblique'
				break
			case 'bold':
				r += ';font-weight:bold'
				break
			}
		}
		return r + p[p.length - 1]
	})
}
// install
// - front-end
	fix_out = abc2svg.page.img_out
	abc2svg.page.img_out = function(page, p) {
		fix_out(page, font_bug(p))
	}
// - back-end
    var	page_h, page_out,
	page_sty = ""

	// get the style from the image and remove it from the block
	function get_style(p) {
	    var	i,
		sty = p.match(/<style.*?>((.|\n)*?)<\/style>/)

		if (!sty)
			return p
		sty = sty[1].split('\n')
		if (!page_sty)
			page_sty = '<style type="text/css">\n</style>\n'
		for (i = 0; i < sty.length; i++) {
			if (page_sty.indexOf(sty[i]) < 0)
				page_sty = page_sty.replace('</style>\n',
						sty[i] + '\n</style>\n')
		}
		return p.replace(/<style(.|\n)*?\/style>\n/, '')
	} // get_style()

	// low level output
	abc.page.user_out = function(p) {
	    var	h, w,
		cfmt = abc.cfmt()

		switch (p.slice(0, 4)) {
		case "<div":			// new page
			page_h = 0
			page_out = ""
			break
		case "<svg":			// SVG image
			p = get_style(p)
			page_out += p
			    .replace(/<svg(.|\n)*?>/,
				'<g transform="translate(0, ' +
					page_h.toFixed(1) + ')">')
			    .replace('</svg>', '</g>\n')
			page_h += Number(p.match(/height="(\d+)px"/)[1])
			break
		case "</di":			// end of page
			w = cfmt.pagewidth
			h = cfmt.pageheight
			abc2svg.print('\
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
 xmlns:xlink="http://www.w3.org/1999/xlink"\n\
 color="black" class="music" stroke-width=".7"\n\
 width="' +
				(w * .75).toFixed(0) + 'px" height="' +
				(h * .75).toFixed(0) + 'px" viewBox="0 0 ' +
					w.toFixed(0) + ' ' +
					h.toFixed(0) + '"' +
				(cfmt.bgcolor ?
					(' style="background-color: ' +
						cfmt.bgcolor + '"') : '') +
				'>\n' +
				page_sty +
				page_out + "</svg>")
			break
		}
	} // user_out()
// for tests
abc2svg.abort = function(e) {
abc2svg.printErr('abort: '+e.message+'\n'+e.stack)
abc2svg.quit()
}
%%endjs
EOF

# purge /tmp
rm -f /tmp/abc*.svg

# generate a (XHTML+SVG) file with a abc2svg batch script
# then, extract the SVG images (pages) to /tmp
n=0
command $abcscr /tmp/fix.abc "$@" | while read v; do
	case "$v" in
	"<svg"*)
		n=$(($n+1))
		fn="/tmp/abc$n.svg"
		echo $v > $fn
		;;
	"</svg"*)
		echo $v >> $fn
		fn=
		;;
	"</body"*)
		break;;
	*)
		if [ "X$fn" != "X" ]; then
			echo $v >> $fn
		fi
		;;
	esac
done

# convert the SVG images to PDF
if [ -f /tmp/abc1.svg ]; then
 rsvg-convert -f pdf $(ls -rt /tmp/abc*.svg) -o $out
else
 echo 'Errors during the generation'
fi

# cleanup
rm -f /tmp/abc*.svg /tmp/fix.abc
Changes to build.
︙
12
13
14
15
16
17
18

19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36

37

38
39
40
41
42

43


44
45
46
47
48
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
52
53







+







-
+










+

+




-
+

+
+





 core/svg.js core/tune.js core/lyrics.js core/gchord.js core/tail.js\
 core/modules.js >> $out
 echo "abc2svg.version=\"$VERSION\";abc2svg.vdate=\"$DATE\"" >> $out

cp modules/ambitus.js ambitus-$MAJOR.js
cp modules/break.js break-$MAJOR.js
cp modules/capo.js capo-$MAJOR.js
cat util/midi.js modules/clair.js >> clair-$MAJOR.js
cp modules/clip.js clip-$MAJOR.js
cp modules/combine.js combine-$MAJOR.js
cp modules/diag.js diag-$MAJOR.js
cp modules/equalbars.js equalbars-$MAJOR.js
cp modules/grid.js grid-$MAJOR.js
cp modules/grid2.js grid2-$MAJOR.js
cp modules/MIDI.js MIDI-$MAJOR.js
cp modules/page.js page-$MAJOR.js
cat modules/page.js modules/strftime.js > page-$MAJOR.js
cp modules/perc.js perc-$MAJOR.js
cat modules/wps.js modules/psvg.js > psvg-$MAJOR.js
cp modules/soloffs.js soloffs-$MAJOR.js
cp modules/sth.js sth-$MAJOR.js
cp modules/temper.js temper-$MAJOR.js

cp editor/edit.css edit-$MAJOR.css

cp util/abcdoc.js abcdoc-$MAJOR.js
cp util/abcemb.js abcemb-$MAJOR.js
cp util/abcemb1.js abcemb1-$MAJOR.js
cp util/abcemb2.js abcemb2-$MAJOR.js
cp util/abcweb2.js abcweb2-$MAJOR.js
cp util/follow.js follow-$MAJOR.js
cp util/json.js json-$MAJOR.js
cp util/midi.js setmidi-$MAJOR.js
cp util/toaudio.js toaudio-$MAJOR.js
cat util/play.js util/toaudio.js util/toaudio5.js\
cat util/play.js util/toaudio.js util/toaudio5.js util/midi.js\
 util/sf2-parser.js util/tomidi5.js > play-$MAJOR.js
cat util/play.js util/sndgen.js util/sndaud.js util/midi.js\
 util/sf2-parser.js util/sndmid.js > snd-$MAJOR.js

sed "s+@MAJOR@+$MAJOR+" editor/edit.xhtml > edit-$MAJOR.xhtml
sed "s+@MAJOR@+$MAJOR+" editor/abckbd.js > abckbd-$MAJOR.js
sed "s+@MAJOR@+$MAJOR+" editor/abckbd2.js > abckbd2-$MAJOR.js
sed "s+@MAJOR@+$MAJOR+" editor/edit.js > edit-$MAJOR.js
Changes to build.ninja.
1
2
3

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21


22
23

24
25
26
27

28
29
30
31
32
33
34
35
36
37
38
39



40

41
42
43
44
45
46
47
48



49

50
51
52
53
54
55



56

57
58
59
60
61
62
63
1
2

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


20
21
22

23
24
25
26

27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74


-
+
















-
-
+
+

-
+



-
+
-











+
+
+

+








+
+
+

+






+
+
+

+







# rules for ninja (ninja-build)

# 'uglifyjs' may be disabled running
# minifying with 'jsmin' or 'uglifyjs' may be disabled by running
#	NOMIN=1 ninja -v

MAJOR=1

# ninja tool
tool=samu

rule cp
  command = cp $in $out

rule cpm
  command = sed "s+@MAJOR@+$MAJOR+" $in > $out

rule font
  command = fontforge -c 'open("font/abc2svg.sfd").generate("abc2svg.ttf")';$
   fontforge -c 'open("font/abc2svg.sfd").generate("abc2svg.woff")';$
   echo "// abc2svg music font" > font.js;$
   echo "var musicfont = 'url(\"data:application/x-font-ttf;base64,\\" >> font.js;$
   echo '// abc2svg music font' > font.js;$
   echo "var musicfont = 'url(\"data:application/octet-stream;base64,\\" >> font.js;$
   base64 abc2svg.ttf | sed 's/$$/\\/' >> font.js;$
   echo "\")'" >> font.js
   echo "\") format(\"truetype\")'" >> font.js

rule jsc
  command = VERSION=`grep '"version"' package.json|cut -d'"' -f4`;$
   type fossil &> /dev/null;$
   if command -v fossil >/dev/null 2>&1; then $
   if [ $$? -eq 0 ]; then $
	c=`fossil info | grep checkout:`;$
	t=`fossil info | grep tags: | awk '{print $$NF}'`;$
	if [ "$${t#v}" != "$$VERSION" ]; then $
		VERSION="$${VERSION}-$$(echo $$c | cut -d' ' -f2 | cut -c1-10)";$
	fi;$
	DATE=`echo $$c | cut -d' ' -f3`;$
   else $
	DATE=`date '+%Y-%m-%d' -r package.json`;$
   fi;$
   cp core/license $out;$
   if [ "x$$NOMIN" = "x" ]; then $
      if command -v jsmin >/dev/null 2>&1; then $
	cat $in | jsmin >> $out;$
      else $
	cat $in | uglifyjs >> $out;$
     fi;$
   else $
	cat $in >> $out;$
   fi;$
   echo "abc2svg.version=\"$$VERSION\";abc2svg.vdate=\"$$DATE\"" >> $out

rule jscu
  command = cp core/license $out;$
   if [ "x$$NOMIN" = "x" ]; then $
      if command -v jsmin >/dev/null 2>&1; then $
	cat $in | jsmin >> $out;$
      else $
	uglifyjs $in >> $out;$
      fi;$
   else $
	cat $in >> $out;$
   fi

rule edit
  command = if [ "x$$NOMIN" = "x" ]; then $
      if command -v jsmin >/dev/null 2>&1; then $
	sed "s+@MAJOR@+$MAJOR+" $in | jsmin > $out;$
      else $
	sed "s+@MAJOR@+$MAJOR+" $in | uglifyjs > $out;$
      fi;$
   else $
	sed "s+@MAJOR@+$MAJOR+" $in > $out;$
   fi

# generate all Scc1t2/n.js from sf2-Scc1t2/n.sf2
rule sf
  command = for f in sf2-Scc1t2/*.sf2; do $
︙
75
76
77
78
79
80
81




82
83
84
85

86
87
88
89
90
91
92
93

94
95
96
97
98
99
100
101
102
103

104

105
106
107
108
109


110

111
112
113
114
115
116
117
118
119
120





121
122
123
124

125
126
127
128
129
130
131
132
133
134
135
136
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

130
131
132
133
134
135
136
137



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159







+
+
+
+




+







-
+










+

+





+
+
-
+







-
-
-
+
+
+
+
+




+












build font: font | font/abc2svg.sfd
build sf: sf | Scc1t2/

build abc2svg-$MAJOR.js: jsc core/abc2svg.js core/deco.js core/draw.js $
 font.js core/format.js core/front.js core/music.js core/parse.js core/subs.js $
 core/svg.js core/tune.js core/lyrics.js core/gchord.js core/tail.js $
 core/modules.js | package.json
build mei2svg-$MAJOR.js: jsc core/abc2svg.js core/deco.js core/draw.js $
 font.js core/format.js core/front.js core/music.js core/parse.js core/subs.js $
 core/svg.js core/tune.js core/lyrics.js core/gchord.js core/mei.js core/tail.js $
 core/modules.js | package.json

build ambitus-$MAJOR.js: jscu modules/ambitus.js
build break-$MAJOR.js: jscu modules/break.js
build capo-$MAJOR.js: jscu modules/capo.js
build clair-$MAJOR.js: jscu util/midi.js modules/clair.js
build clip-$MAJOR.js: jscu modules/clip.js
build combine-$MAJOR.js: jscu modules/combine.js
build diag-$MAJOR.js: jscu modules/diag.js
build equalbars-$MAJOR.js: jscu modules/equalbars.js
build grid-$MAJOR.js: jscu modules/grid.js
build grid2-$MAJOR.js: jscu modules/grid2.js
build MIDI-$MAJOR.js: jscu modules/MIDI.js
build page-$MAJOR.js: jscu modules/page.js
build page-$MAJOR.js: jscu modules/page.js modules/strftime.js
build perc-$MAJOR.js: jscu modules/perc.js
build psvg-$MAJOR.js: jscu modules/wps.js modules/psvg.js
build soloffs-$MAJOR.js: jscu modules/soloffs.js
build sth-$MAJOR.js: jscu modules/sth.js
build temper-$MAJOR.js: jscu modules/temper.js

build edit-$MAJOR.css: cp editor/edit.css

build abcdoc-$MAJOR.js: jscu util/abcdoc.js
build abcemb-$MAJOR.js: jscu util/abcemb.js
build abcemb1-$MAJOR.js: jscu util/abcemb1.js
build abcemb2-$MAJOR.js: jscu util/abcemb2.js
build abcweb2-$MAJOR.js: jscu util/abcweb2.js
build follow-$MAJOR.js: jscu util/follow.js
build json-$MAJOR.js: jscu util/json.js
build setmidi-$MAJOR.js: jscu util/midi.js
build toaudio-$MAJOR.js: jscu util/toaudio.js
build play-$MAJOR.js: jscu util/play.js util/toaudio.js $
 util/toaudio5.js util/midi.js util/sf2-parser.js util/tomidi5.js
build snd-$MAJOR.js: jscu util/play.js util/sndgen.js $
 util/toaudio5.js util/sf2-parser.js util/tomidi5.js
 util/sndaud.js util/midi.js util/sf2-parser.js util/sndmid.js

build edit-$MAJOR.xhtml: cpm editor/edit.xhtml
build abckbd-$MAJOR.js: edit editor/abckbd.js
build abckbd2-$MAJOR.js: edit editor/abckbd2.js
build edit-$MAJOR.js: edit editor/edit.js

build all: phony abc2svg-$MAJOR.js abcdoc-$MAJOR.js $
 abcemb-$MAJOR.js abcemb2-$MAJOR.js json-$MAJOR.js setmidi-$MAJOR.js $
 toaudio-$MAJOR.js play-$MAJOR.js psvg-$MAJOR.js $
 ambitus-$MAJOR.js break-$MAJOR.js capo-$MAJOR.js clip-$MAJOR.js $
 abcemb-$MAJOR.js abcemb1-$MAJOR.js abcemb2-$MAJOR.js $
 abcweb2-$MAJOR.js $
 json-$MAJOR.js $
 setmidi-$MAJOR.js toaudio-$MAJOR.js snd-$MAJOR.js play-$MAJOR.js psvg-$MAJOR.js $
 ambitus-$MAJOR.js break-$MAJOR.js capo-$MAJOR.js clair-$MAJOR.js clip-$MAJOR.js $
 combine-$MAJOR.js diag-$MAJOR.js equalbars-$MAJOR.js grid-$MAJOR.js grid2-$MAJOR.js $
 MIDI-$MAJOR.js page-$MAJOR.js perc-$MAJOR.js soloffs-$MAJOR.js sth-$MAJOR.js $
 temper-$MAJOR.js edit-$MAJOR.css edit-$MAJOR.js edit-$MAJOR.xhtml $
 abckbd-$MAJOR.js abckbd2-$MAJOR.js follow-$MAJOR.js
# not mei2svg-$MAJOR.js

default all

# npm/fossil new release
rule version
  command = npm version $out;$
	tag=`ag '"version"' package.json|cut -d'"' -f4`;$
	fossil commit -m "New release v$$tag" -tag v$$tag;$
	$tool -v;$
	npm publish
build minor: version
build patch: version
Changes to cmdline.js.
︙
34
35
36
37
38
39
40
41

42
43
44



45


46
47
48
49



50
51
52
53
54
55
56
57
58












59
60
61







62

63
64
65
66
67
68
69
70
71
72
73
74
75
76



77
78
79
80
81

82
83
84
85
86
87
88
89
90
34
35
36
37
38
39
40

41
42
43
44
45
46
47

48
49
50



51
52
53









54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

98


99
100
101
102
103
104
105







-
+



+
+
+
-
+
+

-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
-
+














+
+
+




-
+
-
-








	// print or store the error messages
	if (typeof abc2svg.printErr == 'function')
		user.errmsg = function(msg, l, c) { abc2svg.printErr(msg) }
	else
		user.errmsg = function(msg, l, c) { user.errtxt += msg + '\n' }

var	abc = new abc2svg.Abc(user)		// (global for 'toxxx.js')
var	abc				// (global for 'toxxx.js')

// treat a file
function do_file(fn) {
    var	ext, file

	try {
	var	file = user.read_file(fn)
		file = user.read_file(fn)
	} catch (e) {

	if (!file) {
		j = fn.lastIndexOf("/")
		if (j < 0)
		// get the file extension
		ext = fn.slice((fn.lastIndexOf('.') - 1 >>> 0) + 2)
		if (!ext) {
			j = 0;
		i = fn.indexOf(".", j)
		if (i < 0) {
			fn += ".abc";
			file = user.read_file(fn)
		}
	}
	if (!file)
		abort(new Error("Cannot read file '" + fn + "'"))
			fn += ".abc"
			try {
				file = user.read_file(fn)
			} catch (e) {
			}
		}
	}
	if (!file && fn != "default.abc") {
//		abc2svg.abort(new Error("Cannot read file '" + fn + "'"))
		user.errmsg("Cannot read file '" + fn + "'")
		return
	}
//	if (typeof(utf_convert) == "function")
//		file = utf_convert(file)

	if (fn.slice(-4) == ".mei") {
		if (!abc.mei2mus)
			abc2svg.abort(new Error("No MEI support"));
		abc.mei2mus(file)
		return
	}

	// load the required modules
	// load the required modules (synchronous)
	abc2svg.modules.load(file)

	// generate
	try {
		abc.tosvg(fn, file)
	} catch (e) {
		abc2svg.abort(e)
	}
} // do_file()

function abc_cmd(cmd, args) {
	var	arg, parm, fn;

	// initialize the backend
	abc = new abc2svg.Abc(user)
	if (typeof global == "object" && !global.abc)
		global.abc = abc
	abc2svg.abc_init(args)

	// load 'default.abc'
	try {
		arg = user.read_file('default.abc');
		do_file("default.abc")
		abc2svg.modules.load(arg)
		abc.tosvg(cmd, arg)
	} catch (e) {
	}

	while (1) {
		arg = args.shift()
		if (!arg)
			break
︙
107
108
109
110
111
112
113
114
115
116
117
122
123
124
125
126
127
128

129
130
131







-



		do_file(fn);

	abc2svg.abc_end()
}

// nodejs
if (typeof module == 'object' && typeof exports == 'object') {
	exports.abc = abc;
	exports.user = user;
	exports.abc_cmd = abc_cmd
}
Changes to core/abc2svg.js.
︙
13
14
15
16
17
18
19
20
21



22
23
24

25
26
27
28
29
30
31
13
14
15
16
17
18
19


20
21
22
23
24

25
26
27
28
29
30
31
32







-
-
+
+
+


-
+







// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

// start of the abc2svg object
var abc2svg = {
// define the abc2svg object is not yet done
if (typeof abc2svg == "undefined")
	var abc2svg = {};

// constants
    C: {
abc2svg.C = {
	BLEN: 1536,

	// symbol types
	BAR: 0,
	CLEF: 1,
	CUSTOS: 2,
	GRACE: 4,
︙
51
52
53
54
55
56
57
58

59
























































































60
61

62
63
64
65
66
67
68
52
53
54
55
56
57
58

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149

150
151
152
153
154
155
156
157







-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+








	// slur/tie types (3 + 1 bits)
	SL_ABOVE: 0x01,
	SL_BELOW: 0x02,
	SL_AUTO: 0x03,
	SL_HIDDEN: 0x04,
	SL_DOTTED: 0x08		// (modifier bit)
    },
    };

	// key table - index = number of accidentals + 7
abc2svg.keys = [
	new Int8Array([-1,-1,-1,-1,-1,-1,-1 ]),	// 7 flat signs
	new Int8Array([-1,-1,-1, 0,-1,-1,-1 ]),	// 6 flat signs
	new Int8Array([ 0,-1,-1, 0,-1,-1,-1 ]),	// 5 flat signs
	new Int8Array([ 0,-1,-1, 0, 0,-1,-1 ]),	// 4 flat signs
	new Int8Array([ 0, 0,-1, 0, 0,-1,-1 ]),	// 3 flat signs
	new Int8Array([ 0, 0,-1, 0, 0, 0,-1 ]),	// 2 flat signs
	new Int8Array([ 0, 0, 0, 0, 0, 0,-1 ]),	// 1 flat signs
	new Int8Array([ 0, 0, 0, 0, 0, 0, 0 ]),	// no accidental
	new Int8Array([ 0, 0, 0, 1, 0, 0, 0 ]),	// 1 sharp signs
	new Int8Array([ 1, 0, 0, 1, 0, 0, 0 ]),	// 2 sharp signs
	new Int8Array([ 1, 0, 0, 1, 1, 0, 0 ]),	// 3 sharp signs
	new Int8Array([ 1, 1, 0, 1, 1, 0, 0 ]),	// 4 sharp signs
	new Int8Array([ 1, 1, 0, 1, 1, 1, 0 ]),	// 5 sharp signs
	new Int8Array([ 1, 1, 1, 1, 1, 1, 0 ]),	// 6 sharp signs
	new Int8Array([ 1, 1, 1, 1, 1, 1, 1 ])	// 7 sharp signs
]

// base-40 representation of musical pitch
// (http://www.ccarh.org/publications/reprints/base40/)
abc2svg.p_b40 = new Int8Array(			// staff pitch to base-40
//		  C  D   E   F   G   A   B
		[ 2, 8, 14, 19, 25, 31, 37 ])
abc2svg.b40_p = new Int8Array(			// base-40 to staff pitch
//		       C		 D
		[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
//	      E		     F		       G
	2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4,
//	      A			B
	5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6 ])
abc2svg.b40_a = new Int8Array(			// base-40 to accidental
//		         C		     D
		[-2, -1, 0, 1, 2, 0, -2, -1, 0, 1, 2, 0,
//		E		 F		     G
	-2, -1, 0, 1, 2, -2, -1, 0, 1, 2, 0, -2, -1, 0, 1, 2, 0,
//		A		    B
	-2, -1, 0, 1, 2, 0, -2, -1, 0, 1, 2 ])
abc2svg.b40_m = new Int8Array(			// base-40 to midi
//			 C		   D
		[-2, -1, 0, 1, 2, 0, 0, 1, 2, 3, 4, 0,
//	      E		     F		       G
	2, 3, 4, 5, 6, 3, 4, 5, 6, 7, 0, 5, 6, 7, 8, 9, 0,
//	      A			    B
	7, 8, 9, 10, 11, 0, 9, 10, 11, 12, 13 ])
abc2svg.b40k =  new Int8Array(		// base-40 interval to possible transposition
//		        C		  D
		[36, 1, 2, 3, 8, 2, 2, 7, 8,13,14, 2,
//		    37
//	       E	      F		        G
	 8,13,14,19,20,13,14,19,20,25, 2,19,24,25,30,31, 2,
//					    20
//	       A		 B
	25,30,31,36,37, 2,31,36,37,38, 3 ])
//				    2
abc2svg.b40sf = new Int8Array(		// base-40 interval to key signature
//		        C		   D
		[-2,-7, 0, 7, 2, 88, 0,-5, 2,-3, 4, 88,
//	       E	      F		        G
	 2,-3, 4,-1, 6,-3, 4,-1, 6, 1,88,-1,-6, 1,-4, 3,88,
//	       A		 B
	 1,-4, 3,-2, 5,88, 3,-2, 5, 0, 7 ])
abc2svg.isb40 = new Int8Array(		// interval with sharp to base-40 interval
	[0, 1, 6,11,12,17,18,23,28,29,34,35])
abc2svg.ifb40 = new Int8Array(		// interval with flat to base-40 interval
	[0, 5, 6,11,12,17,22,23,28,29,34,39])

abc2svg.pab40 = function(p, a) {
	p += 19				// staff pitch from C-1
   var	b40 = ((p / 7) | 0) * 40 + abc2svg.p_b40[p % 7]
	if (a && a != 3)		// if some accidental, but not natural
		b40 += a
	return b40
} // pit2b40()
abc2svg.b40p = function(b) {
	return ((b / 40) | 0) * 7 + abc2svg.b40_p[b % 40] - 19
} // b40p()
abc2svg.b40a = function(b) {
	return abc2svg.b40_a[b % 40]
} // b40a()
abc2svg.b40m = function(b) {
	return ((b / 40) | 0) * 12 + abc2svg.b40_m[b % 40]
} // b40m()

// compare pitches
// This function is used to sort the note pitches
abc2svg.pitcmp = function(n1, n2) { return n1.pit - n2.pit }

// start of the Abc object
  Abc: function(user) {
abc2svg.Abc = function(user) {
	"use strict";

    // constants
    var	C = abc2svg.C;

	// mask some unsafe functions
    var	require = empty_function,
︙
90
91
92
93
94
95
96

97
98
99
100

101
102
103

104
105
106


107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

124

125

126
127
128
129
130
131
132

133
134
135
136
137
138
139
179
180
181
182
183
184
185
186
187
188
189

190
191
192
193
194
195
196

197
198
199
200
201
202
203
204
205
206
207
208
209


210
211
212
213
214

215
216
217
218
219
220
221
222
223

224
225
226
227
228
229
230
231







+



-
+



+


-
+
+











-
-




+
-
+

+






-
+







	IN = 96,		// resolution 96 PPI
	CM = 37.8,		// 1 inch = 2.54 centimeter
	YSTEP = 256		/* number of steps for y offsets */

// error texts
var errs = {
	bad_char: "Bad character '$1'",
	bad_grace: "Bad character in grace note sequence",
	bad_val: "Bad value in $1",
	bar_grace: "Cannot have a bar in grace notes",
	ignored: "$1: inside tune - ignored",
	misplaced: "Misplaced '$1' in %%staves",
	misplaced: "Misplaced '$1' in %%score",
	must_note: "!$1! must be on a note",
	must_note_rest: "!$1! must be on a note or a rest",
	nonote_vo: "No note in voice overlay",
	not_ascii: "Not an ASCII character",
	not_enough_n: 'Not enough notes/rests for %%repeat',
	not_enough_m: 'Not enough measures for %%repeat',
	not_ascii: "Not an ASCII character"
	not_enough_p: "Not enough parameters in %%map",
	not_in_tune: "Cannot have '$1' inside a tune"
}

    var	self = this,				// needed for modules
	glovar = {
		meter: {
			type: C.METER,		// meter in tune header
			wmeasure: 1,		// no M:
			a_meter: []		// default: none
		}
	},
	info = {},			// information fields
	mac = {},			// macros (m:)
	maci = new Int8Array(128),	// first letter of macros
	parse = {
		ctx: {},
		prefix: '%',
		state: 0,
		ottava: [],
		line: new scanBuf()
		line: new scanBuf
	},
	tunes = [],		// first time symbol and voice array per tune for playing
	psvg			// PostScript

// utilities
function clone(obj, lvl) {
	if (!obj)
		return obj
	var tmp = new obj.constructor()
	var tmp = new obj.constructor
	for (var k in obj)
	    if (obj.hasOwnProperty(k)) {
		if (lvl && typeof obj[k] == 'object')
			tmp[k] = clone(obj[k], lvl - 1)
		else
			tmp[k] = obj[k]
	    }
︙
177
178
179
180
181
182
183


184
185
186
187
188
189
190
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284







+
+







	}
	user.errmsg(h + txt, l, c)
}

function error(sev, s, msg, a1, a2, a3, a4) {
	var i, j, regex, tmp

	if (!sev && cfmt.quiet)
		return
	if (user.textrans) {
		tmp = user.textrans[msg]
		if (tmp)
			msg = tmp
	}
	if (arguments.length > 3)
		msg = msg.replace(/\$./g, function(a) {
︙
Changes to core/deco.js.
︙
23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46

47
48
49
50
51
52
53
23
24
25
26
27
28
29

30
31
32
33
34
35
36
37
38
39

40
41
42
43
44
45

46
47
48
49
50
51
52
53







-
+









-
+





-
+







// decorations - populate with standard decorations
var decos = {
	dot: "0 stc 5 1 1",
	tenuto: "0 emb 5 3 3",
	slide: "1 sld 3 7 0",
	arpeggio: "2 arp 12 10 0",
	roll: "3 roll 7 6 6",
	fermata: "3 hld 12 7 7",
	fermata: "3 hld 10 7 7",
	emphasis: "3 accent 7 4 4",
	lowermordent: "3 lmrd 10 5 5",
	coda: "3 coda 24 10 10",
	uppermordent: "3 umrd 10 5 5",
	segno: "3 sgno 22 8 8",
	trill: "3 trl 14 5 5",
	upbow: "3 upb 10 5 5",
	downbow: "3 dnb 9 5 5",
	gmark: "3 grm 6 5 5",
	wedge: "3 wedge 8 3 3",		// (staccatissimo or spiccato)
	wedge: "0 wedge 7 3 3",		// (staccatissimo or spiccato)
	turnx: "3 turnx 10 0 5",
	breath: "3 brth 0 1 20",
	longphrase: "3 lphr 0 1 1",
	mediumphrase: "3 mphr 0 1 1",
	shortphrase: "3 sphr 0 1 1",
	invertedfermata: "3 hld 12 7 7",
	invertedfermata: "3 hld 10 7 7",
	invertedturn: "3 turn 10 0 5",
	invertedturnx: "3 turnx 10 0 5",
	"0": "3 fng 8 3 3 0",
	"1": "3 fng 8 3 3 1",
	"2": "3 fng 8 3 3 2",
	"3": "3 fng 8 3 3 3",
	"4": "3 fng 8 3 3 4",
︙
82
83
84
85
86
87
88
89
90


91
92
93
94
95
96
97
82
83
84
85
86
87
88


89
90
91
92
93
94
95
96
97







-
-
+
+







	mp: "6 mp 18 6 16",
	p: "6 p 18 2 8",
	pp: "6 pp 18 5 14",
	ppp: "6 ppp 18 8 20",
	pppp: "6 pppp 18 10 25",
	pralltriller: "3 umrd 10 5 5",
	sfz: "6 sfz 18 4 10",
	ped: "4 ped 18 8 8",
	"ped-up": "4 pedoff 18 8 8",
	ped: "4 ped 18 6 10",
	"ped-up": "4 pedoff 12 4 4",
	"ped(": "4 lped 20 1 1",
	"ped)": "4 lped 20 1 1",
	"crescendo(": "6 cresc 18 0 0",
	"crescendo)": "6 cresc 18 0 0",
	"<(": "6 cresc 18 0 0",
	"<)": "6 cresc 18 0 0",
	"diminuendo(": "6 dim 18 0 0",
︙
334
335
336
337
338
339
340
341

342



343
344



345
346
347
348
349
350
351


352
353

354
355
356
357
358
359
360
361
362
334
335
336
337
338
339
340

341
342
343
344
345


346
347
348
349
350
351
352
353
354
355
356
357
358

359
360

361
362
363
364
365
366
367







-
+

+
+
+
-
-
+
+
+







+
+

-
+

-







//		de.y = s.y;
		return
	}
	if (s.multi)
		up = s.multi > 0
	else
		up = s.stem < 0
	if (up)
	if (up) {
		y = s.ymx | 0
	} else if (dd.name[0] == 'w') {
		de.inv = true
		y = s.ymn
	else
		y = (s.ymn - dd.h) | 0
	} else {
		y = s.ymn - dd.h
	}
	if (y > -6 && y < 24) {
		if (up)
			y += 3;
		y = (((y + 6) / 6) | 0) * 6 - 6		/* between lines */
	}
	if (up)
		s.ymx = y + dd.h
	else if (dd.name[0] == 'w')
		s.ymn = y - dd.h
	else
		s.ymn = y;
		s.ymn = y
	de.y = y
//	de.x = s.x + s.notes[s.stem >= 0 ? 0 : s.nhd].shhd
	if (s.type == C.NOTE)
		de.x += s.notes[s.stem >= 0 ? 0 : s.nhd].shhd
	if (dd.name[0] == 'd'			/* if dot decoration */
	 && s.nflags >= -1) {			/* on stem */
		if (up) {
			if (s.stem > 0)
				de.x += 3.5	// stem_xoff
︙
809
810
811
812
813
814
815
816

817
818

819
820
821
822
823
824
825
814
815
816
817
818
819
820

821
822

823
824
825
826
827
828
829
830







-
+

-
+







				error(1, s,
					"!$1! must be on the last of a couple of notes",
					dd.name)
				continue
			}
			s.trem2 = true;
			s.beam_end = true;
//			s.beam_st = false;
			s.beam_st = false;
			prev.beam_st = true;
//			prev.beam_end = false;
			prev.beam_end = false;
			s.ntrem = prev.ntrem = Number(dd.name[4]);
			for (j = 0; j <= s.nhd; j++)
				s.notes[j].dur *= 2;
			for (j = 0; j <= prev.nhd; j++)
				prev.notes[j].dur *= 2
			continue
		case 35:		/* xstem */
︙
1039
1040
1041
1042
1043
1044
1045
1046

1047
1048
1049
1050
1051
1052
1053
1054
1044
1045
1046
1047
1048
1049
1050

1051

1052
1053
1054
1055
1056
1057
1058







-
+
-







			 || stv_g.st < 0)	// or not staff scale
// || voice_tb[s.v].scale != 1)
				out_deco_val(x, y, f, de.val / stv_g.scale, de.defl)
			else
				out_deco_val(x, y, f, de.val, de.defl)
			if (de.defl.noen)
				new_de.push(de.start)	// to be continued next line
		} else if (dd.str != undefined
		} else if (dd.str != undefined) {
			&& dd.str != 'sfz') {
			str = dd.str
			if (str[0] == '@') {
				a = str.match(/^@([0-9.-]+),([0-9.-]+);?/);
				x += Number(a[1]);
				y += Number(a[2]);
				str = str.replace(a[0], "")
			}
︙
1204
1205
1206
1207
1208
1209
1210
1211

1212
1213
1214
1215
1216
1217
1218
1208
1209
1210
1211
1212
1213
1214

1215
1216
1217
1218
1219
1220
1221
1222







-
+







			} else if (dd.dd_st) {
				de.lden = true;
				de.defl.nost = true
			}
		}
	} // create_dh()

	// create all decoration of a note (chord and heads)
	// create all decorations of a note (chord and heads)
	function create_all(s) {
		var m

		if (s.a_dd)
			create_deco(s)
		if (s.notes) {
			for (m = 0; m < s.notes.length; m++) {
︙
1357
1358
1359
1360
1361
1362
1363
1364

1365
1366
1367
1368
1369
1370
1371
1361
1362
1363
1364
1365
1366
1367

1368
1369
1370
1371
1372
1373
1374
1375







-
+







//	- chord symbols
//	- repeat brackets
/* (the staves are not yet defined) */
/* (unscaled delayed output) */
function draw_deco_staff() {
	var	s, first_gchord, p_voice, x, y, w, i, v, de, dd,
		gch, gch2, ix, top, bot,
		minmax = new Array(nstaff),
		minmax = new Array(nstaff + 1),
		nd = a_de.length

	/* draw the repeat brackets */
	function draw_repbra(p_voice) {
		var s, s1, y, y2, i, p, w, wh, first_repeat;

		/* search the max y offset */
︙
1546
1547
1548
1549
1550
1551
1552
1553

1554
1555
1556
1557
1558
1559
1560
1550
1551
1552
1553
1554
1555
1556

1557
1558
1559
1560
1561
1562
1563
1564







-
+







			if (gch.type != 'g')
				continue
			gch2 = gch	// chord closest to the staff
			if (gch.y < 0)
				break
		}
		if (gch2) {
			w = gch2.w
			w = gch2.wh[0]
			if (gch2.y >= 0) {
				y = y_get(s.st, true, s.x, w)
				if (y > minmax[s.st].ymax)
					minmax[s.st].ymax = y
			} else {
				y = y_get(s.st, false, s.x, w)
				if (y < minmax[s.st].ymin)
︙
1622
1623
1624
1625
1626
1627
1628
1629






1630

1631
1632
1633
1634
1635
1636
1637
1638
1639
1640



1641
1642
1643
1644
1645
1646
1647
1626
1627
1628
1629
1630
1631
1632

1633
1634
1635
1636
1637
1638
1639
1640
1641
1642


1643
1644
1645



1646
1647
1648
1649
1650
1651
1652
1653
1654
1655







-
+
+
+
+
+
+

+


-
-



-
-
-
+
+
+







			y = y_get(st, true, 0, 20)
			if (y < staff_tb[st].topbar + 14)
				y = staff_tb[st].topbar + 14;
			xy_str(0, y, bar_num.toString());
			y_set(st, true, 0, 20, y + gene.curfont.size + 2)
		} else if (bar_num % cfmt.measurenb == 0) {
			for ( ; ; s = s.ts_next) {
				if (!s.dur)
				switch (s.type) {
				case C.TIMESIG:
				case C.CLEF:
				case C.KEYSIG:
				case C.FMTCHG:
				case C.STBRK:
					continue
				}
				break
			}
			while (s.st != st)
				s = s.ts_next

			// don't display the number twice
		     if (s.type != C.BAR || !s.bar_num) {
//			if (s.prev && s.prev.type != C.CLEF)
//				s = s.prev;
			x = s.x - s.wl;
			if (s.prev)
				s = s.prev;
			x = s.x + s.wr;
			any_nb = true;
			w = w0
			if (bar_num >= 10)
				w *= bar_num >= 100 ? 3 : 2
			if (gene.curfont.box)
				w += 4;
			y = y_get(st, true, x, w)
︙
1669
1670
1671
1672
1673
1674
1675
1676

1677
1678
1679
1680
1681
1682
1683
1677
1678
1679
1680
1681
1682
1683

1684
1685
1686
1687
1688
1689
1690
1691







-
+







					break
			}
			set_dscale(st)
			continue
		default:
			continue
		case C.BAR:
			if (!s.bar_num)
			if (!s.bar_num || s.bar_num <= 1)
				continue
			break
		}

		bar_num = s.bar_num
		if (cfmt.measurenb == 0
		 || (bar_num % cfmt.measurenb) != 0
︙
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863

1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877

1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897

1898
1899
1900
1901
1902
1903
1904
1724
1725
1726
1727
1728
1729
1730


























































































































1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748

1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762

1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782

1783
1784
1785
1786
1787
1788
1789
1790







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-


















-
+













-
+



















-
+







	}
	gene.nbar = bar_num

	if (font_size)
		param_set_font("measurefont", "* " + font_size.toString());
}

/* -- draw the note of the tempo -- */
function draw_notempo(s, x, y, dur, sc) {
	var	dx, p, dotx,
		elts = identify_note(s, dur),
		head = elts[0],
		dots = elts[1],
		nflags = elts[2]

//useless
//	// protection against end of container
//	if (stv_g.started) {
//		output += "</g>\n";
//		stv_g.started = false
//	}

	out_XYAB('<g transform="translate(X,Y) scale(F)">\n',
		x + 4, y + 4, sc)
	switch (head) {
	case C.OVAL:
		p = "HD"
		break
	case C.EMPTY:
		p = "Hd"
		break
	default:
		p = "hd"
		break
	}
	xygl(-posx, posy, p);
	dx = 4
	if (dots) {
		dotx = 9
		if (nflags > 0)
			dotx += 4
		switch (head) {
		case C.SQUARE:
			dotx += 3
			break
		case C.OVALBARS:
		case C.OVAL:
			dotx += 2
			break
		case C.EMPTY:
			dotx += 1
			break
		}
		dx = dotx * dots;
		dotx -= posx
		while (--dots >= 0) {
			xygl(dotx, posy, "dot");
			dotx += 3.5
		}
	}
	if (dur < C.BLEN) {
		if (nflags <= 0) {
			out_stem(-posx, posy, 21)		// stem height
		} else {
			out_stem(-posx, posy, 21, false, nflags)
			if (dx < 6)
				dx = 6
		}
	}
	output += '</g>\n'
	return (dx + 15) * sc
}

/* -- estimate the tempo width -- */
function tempo_width(s) {
	var	w = 0;

	if (s.tempo_wh1)
		w = s.tempo_wh1[0]
	if (s.tempo_wh0)
		w += s.tempo_wh0[0]
	if (s.tempo_wh2)
		w += s.tempo_wh2[0]
	return w
}

/* - output a tempo --*/
function write_tempo(s, x, y) {
	var	j, dx, bx, bh,
		sc = .6 * gene.curfont.size / 15.0; //fixme: 15.0 = initial tempofont

	set_font("tempo")
	if (gene.curfont.box) {
		gene.curfont.box = false
		bx = x
	}
	if (s.tempo_str1) {
		xy_str(x, y, s.tempo_str1, null, null, s.tempo_wh1);
		x += s.tempo_wh1[0] + 3
	}
	if (s.tempo_notes) {
		for (j = 0; j < s.tempo_notes.length; j++)
			x += draw_notempo(s, x, y, s.tempo_notes[j], sc);
		xy_str(x, y, s.tempo_str0, null, null, s.tempo_wh0);
		x += s.tempo_wh0[0]
		if (s.tempo)
			x += 5
		else
			x += draw_notempo(s, x, y, s.new_beat, sc)
	}
	if (s.tempo_str2)
		xy_str(x, y, s.tempo_str2, null, null, s.tempo_wh2)

	if (bx) {
		gene.curfont.box = true
		if (s.tempo_str2)
			x += s.tempo_wh2[0] + 3;
		bh = gene.curfont.size + 4;
		output += '<rect class="stroke" x="';
		out_sxsy(bx - 2, '" y="', y + bh - 1);
		output += '" width="' + (x - bx + 2).toFixed(1) +
			'" height="' + bh.toFixed(1) +
			'"/>\n'
	}

	// don't display anymore
	s.del = true
}

/* -- draw the parts and the tempo information -- */
/* (the staves are being defined) */
function draw_partempo(st, top) {
	var	s, some_part, some_tempo, h, w, y,
		dy = 0,		/* put the tempo indication at top */
		ht = 0

	/* get the minimal y offset */
	var	ymin = staff_tb[st].topbar + 8,
		dosh = 0,
		shift = 1,
		x = -100	// (must be negative for %%soloffs)

	for (s = tsfirst; s; s = s.ts_next) {
		if (s.type != C.TEMPO || s.del)
			continue
		if (!some_tempo)
			some_tempo = s;
		w = tempo_width(s);
		w = s.tempo_wh[0]
		if (s.time == 0 && s.x > 40)	// at start of tune and no %%soloffs,
			s.x = 40;	// shift the tempo over the key signature
		y = y_get(st, true, s.x - 16, w)
		if (y > ymin)
			ymin = y
		if (x >= s.x - 16 && !(dosh & (shift >> 1)))
			dosh |= shift;
		shift <<= 1;
		x = s.x - 16 + w
	}
	if (some_tempo) {
		set_sscale(-1);
		set_font("tempo");
		ht = gene.curfont.size + 2;
		ht = gene.curfont.size + 8;
		y = 2 - ht;
		h = y - ht
		if (dosh != 0)
			ht *= 2
		if (top < ymin + ht)
			dy = ymin + ht - top

		/* draw the tempo indications */
		for (s = some_tempo; s; s = s.ts_next) {
			if (s.type != C.TEMPO
			 || s.del)		// (displayed by %%titleformat)
				continue
			if (user.anno_start || user.anno_stop) {
				s.wl = 16;
				s.wr = 30;
				s.ymn = (dosh & 1) ? h : y;
				s.ymx = s.ymn + 14;
				anno_start(s)
			}
			write_tempo(s, s.x - 16, (dosh & 1) ? h : y);
			writempo(s, s.x - 16, (dosh & 1) ? h : y);
			anno_stop(s);
			dosh >>= 1
		}
	}

	/* then, put the parts */
/*fixme: should reduce vertical space if parts don't overlap tempo...*/
︙
Changes to core/draw.js.
︙
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
25
26
27
28
29
30
31

32
33
34
35
36
37
38







-







	STEM_CH_MIN	= 14,	/* min stem height for chords under beams */
	STEM_CH_MIN2	= 10,	/* ... for notes with two beams */
	STEM_CH_MIN3	= 9,	/* ... for notes with three beams */
	STEM_CH_MIN4	= 9,	/* ... for notes with four beams */
	BEAM_DEPTH	= 3.2,	/* width of a beam stroke */
	BEAM_OFFSET	= .25,	/* pos of flat beam relative to staff line */
	BEAM_SHIFT	= 5,	/* shift of second and third beams */
	BEAM_SLOPE	= .4,	/* max slope of a beam */
	BEAM_STUB	= 8,	/* length of stub for flag under beam */ 
	SLUR_SLOPE	= .5,	/* max slope of a slur */
	GSTEM		= 15,	/* grace note stem length */
	GSTEM_XOFF	= 2.3	/* x offset for grace note stem */

    var cache

︙
63
64
65
66
67
68
69
70
71


72
73

74

75
76
77
78
79

80
81

82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
122
62
63
64
65
66
67
68


69
70

71
72
73
74
75
76
77
78

79
80

81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113

114

115
116
117
118
119
120
121







-
-
+
+
-

+

+




-
+

-
+















-
+
















-
+
-








	d1 = rnd6(top - BEAM_OFFSET);
	d2 = rnd6(bot + BEAM_OFFSET)
	return d1 * d1 > d2 * d2 ? d2 : d1
}

/* duplicate a note for beaming continuation */
function sym_dup(s_orig) {
	var	m, note,
function sym_dup(s) {
    var	m, note
		s = clone(s_orig);

	s = clone(s)
	s.invis = true
	delete s.extra;
	delete s.text
	delete s.a_gch
	delete s.a_ly
	delete s.a_dd;
	s.notes = clone(s_orig.notes)
	s.notes = clone(s.notes)
	for (m = 0; m <= s.nhd; m++) {
		note = s.notes[m] = clone(s_orig.notes[m])
		note = s.notes[m] = clone(s.notes[m])
		delete note.a_dcn
	}
	return s
}

/* -- calculate a beam -- */
/* (the staves may be defined or not) */
var min_tb = [
	[STEM_MIN, STEM_MIN,
		STEM_MIN2, STEM_MIN3, STEM_MIN4, STEM_MIN4],
	[STEM_CH_MIN, STEM_CH_MIN,
		STEM_CH_MIN2, STEM_CH_MIN3, STEM_CH_MIN4, STEM_CH_MIN4]
]

// (possible hook)
function calculate_beam(bm, s1) {
Abc.prototype.calculate_beam = function(bm, s1) {
    var	s, s2, g, notes, nflags, st, v, two_staves, two_dir,
		x, y, ys, a, b, stem_err, max_stem_err,
		p_min, p_max, s_closest,
		stem_xoff, scale,
		visible, dy

	if (!s1.beam_st) {	/* beam from previous music line */
		s = sym_dup(s1);
		lkvsym(s, s1);
		lktsym(s, s1);
		s.x -= 12
		if (s.x > s1.prev.x + 12)
			s.x = s1.prev.x + 12;
		s.beam_st = true
		delete s.beam_end;
		s.tmp = true
		delete s.slur_start
		delete s.sls;
		delete s.slur_end;
		s1 = s
	}

	/* search last note in beam */
	notes = nflags = 0;	/* set x positions, count notes and flags */
	two_staves = two_dir = false;
	st = s1.st;
︙
152
153
154
155
156
157
158
159

160
161
162
163
164
165
166
167
151
152
153
154
155
156
157

158

159
160
161
162
163
164
165







-
+
-







			if (s.ts_next)
				s.ts_next.ts_prev = s;
			s2.ts_next = s;
			s.ts_prev = s2
			delete s.beam_st;
			s.beam_end = true;
			s.tmp = true
			delete s.slur_start
			delete s.sls;
			delete s.slur_end
			s.x += 12
			if (s.x < realwidth - 12)
				s.x = realwidth - 12;
			s2 = s;
			notes++
			break
		}
︙
209
210
211
212
213
214
215
216
217


218
219
220
221
222
223
224
225
226
227
228
229
230
231
232



233
234
235
236
237
238
239
240
241
242
207
208
209
210
211
212
213


214
215
216
217
218
219
220
221
222
223
224
225
226
227



228
229
230



231
232
233
234
235
236
237







-
-
+
+












-
-
-
+
+
+
-
-
-







			}
		}
		s.xs = s.x + x * scale;
		if (s == s2)
			break
	}

	// have flat beams when asked
	if (cfmt.flatbeams)
	// have flat beams on grace notes when asked
	if (s.grace && cfmt.flatbeams)
		a = 0

	// if a note inside the beam is the closest to the beam, the beam is flat
	else if (!two_dir
	      && notes >= 3
	      && s_closest != s1 && s_closest != s2)
		a = 0

	y = s1.ys + staff_tb[st].y
	if (a == undefined)
		a = (s2.ys + staff_tb[s2.st].y - y) / (s2.xs - s1.xs)

	if (a != 0) {
		if (a > 0)
			a = BEAM_SLOPE * a / (BEAM_SLOPE + a) // max steepness for beam
	if (a != 0)
		a = cfmt.beamslope * a /
			(cfmt.beamslope + Math.abs(a)) // max steepness for beam
		else
			a = BEAM_SLOPE * a / (BEAM_SLOPE - a);
	}

	// pivot around the middle of the beam
	b = (y + s2.ys + staff_tb[s2.st].y) / 2 - a * (s2.xs + s1.xs) / 2

/*fixme: have a look again*/
	/* have room for the symbols in the staff */
	max_stem_err = 0;		/* check stem lengths */
︙
738
739
740
741
742
743
744
745

746

747
748
749
750



















751
752
753
754


755
756
757

758

759
760


761
762

763
764
765
766
767


768
769

770
771
772
773
774
775
776
777
778
779
780














781
782
783

784
785
786
787
788
789
790
791
792


793
794
795
796
797
798
799
733
734
735
736
737
738
739

740
741
742




743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763


764
765



766
767
768


769
770


771





772
773


774





775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805


806
807
808
809
810
811
812
813
814







-
+

+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
+
+
-
-
-
+

+
-
-
+
+
-
-
+
-
-
-
-
-
+
+
-
-
+
-
-
-
-
-






+
+
+
+
+
+
+
+
+
+
+
+
+
+



+







-
-
+
+







			xygl(x, y, "acc" + acc + '_' + micro_n + '_' + micro_d)
			return
		}
	}
	xygl(x, y, "acc" + acc)
}

// draw helper lines between yl and yu
// draw helper lines
//fixme: double lines when needed for different voices
// (possible hook)
function draw_hl(x, yl, yu, st, hltype) {
    var	i, j,
	p_staff = staff_tb[st],
	staffb = p_staff.y,
Abc.prototype.draw_hl = function(x, s, hltype) {
    var	i, j, n,
	hla = [],
	st = s.st,
	p_staff = staff_tb[st]

	// check if any helper line
	if (!p_staff.hll)
		return			// no helper line (no line)
	for (i = 0; i <= s.nhd; i++) {
		if (!p_staff.hlmap[s.notes[i].pit - p_staff.hll])
			hla.push((s.notes[i].pit - 18) * 3)
	}
	n = hla.length
	if (!n)
		return			// no

	// handle the helper lines out of the staff
    var	staffb = p_staff.y,
	stafflines = p_staff.stafflines,
	top = (stafflines.length - 1) * 6,
	bot = p_staff.botline

	bot = p_staff.botline,
	yl = bot,
	// no helper if no line
	if (!/[\[|]/.test(stafflines))
		return
	yu = top

	for (i = 0; i < hla.length; i++) {
	if (yl % 6)
		yl += 3
		if (hla[i] < yl) {
			yl = (((hla[i] + 51) / 6) | 0) * 6 - 48;
	if (yu % 6)
		yu -= 3
			n--
	if (stafflines.indexOf('-') >= 0	// if forced helper lines ('-')
	 && ((yl > bot && yl < top) || (yu > bot && yu < top)
	  || (yl <= bot && yu >= top))) {
		i = yl;
		j = yu
		} else if (hla[i] > yu) {
			yu = ((hla[i] / 6) | 0) * 6;
		while (i > bot && stafflines[i / 6] == '-')
			i -= 6
			n--
		while (j < top && stafflines[j / 6] == '-')
			j += 6
		for ( ; i < j; i += 6) {
			if (stafflines[i / 6] == '-')
				xygl(x, staffb + i, hltype)	// hole
		}
	}
	for (; yl < bot; yl += 6)
		xygl(x, staffb + yl, hltype)
	for (; yu > top; yu -= 6)
		xygl(x, staffb + yu, hltype)
	if (!n)
		return			// no more helper lines

	// draw the helper lines inside the staff
	i = yl;
	j = yu
	while (i > bot && stafflines[i / 6] == '-')
		i -= 6
	while (j < top && stafflines[j / 6] == '-')
		j += 6
	for ( ; i < j; i += 6) {
		if (stafflines[i / 6] == '-')
			xygl(x, staffb + i, hltype)	// hole
	}
}

/* -- draw a key signature -- */
// (possible hook)
var	sharp_cl = new Int8Array([24, 9, 15, 21, 6, 12, 18]),
	flat_cl = new Int8Array([12, 18, 24, 9, 15, 21, 6]),
	sharp1 = new Int8Array([-9, 12, -9, -9, 12, -9]),
	sharp2 = new Int8Array([12, -9, 12, -9, 12, -9]),
	flat1 = new Int8Array([9, -12, 9, -12, 9, -12]),
	flat2 = new Int8Array([-12, 9, -12, 9, -12, 9])

function draw_keysig(x, s) {
	if (s.k_none)
Abc.prototype.draw_keysig = function(x, s) {
	if (s.k_none || s.k_play)
		return
	var	old_sf = s.k_old_sf,
		st = s.st,
		staffb = staff_tb[st].y,
		i, shift, p_seq,
		clef_ix = s.k_y_clef

︙
874
875
876
877
878
879
880
881






882
883
884
885
886
887
888
889
890
891
892
893

894

895
896
897
898
899
900
901
889
890
891
892
893
894
895

896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914

915
916
917
918
919
920
921
922







-
+
+
+
+
+
+












+
-
+







			}
		}
	} else if (s.k_a_acc.length) {

		/* explicit accidentals */
		var	acc,
			last_acc = s.k_a_acc[0].acc,
			last_shift = 100
			last_shift = 100,
			s2 = {
				st: st,
				nhd: 0,
				notes: [{}]
			}

		for (i = 0; i < s.k_a_acc.length; i++) {
			acc = s.k_a_acc[i];
			shift = (s.k_y_clef	// clef shift
				+ acc.pit - 18) * 3
			if (i != 0
			 && (shift > last_shift + 18
			  || shift < last_shift - 18))
				x -= 5.5		// no clash
			else if (acc.acc != last_acc)
				x += 3;
			last_acc = acc.acc;
			s2.notes[0].pit = shift / 3 + 18;
			draw_hl(x, shift, shift, st, "hl");
			self.draw_hl(x, s2, "hl");
			last_shift = shift;
			draw_acc(x, staffb + shift,
				 acc.acc, acc.micro_n, acc.micro_d);
			x += 5.5
		}
	}
}
︙
924
925
926
927
928
929
930
931
932


933
934
935



936







937
938
939
940
941
942
943
945
946
947
948
949
950
951


952
953
954


955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972







-
-
+
+

-
-
+
+
+

+
+
+
+
+
+
+







/* (the staves are defined) */
var rest_tb = [
	"r128", "r64", "r32", "r16", "r8",
	"r4",
	"r2", "r1", "r0", "r00"]

function draw_rest(s) {
	var	s2, i, j, x, y, dotx, yb, yt, head, bx,
		p_staff = staff_tb[s.st]
    var	s2, i, j, x, y, yb, bx,
	p_staff = staff_tb[s.st]

	/* if rest alone in the measure or measure repeat, center */
	if (s.dur == s.p_v.meter.wmeasure
	// if rest alone in the measure or measure repeat,
	// change the head and center
	if (s.dur_orig == s.p_v.meter.wmeasure
	 || (s.rep_nb && s.rep_nb >= 0)) {
		if (s.dur < C.BLEN * 2)
			s.nflags = -2		// semibreve / whole
		else if (s.dur < C.BLEN * 4)
			s.nflags = -3
		else
			s.nflags = -4;
		s.dots = 0;

		/* don't use next/prev: there is no bar in voice overlay */
		s2 = s.ts_next
		while (s2 && s2.time != s.time + s.dur)
			s2 = s2.ts_next;
		x = s2 ? s2.x : realwidth;
		s2 = s
︙
986
987
988
989
990
991
992



993
994
995
996
997
998
999
1000
1001

1002
1003
1004
1005
1006
1007
1008
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032

1033
1034
1035
1036
1037
1038
1039
1040







+
+
+








-
+







		anno_stop(s)
		return
	}

	set_scale(s);
	anno_start(s);

	if (s.notes[0].color)
		set_color(s.notes[0].color);

	y = s.y;

	i = 5 - s.nflags		/* rest_tb index (5 = C_XFLAGS) */
	if (i == 7 && y == 12
	 && p_staff.stafflines.length <= 2)
		y -= 6				/* semibreve a bit lower */

	// draw the rest
	xygl(x, y + yb, s.notes[0].head ? s.notes[0].head : rest_tb[i])
	xygl(x, y + yb, s.notes[0].head || rest_tb[i])

	/* output ledger line(s) when greater than minim */
	if (i >= 6) {
		j = y / 6
		switch (i) {
		default:
			switch (p_staff.stafflines[j + 1]) {
︙
1037
1038
1039
1040
1041
1042
1043

1044
1045
1046
1047
1048
1049
1050
1051
1052



1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065


1066
1067
1068
1069
1070
1071
1072
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082



1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107







+






-
-
-
+
+
+













+
+







		x += 8;
		y += yb + 3
		for (i = 0; i < s.dots; i++) {
			xygl(x, y, "dot");
			x += 3.5
		}
	}
	set_color();
	anno_stop(s)
}

/* -- draw grace notes -- */
/* (the staves are defined) */
function draw_gracenotes(s) {
	var	yy, x0, y0, x1, y1, x2, y2, x3, y3, bet1, bet2,
		dy1, dy2, g, last, note,
		bm = {}
    var	yy, x0, y0, x1, y1, x2, y2, x3, y3, bet1, bet2, slur,
	dy1, dy2, g, last, note,
	bm = {}

	/* draw the notes */
//	bm.s2 = undefined			/* (draw flags) */
	for (g = s.extra; g; g = g.next) {
		if (g.beam_st && !g.beam_end) {
			if (self.calculate_beam(bm, g))
				draw_beams(bm)
		}
		anno_start(g);
		draw_note(g, !bm.s2)
		if (g == bm.s2)
			bm.s2 = null			/* (draw flags again) */
		anno_stop(g)
		if (g.sls || g.sl2)
			slur = true
		if (!g.next)
			break			/* (keep the last note) */
	}

	// if an acciaccatura, draw a bar 
	if (s.sappo) {
		g = s.extra
︙
1086
1087
1088
1089
1090
1091
1092
1093


1094
1095
1096
1097
1098


1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114

1115
1116
1117
1118
1119
1120
1121
1121
1122
1123
1124
1125
1126
1127

1128
1129
1130
1131
1132
1133

1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150

1151
1152
1153
1154
1155
1156
1157
1158







-
+
+




-
+
+















-
+







				x1, y1, g.stem > 0)
	}

	/* slur */
//fixme: have a full key symbol in voice
	if (s.p_v.key.k_bagpipe			/* no slur when bagpipe */
	 || !cfmt.graceslurs
	 || s.slur_start			/* explicit slur */
	 || slur				// explicit slur
	 || s.tie_s				// some tie
	 || !s.next
	 || s.next.type != C.NOTE)
		return
	last = g
	if (last.stem >= 0) {
	if (((g.stem >= 0 || s.multi < 0) && g.notes[0].pit <= 28)
	 || g.notes[0].pit < 16) {		// slur below
		yy = 127
		for (g = s.extra; g; g = g.next) {
			if (g.y < yy) {
				yy = g.y;
				last = g
			}
		}
		x0 = last.x;
		y0 = last.y - 5
		if (s.extra != last) {
			x0 -= 4;
			y0 += 1
		}
		s = s.next;
		x3 = s.x - 1
		if (s.stem < 0)
		if (s.stem < 0 && s.nflags > -2)
			x3 -= 4;
		y3 = 3 * (s.notes[0].pit - 18) - 5;
		dy1 = (x3 - x0) * .4
		if (dy1 > 3)
			dy1 = 3;
		dy2 = dy1;
		bet1 = .2;
︙
1129
1130
1131
1132
1133
1134
1135
1136

1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152

1153
1154
1155
1156
1157
1158
1159
1166
1167
1168
1169
1170
1171
1172

1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188

1189
1190
1191
1192
1193
1194
1195
1196







-
+















-
+







			dy2 = (y0 - y3) * .2;
			bet1 = 0
		} else if (y3 > y0 + 4) {
			y3 = y0 + 4;
			x0 = last.x + 2;
			y0 = last.y - 4
		}
	} else {
	} else {				// slur above
		yy = -127
		for (g = s.extra; g; g = g.next) {
			if (g.y > yy) {
				yy = g.y;
				last = g
			}
		}
		x0 = last.x;
		y0 = last.y + 5
		if (s.extra != last) {
			x0 -= 4;
			y0 -= 1
		}
		s = s.next;
		x3 = s.x - 1
		if (s.stem >= 0)
		if (s.stem >= 0 && s.nflags > -2)
			x3 -= 2;
		y3 = 3 * (s.notes[s.nhd].pit - 18) + 5;
		dy1 = (x0 - x3) * .4
		if (dy1 < -3)
			dy1 = -3;
		dy2 = dy1;
		bet1 = .2;
︙
1230
1231
1232
1233
1234
1235
1236
1237

1238
1239
1240
1241
1242
1243
1244
1267
1268
1269
1270
1271
1272
1273

1274
1275
1276
1277
1278
1279
1280
1281







-
+







	return staff_tb[s.st].y + 3 * (note.pit - 18)
}

/* -- draw m-th head with accidentals and dots -- */
/* (the staves are defined) */
// sets {x,y}_note
function draw_basic_note(x, s, m, y_tb) {
	var	i, k, p, yy, dotx, doty, inv,
	var	i, p, yy, dotx, doty, inv,
		old_color = false,
		note = s.notes[m],
		staffb = staff_tb[s.st].y,	/* bottom of staff */
		y = 3 * (note.pit - 18),	/* note height on staff */
		shhd = note.shhd * stv_g.scale,
		x_note = x + shhd,
		y_note = y + staffb
︙
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1358
1359
1360
1361
1362
1363
1364


1365
1366
1367
1368
1369
1370
1371







-
-







		default:			// black note
			p = "hd"
			break
		}
	}
	if (note.color != undefined)
		old_color = set_color(note.color)
	else if (note.map && note.map[2])
		old_color = set_color(note.map[2])
	if (p) {
		if (inv) {
			g_open(x_note, y_note, 0, 1, -1);
			x_note = y_note = 0
		}
		if (!self.psxygl(x_note, y_note, p))
			xygl(x_note, y_note, p)
︙
1344
1345
1346
1347
1348
1349
1350

1351
1352


1353
1354
1355
1356
1357
1358
1359
1379
1380
1381
1382
1383
1384
1385
1386


1387
1388
1389
1390
1391
1392
1393
1394
1395







+
-
-
+
+







		dotx = x + (7.7 + s.xmx) * stv_g.scale
		if (y_tb[m] == undefined) {
			y_tb[m] = 3 * (s.notes[m].pit - 18)
			if ((s.notes[m].pit & 1) == 0)
				y_tb[m] += 3
		}
		doty = y_tb[m] + staffb
		i = (note.dur / 12) >> ((5 - nflags) - dots)
		while (--dots >= 0) {
			xygl(dotx, doty, "dot");
		while (dots-- > 0) {
			xygl(dotx, doty, (i & (1 << dots)) ? "dot" : "dot+")
			dotx += 3.5
		}
	}

	/* draw the accidental */
	if (note.acc) {
		x -= note.shac * stv_g.scale
︙
1383
1384
1385
1386
1387
1388
1389
1390


1391
1392
1393
1394
1395
1396

1397
1398
1399
1400

1401
1402
1403
1404
1405
1406

1407
1408
1409
1410
1411
1412
1413
1419
1420
1421
1422
1423
1424
1425

1426
1427
1428
1429
1430
1431
1432

1433
1434
1435
1436

1437
1438
1439
1440
1441


1442
1443
1444
1445
1446
1447
1448
1449







-
+
+





-
+



-
+




-
-
+








	note = s.notes[s.stem < 0 ? s.nhd : 0];	// master note head
	x_hl = x = x_head(s, note);
	staffb = staff_tb[s.st].y

	/* output the ledger lines */
	if (s.grace) {
		hltype = "ghl"
		hltype = "ghl";
		x_hl += 1.5
	} else {
		hltype = "hl"
		switch (s.head) {
		case C.OVALBARS:
		case C.OVAL:
			x_hl = x - .3;
			x_hl -= .3;
			hltype = "hl1"
			break
		case C.SQUARE:
			x_hl = x - 2;
			x_hl -= 2;
			hltype = "hl1"
			break
		}
	}
	draw_hl(x_hl, 3 * (s.notes[0].pit - 18), 3 * (s.notes[s.nhd].pit - 18),
		s.st, hltype)
	self.draw_hl(x_hl, s, hltype)

	/* draw the stem and flags */
	y = y_head(s, note)
	if (!s.stemless) {
		slen = s.ys - s.y;
		nflags = s.nflags
		if (s.ntrem)
︙
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1460
1461
1462
1463
1464
1465
1466


1467
1468
1469
1470
1471
1472
1473







-
-







			out_stem(x, y, slen, s.grace,
				 nflags, cfmt.straightflags)
		}
	} else if (s.xstem) {				/* cross-staff stem */
		s2 = s.ts_prev;
		slen = (s2.stem > 0 ? s2.y : s2.ys) - s.y;
		slen += staff_tb[s2.st].y - staffb;
/*fixme:KO when different scales*/
		slen /= s.p_v.scale;
		out_stem(x, y, slen)
	}

	/* draw the tremolo bars */
	if (fl && s.trem1) {
		var	ntrem = s.ntrem || 0,
			x1 = x;
︙
1456
1457
1458
1459
1460
1461
1462
1463

1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494

1495
1496


















1497
1498
1499




1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511


1512
1513
1514
1515
1516
1517
1518
1490
1491
1492
1493
1494
1495
1496

1497












1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515

1516
1517

1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553

1554
1555
1556
1557
1558
1559
1560
1561
1562







-
+
-
-
-
-
-
-
-
-
-
-
-
-


















-
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+











-
+
+








	/* draw the note heads */
	x = s.x
	for (m = 0; m <= s.nhd; m++)
		draw_basic_note(x, s, m, y_tb)
}

/* -- find where to terminate/start a slur -- */
// find where to start a long decoration
function next_scut(s) {
	var prev = s

	for (s = s.next; s; s = s.next) {
		if (s.rbstop)
			return s
		prev = s
	}
	/*fixme: KO when no note for this voice at end of staff */
	return prev
}

function prev_scut(s) {
	while (s.prev) {
		s = s.prev
		if (s.rbstart)
			return s
	}

	/* return a symbol of any voice starting before the start of the voice */
	s = s.p_v.sym
	while (s.type != C.CLEF)
		s = s.ts_prev		/* search a main voice */
	if (s.next && s.next.type == C.KEY)
		s = s.next
	if (s.next && s.next.type == C.METER)
		return s.next
	return s
}

/* -- decide whether a slur goes up or down -- */
/* -- decide whether a slur goes up or down (same voice) -- */
function slur_direction(k1, k2) {
	var s, some_upstem, low
    var	s, some_upstem, low, dir

	// check if slur sequence in a multi-voice staff
	function slur_multi(s1, s2) {
//		while (1) {
//			if (s1.multi)		// if multi voice
//				//fixme: may change
//				return s1.multi
//			if (s1 == s2)
//				break
//			s1 = s1.next
//		}
		if (s1.multi)
			return s1.multi
		if (s2.multi)
			return s2.multi
		return 0
	} // slur_multi()

	if (k1.grace && k1.stem > 0)
		return -1

	dir = slur_multi(k1, k2)
	if (dir)
		return dir

	for (s = k1; ; s = s.next) {
		if (s.type == C.NOTE) {
			if (!s.stemless) {
				if (s.stem < 0)
					return 1
				some_upstem = true
			}
			if (s.notes[0].pit < 22)	/* if under middle staff */
				low = true
		}
		if (s == k2)
//		if (s == k2)
		if (s.time == k2.time)		// (k2 may be a grace note)
			break
	}
	if (!some_upstem && !low)
		return 1
	return -1
}

︙
1562
1563
1564
1565
1566
1567
1568
1569
1570



1571
1572

1573
1574
1575
1576
1577
1578
1579
1606
1607
1608
1609
1610
1611
1612


1613
1614
1615
1616

1617
1618
1619
1620
1621
1622
1623
1624







-
-
+
+
+

-
+







//		dx = 10.
//	dy = 1.6 * dir
	dy = 2 * dir;
	dz = .2 + .001 * (x2 - x1)
	if (dz > .6)
		dz = .6;
	dz *= dir
	
	var scale_y = stv_g.v ? stv_g.scale : 1

//	var scale_y = stv_g.st < 0 ? stv_g.scale : 1
	var scale_y = 1			// (see set_dscale())
	if (!dotted)
		output += '<path class="fill" d="M'
		output += '<path d="M'
	else
		output += '<path class="stroke" stroke-dasharray="5,5" d="M';
	out_sxsy(x1, ' ', y1);
	output += 'c' +
		((xx1 - x1) / stv_g.scale).toFixed(1) + ' ' +
		((y1 - yy1) / scale_y).toFixed(1) + ' ' +
		((xx2 - x1) / stv_g.scale).toFixed(1) + ' ' +
︙
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609

1610
1611
1612
1613
1614
1615
1616
1617







1618
1619
1620
1621
1622
1623


1624
1625
1626
1627
1628
1629






1630
1631
1632
1633
1634
1635
1636
1637



1638
1639
1640
1641
1642
1643
1644
1645
1646








1647
1648
1649
1650

1651
1652
1653
1654
1655
1656
1657
1658



1659
1660
1661
1662



1663
1664
1665
1666
1667
1668
1669
1670


1671
1672
1673



1674
1675
1676
1677
1678
1679
1680
1634
1635
1636
1637
1638
1639
1640














1641
1642
1643
1644





1645
1646
1647
1648
1649
1650
1651






1652
1653






1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665


1666
1667
1668









1669
1670
1671
1672
1673
1674
1675
1676




1677

1678
1679
1680
1681



1682
1683
1684




1685
1686
1687








1688
1689



1690
1691
1692
1693
1694
1695
1696
1697
1698
1699







-
-
-
-
-
-
-
-
-
-
-
-
-
-
+



-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+






-
-
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
+
-




-
-
-
+
+
+
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
+
+
-
-
-
+
+
+







			((xx1 + dx - x2) / stv_g.scale).toFixed(1) + ' ' +
			((y2 + dz - yy1 - dy) / scale_y).toFixed(1) + ' ' +
			((x1 - x2) / stv_g.scale).toFixed(1) + ' ' +
			((y2 + dz - y1) / scale_y).toFixed(1);
	output += '"/>\n'
}

/* -- check if slur sequence in a multi-voice staff -- */
function slur_multi(k1, k2) {
	while (1) {
		if (k1.multi)		/* if multi voice */
			/*fixme: may change*/
			return k1.multi
		if (k1 == k2)
			break
		k1 = k1.next
	}
	return 0
}

/* -- draw a phrasing slur between two symbols -- */
// draw a slur between two chords / notes
/* (the staves are not yet defined) */
/* (delayed output) */
/* (not a pretty routine, this) */
function draw_slur(k1_o, k2, m1, m2, slur_type) {
	var	k1 = k1_o,
		k, g, x1, y1, x2, y2, height, addy,
		a, y, z, h, dx, dy, dir

function draw_slur(path,	// list of symbols under the slur
		not1,		// note if start on a note head
		sl) {		// ending variables: type, note, end on a note
    var	i,
	k, g, x1, y1, x2, y2, height, addy,
	a, y, z, h, dx, dy,
	ty = sl.ty,
	while (k1.v != k2.v)
		k1 = k1.ts_next
/*fixme: if two staves, may have upper or lower slur*/
	switch (slur_type & 0x07) {	/* (ignore dotted flag) */
	case C.SL_ABOVE: dir = 1; break
	case C.SL_BELOW: dir = -1; break
	dir = (ty & 0x07) == C.SL_ABOVE ? 1 : -1,
	n = path.length,
	default:
		dir = slur_multi(k1, k2)
		if (!dir)
			dir = slur_direction(k1, k2)
		break
	}
	i1 = 0,
	i2 = n - 1,
	k1 = path[0],
	k2 = path[i2]

/*fixme: if two staves, may have upper or lower slur*/

	var	nn = 1,
		upstaff = k1.st,
		two_staves = false

	set_dscale(k1.st)
	if (k1 != k2) {
		k = k1.next

	for (i = 1; i < n; i++) {
		k = path[i]
		while (1) {
			if (k.type == C.NOTE || k.type == C.REST) {
				nn++
				if (k.st != upstaff) {
					two_staves = true
					if (k.st < upstaff)
						upstaff = k.st
				}
			}
		if (k.type == C.NOTE || k.type == C.REST) {
			nn++
			if (k.st != upstaff) {
				two_staves = true
				if (k.st < upstaff)
					upstaff = k.st
			}
		}
			if (k == k2)
				break
			k = k.next
		}
	}
	}
/*fixme: KO when two staves*/
if (two_staves) error(2, k1, "*** multi-staves slurs not treated yet");

	/* fix endpoints */
	x1 = k1_o.x
	if (k1_o.notes && k1_o.notes[0].shhd)
		x1 += k1_o.notes[0].shhd
	x1 = k1.x
	if (k1.notes && k1.notes[0].shhd)
		x1 += k1.notes[0].shhd;
	if (k1_o != k2) {
		x2 = k2.x
		if (k2.notes)
			x2 += k2.notes[0].shhd
	x2 = k2.x
	if (k2.notes)
		x2 += k2.notes[0].shhd
	} else {		/* (the slur starts on last note of the line) */
		for (k = k2.ts_next; k; k = k.ts_next)
//fixme: must check if the staff continues
			if (k.type == C.STAVES)
				break
		x2 = k ? k.x : realwidth
	}


	if (not1) {					// start on a note
	if (m1 >= 0) {
		y1 = 3 * (k1.notes[m1].pit - 18) + 5 * dir
	} else {
		y1 = 3 * (not1.pit - 18) + 2 * dir
		x1 += 3
	} else {					// start on a chord
		y1 = dir > 0 ? k1.ymx + 2 : k1.ymn - 2
		if (k1.type == C.NOTE) {
			if (dir > 0) {
				if (k1.stem > 0) {
					x1 += 5
					if (k1.beam_end
					 && k1.nflags >= -1	/* if with a stem */
︙
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723





1724
1725
1726
1727
1728
1729
1730
1733
1734
1735
1736
1737
1738
1739



1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751







-
-
-
+
+
+
+
+







					}
//				} else {
//					y1 = k1.y - 8
				}
			}
		}
	}
	if (m2 >= 0) {
		y2 = 3 * (k2.notes[m2].pit - 18) + 5 * dir
	} else {

	if (sl.is_note) {				// end on a note
		y2 = 3 * (sl.note.pit - 18) + 2 * dir
		x2 -= 3
	} else {					// end on a chord
		y2 = dir > 0 ? k2.ymx + 2 : k2.ymn - 2
		if (k2.type == C.NOTE) {
			if (dir > 0) {
				if (k2.stem > 0) {
					x2 += 1
					if (k2.beam_st
					 && k2.nflags >= -1
︙
1766
1767
1768
1769
1770
1771
1772

1773
1774


1775
1776

1777
1778
1779
1780

1781
1782
1783
1784
1785
1786
1787



1788
1789

1790
1791
1792
1793

1794
1795
1796
1797
1798
1799
1800
1787
1788
1789
1790
1791
1792
1793
1794


1795
1796
1797

1798
1799
1800
1801

1802
1803
1804
1805
1806



1807
1808
1809
1810

1811
1812
1813
1814

1815
1816
1817
1818
1819
1820
1821
1822







+
-
-
+
+

-
+



-
+




-
-
-
+
+
+

-
+



-
+







		else
			y2 = y1
		if (k1 != k2)
			x2 = k2.x - k2.wl * .3
	}

	if (nn >= 3) {
		k = path[1]
		if (k1.next.type != C.BAR
		 && k1.next.x < x1 + 48) {
		if (k.type != C.BAR
		 && k.x < x1 + 48) {
			if (dir > 0) {
				y = k1.next.ymx - 2
				y = k.ymx - 2
				if (y1 < y)
					y1 = y
			} else {
				y = k1.next.ymn + 2
				y = k.ymn + 2
				if (y1 > y)
					y1 = y
			}
		}
		if (k2.prev
		 && k2.prev.type != C.BAR
		 && k2.prev.x > x2 - 48) {
		k = path[i2 - 1]
		if (k.type != C.BAR
		 && k.x > x2 - 48) {
			if (dir > 0) {
				y = k2.prev.ymx - 2
				y = k.ymx - 2
				if (y2 < y)
					y2 = y
			} else {
				y = k2.prev.ymn + 2
				y = k.ymn + 2
				if (y2 > y)
					y2 = y
			}
		}
	}

	a = (y2 - y1) / (x2 - x1)		/* slur steepness */
︙
1832
1833
1834
1835
1836
1837
1838
1839


1840
1841
1842
1843
1844
1845
1846
1854
1855
1856
1857
1858
1859
1860

1861
1862
1863
1864
1865
1866
1867
1868
1869







-
+
+







		x2 = k2.x + GSTEM_XOFF * 1.5;

	h = 0;
	a = (y2 - y1) / (x2 - x1)
	if (k1 != k2
	 && k1.v == k2.v) {
	    addy = y1 - a * x1
	    for (k = k1.next; k != k2 ; k = k.next) {
	    for (i = 1; i < i2; i++) {
		k = path[i]
		if (k.st != upstaff)
			continue
		switch (k.type) {
		case C.NOTE:
		case C.REST:
			if (dir > 0) {
				y = 3 * (k.notes[k.nhd].pit - 18) + 6
︙
1909
1910
1911
1912
1913
1914
1915
1916

1917
1918
1919
1920
1921
1922
1923
1924
1925









1926
1927
1928
1929
1930
1931
1932
1933

1934
1935
1936


1937
1938

1939
1940
1941




1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952




















































































































1953
1954
1955
1956


1957
1958
1959
1960
1961
1962


1963
1964
1965
1966
1967
1968
1969


1970
1971
1972
1973
1974
1975
1976
1977
1978
1979

1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998

1999
2000
2001
2002
2003
2004
2005
2006
2007

2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020

2021
2022
2023
2024
2025
2026
2027

2028
2029
2030
2031


2032
2033
2034

2035
2036
2037
2038
2039

2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060


2061
2062
2063
2064


2065
2066
2067


2068
2069
2070
2071
2072
2073
2074


2075
2076
2077
2078


2079
2080
2081
2082
2083
2084
2085
2086
2087
2088

2089
2090
2091
2092
2093
2094

2095
2096
2097
2098
2099
2100

2101
2102
2103
2104
2105


2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119


2120
2121
2122
2123






2124
2125

2126
2127
2128
2129
2130
2131
2132
2133


2134
2135
2136
2137
2138
2139
2140


2141
2142
2143
2144
2145
2146
2147




2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164

2165
2166
2167
2168
2169
2170
2171
2172
2173


2174
2175
2176
2177
2178




2179
2180
2181
2182
2183



2184
2185
2186
2187

2188
2189
2190

2191
2192
2193
2194
2195


2196
2197

2198
2199

2200
2201
2202
2203
2204


2205
2206
2207
2208
2209
2210
2211
2212
2213
1932
1933
1934
1935
1936
1937
1938

1939
1940
1941
1942
1943
1944
1945



1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961

1962
1963
1964

1965
1966
1967

1968
1969


1970
1971
1972
1973
1974
1975
1976

1977
1978
1979




1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097


2098
2099
2100
2101
2102
2103


2104
2105
2106
2107
2108




2109
2110










2111



















2112









2113













2114







2115




2116
2117



2118



2119

2120





















2121
2122




2123
2124



2125
2126







2127
2128




2129
2130










2131






2132






2133





2134
2135




2136
2137
2138
2139
2140
2141
2142
2143


2144
2145




2146
2147
2148
2149
2150
2151
2152

2153
2154
2155
2156
2157
2158
2159


2160
2161
2162
2163
2164
2165



2166
2167
2168
2169





2170
2171
2172
2173


2174

2175
2176




2177
2178
2179




2180









2181
2182





2183
2184
2185
2186





2187
2188
2189




2190



2191



2192

2193
2194
2195

2196
2197

2198
2199
2200
2201


2202
2203


2204
2205
2206
2207
2208
2209
2210







-
+






-
-
-
+
+
+
+
+
+
+
+
+







-
+


-
+
+

-
+

-
-
+
+
+
+



-



-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
+
+




-
-
+
+



-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
-
-
-
-
+
+
-
-
-
+
-
-
-

-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
+
+
-
-
-
+
+
-
-
-
-
-
-
-
+
+
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
+
+
-
-
-
-








-
-
+
+
-
-
-
-
+
+
+
+
+
+

-
+






-
-
+
+




-
-
-
+
+


-
-
-
-
-
+
+
+
+
-
-

-


-
-
-
-



-
-
-
-
+
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
-
-
-
-
+
-
-
-
+
-
-
-

-
+
+

-
+

-
+



-
-
+
+
-
-







	} else {
		if (height > -.8 * y)
			height = -.8 * y
	}
	height *= cfmt.slurheight;

//	anno_start(k1_o, 'slur');
	slur_out(x1, y1, x2, y2, dir, height, slur_type & C.SL_DOTTED);
	slur_out(x1, y1, x2, y2, dir, height, ty & C.SL_DOTTED);
//	anno_stop(k1_o, 'slur');

	/* have room for other symbols */
	dx = x2 - x1;
	a = (y2 - y1) / dx;
/*fixme: it seems to work with .4, but why?*/
	addy = y1 - a * x1 + .4 * height
	if (k1.v == k2.v)
	    for (k = k1; k != k2; k = k.next) {
//	addy = y1 - a * x1 + .4 * height
//fixme: the following code seems better!
	addy = y1 - a * x1
	if (height > 0)
		addy += 4 * Math.sqrt(height) - 2
	else
		addy -= 4 * Math.sqrt(-height) - 2
	for (i = 0; i < i2; i++) {
		k = path[i]
		if (k.st != upstaff)
			continue
		y = a * k.x + addy
		if (k.ymx < y)
			k.ymx = y
		else if (k.ymn > y)
			k.ymn = y
		if (k.next == k2) {
		if (i == i2 - 1) {
			dx = x2
			if (k2.sl1)
				dx -= 5
				dx -= 5;
			y -= height / 3
		} else {
			dx = k.next.x
			dx = path[i + 1].x
		}
		if (k != k1)
			x1 = k.x;
		if (i != 0)
			x1 = k.x
		else
			y -= height / 3
		dx -= x1;
		y_set(upstaff, dir > 0, x1, dx, y)
	}
	return (dir > 0 ? C.SL_ABOVE : C.SL_BELOW) | (slur_type & C.SL_DOTTED)
}

/* -- draw the slurs between 2 symbols --*/
function draw_slurs(first, last) {
	var	s1, k, gr1, gr2, i, m1, m2, slur_type, cont,
		s = first

function draw_slurs(s, last) {
    var	gr1, i, m, note, sls, nsls

	// draw a slur knowing the start and stop elements
	function draw_sls(s,		// start symbol
			sl,		// stop note
			snote) {	// optional start note
	    var	k, v, i, dir,
		path = [],
		enote = sl.note,
		s2 = enote.s			// end of slur

		if (last && s2.time > last.time)
			return			// will be drawn next time

		// if the slur continues on the next music line,
		// stop it on the invisible bar at end of current line
		if (tsnext && s2.time >= tsnext.time) {
			s.p_v.sls.push(sl);		// continuation on next line
			s2 = s.p_v.s_next.prev		// one voice
			while (s2.next)
				s2 = s2.next;		// search the ending bar
			sl = Object.create(sl);		// new slur
			sl.note = {
				s: s2
			}
		}

		// set the slur position
		switch (sl.ty & 0x07) {
		case C.SL_ABOVE: dir = 1; break
		case C.SL_BELOW: dir = -1; break
		default:
			if (s.v != s2.v)
				dir = -1	// always above ?
			else
				dir = slur_direction(s, s2);
			sl.ty &= ~0x07;
			sl.ty |= dir > 0 ? C.SL_ABOVE : C.SL_BELOW
			break
		}

		// build the path of the symbols under the slur
		if (dir *			// if slur on first voice
			(cur_sy.voices[s.v].range <= cur_sy.voices[s2.v].range ?
				1 : -1) > 0)
			v = s.v
		else
			v = s2.v

		if (gr1				// if start on a grace note
		 && !(s2.grace			// and not end in the same
		   && s.v == s2.v		// grace note sequence
		   && s.time == s2.time)) {
			do {
				path.push(s);	// add all grace notes
				s = s.next
			} while (s);
			s = gr1.next
		} else {
			path.push(s);
			if (s.grace)
				s = s.next
			else
				s = s.ts_next
		}

		if (!s2.grace) {		// if end on a normal note
			while (1) {
				if (s.v == v)
					path.push(s)
				if (s == s2)
					break
				s = s.ts_next
			}
		} else if (s.grace) {		// if start/end in the same sequence
			while (1) {
				if (s.v == v)
					path.push(s)
				if (s == s2)
					break
				s = s.next
			}
		} else {			// end on a grace note
			k = s2
			while (k.prev)
				k = k.prev	// .extra pointer
			while (1) {
				if (s.v == v)
					path.push(s)
				if (s.extra == k)
					break
				s = s.ts_next
			}
			s = k
			while (1) {
				path.push(s)
				if (s == s2)
					break
				s = s.next
			}
		}

		// if some nested slurs/tuplets, draw them
		for (i = 1; i < path.length - 1; i++) {
			s = path[i]
			if (s.sls || s.sl1)
				draw_slurs(s, last)
			if (s.tp)
				draw_tuplet(s)
		}
		draw_slur(path, snote, sl)
		return 1			// slur drawn, remove it
	} // draw_sls()

	// code of draw_slurs()
	while (1) {
		if (!s || s == last) {
			if (!gr1
			 || !(s = gr1.next)
			if (!gr1		// if end of grace notes
			 || !(s = gr1.next)	// return to normal notes
			 || s == last)
				break
			gr1 = null
		}
		if (s.type == C.GRACE) {
			gr1 = s;
		if (s.type == C.GRACE) {	// if start of grace notes
			gr1 = s;		// continue in the grace note sequence
			s = s.extra
			continue
		}
		if ((s.type != C.NOTE && s.type != C.REST
		  && s.type != C.SPACE)
		 || (!s.slur_start && !s.sl1)) {
			s = s.next
		if (s.sls) {			// slurs from the chord
			sls = s.sls
			continue
		}
		k = null;		/* find matching slur end */
		s1 = s.next
		var gr1_out = false
		while (1) {
			if (!s1) {
				if (gr2) {
					s1 = gr2.next;
					gr2 = null
			s.sls = null
					continue
				}
				if (!gr1 || gr1_out)
					break
				s1 = gr1.next;
				gr1_out = true
				continue
			}
			if (s1.type == C.GRACE) {
				gr2 = s1;
				s1 = s1.extra
				continue
			}
			if (s1.type == C.BAR
			 && (s1.bar_type[0] == ':'
			  || s1.bar_type == "|]"
			  || s1.bar_type == "[|"
			  || (s1.text && s1.text[0] != '1'))) {
				k = s1
			nsls = []
				break
			}
			if (s1.type != C.NOTE && s1.type != C.REST
			 && s1.type != C.SPACE) {
				s1 = s1.next
				continue
			}
			if (s1.slur_end || s1.sl2) {
				k = s1
			for (i = 0; i < sls.length; i++) {
				break
			}
			if (s1.slur_start || s1.sl1) {
				if (gr2) {	/* if in grace note sequence */
					for (k = s1; k.next; k = k.next)
						;
					k.next = gr2.next
					if (gr2.next)
						gr2.next.prev = k;
//					gr2.slur_start = C.SL_AUTO
					k = null
				}
				draw_slurs(s1, last)
				if (!draw_sls(s, sls[i]))
				if (gr2
				 && gr2.next) {
					gr2.next.prev.next = null;
					gr2.next.prev = gr2
				}
			}
			if (s1 == last)
					nsls.push(sls[i])
				break
			s1 = s1.next
		}
		if (!s1) {
			}
			if (nsls.length)
			k = next_scut(s)
		} else if (!k) {
			s = s1
				s.sls = nsls
			if (s == last)
				break
			continue
		}

		if (s.sl1) {			// slurs from the note heads
		/* if slur in grace note sequence, change the linkages */
		if (gr1) {
			for (s1 = s; s1.next; s1 = s1.next)
				;
			s1.next = gr1.next
			if (gr1.next)
				gr1.next.prev = s1;
			gr1.slur_start = C.SL_AUTO
		}
		if (gr2) {
			gr2.prev.next = gr2.extra;
			gr2.extra.prev = gr2.prev;
			gr2.slur_start = C.SL_AUTO
		}
		if (s.slur_start) {
			slur_type = s.slur_start & 0x0f;
			s.slur_start >>= 4;
			m1 = -1
		} else {
			for (m1 = 0; m1 <= s.nhd; m1++)
				if (s.notes[m1].sl1)
			for (m = 0; m <= s.nhd; m++) {
				note = s.notes[m]
					break
			slur_type = s.notes[m1].sl1 & 0x0f;
			s.notes[m1].sl1 >>= 4;
			s.sl1--
				if (note.sls) {
					sls = note.sls
		}
		m2 = -1;
		cont = 0
					note.sls = null
					nsls = []
		if ((k.type == C.NOTE || k.type == C.REST || k.type == C.SPACE) &&
		    (k.slur_end || k.sl2)) {
			if (k.slur_end) {
				k.slur_end--
			} else {
				for (m2 = 0; m2 <= k.nhd; m2++)
					if (k.notes[m2].sl2)
					for (i = 0; i < sls.length; i++) {
						if (!draw_sls(s, sls[i], note))
						break
				k.notes[m2].sl2--;
				k.sl2--
			}
							nsls.push(sls[i])
					}
		} else {
			if (k.type != C.BAR
			 || (k.bar_type[0] != ':'
			  && k.bar_type != "|]"
			  && k.bar_type != "[|"
			  && (!k.text || k.text[0] == '1')))
				cont = 1
		}
		slur_type = draw_slur(s, k, m1, m2, slur_type)
		if (cont) {
					if (nsls.length)
			if (!k.p_v.slur_start)
				k.p_v.slur_start = 0;
			k.p_v.slur_start <<= 4;
			k.p_v.slur_start += slur_type
		}

						note.sls = nsls
		/* if slur in grace note sequence, restore the linkages */
		if (gr1
		 && gr1.next) {
			gr1.next.prev.next = null;
			gr1.next.prev = gr1
		}
				}
		if (gr2) {
			gr2.prev.next = gr2;
			gr2.extra.prev = null
		}

			}
		}
		if (s.slur_start || s.sl1)
			continue
		if (s == last)
			break
		s = s.next
	}
}

/* -- draw a tuplet -- */
/* (the staves are not yet defined) */
/* (delayed output) */
/* See http://moinejf.free.fr/abcm2ps-doc/tuplets.xhtml
 * for the value of 'tf' */
function draw_tuplet(s1,
 * for the value of 'tp.f' */
function draw_tuplet(s1, tp) {
			lvl) {	// nesting level
	var	s2, s3, g, upstaff, nb_only, some_slur,
		x1, x2, y1, y2, xm, ym, a, s0, yy, yx, dy, a, b, dir,
		p, q, r
    var	s2, s3, g, upstaff, nb_only, some_slur,
	x1, x2, y1, y2, xm, ym, a, s0, yy, yx, dy, a, dir, r,
	tp = s1.tp.shift()		// tuplet parameters

	if (!s1.tp.length)
		delete s1.tp		// last tuplet

	// check if some slurs and treat the nested tuplets
	// treat the slurs and the nested tuplets
	upstaff = s1.st
	set_dscale(s1.st)
	for (s2 = s1; s2; s2 = s2.next) {
		if (s2.type != C.NOTE && s2.type != C.REST) {
			if (s2.type == C.GRACE) {
				for (g = s2.extra; g; g = g.next) {
					if (g.slur_start || g.sl1)
						some_slur = true
					if (g.sls || g.sl1)
						draw_slurs(g)
				}
			}
			continue
		}
		if (s2.slur_start || s2.slur_end /* if slur start/end */
		 || s2.sl1 || s2.sl2)
			some_slur = true
		if (s2.sls || s2.sl1)
			draw_slurs(s2)
		if (s2.st < upstaff)
			upstaff = s2.st
		if (lvl == 0) {
			if (s2.tp1)
				draw_tuplet(s2, 1)
			if (s2.te0)
				break
		if (s2.tp)
			draw_tuplet(s2)
		if (s2.tpe)
			break
		} else if (s2.te1)
			break
	}

	if (!s2) {
		error(1, s1, "No end of tuplet in this music line")
		if (lvl == 0)
			s1.tp0 = 0
		else
			s1.tp1 = 0
		return
	}

	/* draw the slurs fully inside the tuplet */
	if (some_slur) {
		draw_slurs(s1, s2)

	s2.tpe--
		// don't draw the tuplet when a slur starts or stops inside it
		if (s1.slur_start || s1.sl1)
			return
		for (s3 = s1.next; s3 != s2; s3 = s3.next) {
			if (s3.slur_start || s3.slur_end
			 || s3.sl1 || s3.sl2)
				return
		}


	if (tp.f[0] == 1)			/* if 'when' == never */
		if (s2.slur_end || s2.sl2)
			return
	}

	if (lvl == 0) {
		return

	dir = tp.f[3]				// 'where'
	if (!dir) {				// if auto
		p = s1.tp0;
		s1.tp0 = 0;
		q = s1.tq0
	} else {
		p = s1.tp1;
		s3 = s1
		while (s3.type != C.NOTE)
			s3 = s3.next
		s1.tp1 = 0
		q = s1.tq1
	}

		dir = s3.stem > 0 ? C.SL_ABOVE : C.SL_BELOW
	if (s1.tf[0] == 1)			/* if 'when' == never */
		return

	}
	dir = s1.tf[3]				/* 'where' (C.SL_xxx) */
	if (!dir)
		dir = s1.stem > 0 ? C.SL_ABOVE : C.SL_BELOW

	if (s1 == s2) {				/* tuplet with 1 note (!) */
	if (s1 == s2				// tuplet with 1 note (!)
	 || tp.f[1] == 2) {			// what == nothing
		nb_only = true
	} else if (s1.tf[1] == 1) {			/* 'what' == slur */
	} else if (tp.f[1] == 1) {			/* 'what' == slur */
		nb_only = true;
		draw_slur(s1, s2, -1, -1, dir)
		draw_slur([s1, s2], null, {ty: dir})
	} else {

		/* search if a bracket is needed */
		if (s1.tf[0] == 2		/* if 'when' == always */
		 || s1.type != C.NOTE || s2.type != C.NOTE) {
		if (tp.f[0] != 2		// if 'when' != always
		 && s1.type == C.NOTE && s2.type == C.NOTE) {
			nb_only = false
		} else {
			nb_only = true
			for (s3 = s1; ; s3 = s3.next) {
				if (s3.type != C.NOTE
				 && s3.type != C.REST) {
					if (s3.type == C.GRACE
					 || s3.type == C.SPACE)
						continue
︙
2247
2248
2249
2250
2251
2252
2253
2254

2255
2256
2257
2258
2259
2260

2261
2262
2263
2264


2265
2266
2267
2268
2269


2270
2271
2272
2273

2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286



2287
2288






2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302

2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2244
2245
2246
2247
2248
2249
2250

2251
2252
2253




2254




2255
2256





2257
2258




2259




2260
2261
2262
2263
2264
2265



2266
2267
2268
2269

2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288

2289

2290
2291
2292



2293
2294
2295
2296
2297
2298
2299







-
+


-
-
-
-
+
-
-
-
-
+
+
-
-
-
-
-
+
+
-
-
-
-
+
-
-
-
-






-
-
-
+
+
+

-
+
+
+
+
+
+













-
+
-



-
-
-







				}
			}
		}
	}

	/* if number only, draw it */
	if (nb_only) {
		if (s1.tf[2] == 1)		/* if 'which' == none */
		if (tp.f[2] == 1)		/* if 'which' == none */
			return
		xm = (s2.x + s1.x) / 2
		if (s1 == s2)			/* tuplet with 1 note */
			a = 0
		else
			a = (s2.ys - s1.ys) / (s2.x - s1.x);
		a = s2.x - s1.x			// width around the middle
		b = s1.ys - a * s1.x;
		yy = a * xm + b
		if (dir == C.SL_ABOVE) {
			ym = y_get(upstaff, 1, xm - 4, 8)
		if (dir == C.SL_ABOVE)
			ym = y_get(upstaff, 1, xm - a / 2, a) + 2
			if (ym > yy)
				b += ym - yy;
			b += 2
		} else {
			ym = y_get(upstaff, 0, xm - 4, 8)
		else
			ym = y_get(upstaff, 0, xm - a / 2, a) - 10
			if (ym < yy)
				b += ym - yy;
			b -= 10
		}

		for (s3 = s1; ; s3 = s3.next) {
			if (s3.x >= xm)
				break
		}
		if (s1.stem * s2.stem > 0) {
			if (s1.stem > 0)
				xm += 1.5
			else
				xm -= 1.5
		}
		ym = a * xm + b
		if (s1.tf[2] == 0)		/* if 'which' == number */
			out_bnum(xm, ym, p)

		if (tp.f[2] == 0)		/* if 'which' == number */
			out_bnum(xm, ym, tp.p)
		else
			out_bnum(xm, ym, p + ':' +  q)
			out_bnum(xm, ym, tp.p + ':' + tp.q)

		for (s3 = s1; ; s3 = s3.next) {
			if (s3.x >= xm)
				break
		}
		if (dir == C.SL_ABOVE) {
			ym += 10
			if (s3.ymx < ym)
				s3.ymx = ym;
			y_set(upstaff, true, xm - 3, 6, ym)
		} else {
			if (s3.ymn > ym)
				s3.ymn = ym;
			y_set(upstaff, false, xm - 3, 6, ym)
		}
		return
	}

	if (s1.tf[1] != 0)				/* if 'what' != square */
	// here, what is square bracket
		error(2, s1, "'what' value of %%tuplets not yet coded")

/*fixme: two staves not treated*/
/*fixme: to optimize*/
	dir = s1.tf[3]				// 'where'
	if (!dir)
		dir = s1.multi >= 0 ? C.SL_ABOVE : C.SL_BELOW
    if (dir == C.SL_ABOVE) {

	/* sole or upper voice: the bracket is above the staff */
	if (s1.st == s2.st) {
		y1 = y2 = staff_tb[upstaff].topbar + 4
	} else {
		y1 = s1.ymx;
︙
2332
2333
2334
2335
2336
2337
2338
2339

2340
2341
2342
2343
2344
2345
2346
2315
2316
2317
2318
2319
2320
2321

2322
2323
2324
2325
2326
2327
2328
2329







-
+







		for (s3 = s2; !s3.dur; s3 = s3.prev)
			;
		ym = y_get(upstaff, 1, s3.x - 4, 8)
		if (ym > y2)
			y2 = ym
	}

	/* end the backet according to the last note duration */
	// end the bracket according to the last note duration
	if (s2.dur > s2.prev.dur) {
		if (s2.next)
			x2 = s2.next.x - s2.next.wl - 5
		else
			x2 = realwidth - 6
	} else {
		x2 = s2.x + 4;
︙
2492
2493
2494
2495
2496
2497
2498
2499

2500
2501
2502
2503
2504

2505
2506
2507
2508

2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521









2522

2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535













2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
















2551
2552
2553
2554


2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576





















2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592















2593





2594

2595
2596
2597
2598



2599
2600
2601

2602
2603
2604
2605
2606
2607
2608
2609
2610
2611

2612
2613
2614
2615


2616
2617
2618
2619
2620
2621
2622
2623







2624

2625
2626
2627
2628

2629
2630

2631
2632

2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644



2645
2646
2647
2648
2649

2650
2651
2652
2653
2654
2655





2656
2657
2658
2659
2660
2661
2662
2663

2664


2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679





2680
2681
2682
2683





2684
2685
2686
2687
2688
2689
2690
2691
2692
2693


2694
2695
2696
2697
2698

2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724

2725
2726
2727
2728
2729
2730
2731


2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774


2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786












2787
2788

2789
2790
2791
2792

2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812










2813
2814



2815
2816
2817
2818

2819
2820
2821
2822
2823




2824
2825
2826

2827
2828

2829

2830
2831
2832
2833









2834
2835
2836


2837
2838
2839
2840
2841
2842

2843
2844
2845
2846
2847
2848
2849


2850
2851
2852
2853


2854
2855

2856
2857

2858
2859

2860
2861
2862
2863
2864
2865
2866
2475
2476
2477
2478
2479
2480
2481

2482
2483
2484
2485
2486

2487
2488

2489

2490
2491
2492
2493
2494
2495








2496
2497
2498
2499
2500
2501
2502
2503
2504

2505
2506












2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520














2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537



2538
2539






















2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561















2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582

2583
2584



2585
2586
2587



2588

2589
2590
2591
2592
2593
2594
2595
2596

2597




2598
2599

2600
2601
2602
2603



2604
2605
2606
2607
2608
2609
2610

2611
2612
2613
2614

2615
2616

2617


2618












2619
2620
2621





2622






2623
2624
2625
2626
2627




2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643


2644




2645
2646
2647
2648
2649
2650



2651
2652
2653
2654
2655
2656
2657
2658
2659






2660
2661

2662
2663
2664

2665
2666
2667
2668




















2669
2670

2671
2672
2673


2674


2675
2676
2677
2678
2679
2680
2681
2682
2683


































2684
2685
2686
2687
2688











2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701

2702




2703










2704
2705
2706
2707






2708
2709
2710
2711
2712
2713
2714
2715
2716
2717


2718
2719
2720
2721

2722

2723





2724
2725
2726
2727



2728
2729
2730
2731

2732
2733
2734


2735
2736
2737
2738
2739
2740
2741
2742
2743



2744
2745






2746







2747
2748




2749
2750
2751

2752
2753

2754
2755

2756
2757
2758
2759
2760
2761
2762
2763







-
+




-
+

-

-
+





-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+

-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
-
+

-
-
-
+
+
+
-
-
-
+
-








-
+
-
-
-
-
+
+
-




-
-
-
+
+
+
+
+
+
+
-
+



-
+

-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-




+

+
+








-
-

-
-
-
-
+
+
+
+
+

-
-
-
+
+
+
+
+




-
-
-
-
-
-
+
+
-



-
+



-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-


-
+


-
-

-
-
+
+







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-


+
+

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-




-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+

-

-
+
-
-
-
-
-
+
+
+
+
-
-
-
+


+
-
+


-
-
+
+
+
+
+
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
+
-
-
-
-
+
+

-
+

-
+

-
+







			y_set(upstaff, false, s3.x, s3.next.x - s3.x, yy)
		}
		if (s3 == s2)
			break
	}
    } /* lower voice */

	if (s1.tf[2] == 1) {			/* if 'which' == none */
	if (tp.f[2] == 1) {			/* if 'which' == none */
		out_tubr(x1, y1 + 4, x2 - x1, y2 - y1, dir == C.SL_ABOVE);
		return
	}
	out_tubrn(x1, y1, x2 - x1, y2 - y1, dir == C.SL_ABOVE,
		s1.tf[2] == 0 ? p.toString() : p + ':' +  q);
		tp.f[2] == 0 ? tp.p.toString() : tp.p + ':' +  tp.q);

	yy = .5 * (y1 + y2)
	if (dir == C.SL_ABOVE)
		y_set(upstaff, true, xm - 3, 6, yy + 9)
		y_set(upstaff, true, xm - 3, 6, yy + 2)
	else
		y_set(upstaff, false, xm - 3, 6, yy)
}

/* -- draw the ties between two notes/chords -- */
function draw_note_ties(k1, k2, mhead1, mhead2, job) {
	var i, dir, m1, m2, p, p2, y, st, k, x1, x2, h, sh, time

	for (i = 0; i < mhead1.length; i++) {
		m1 = mhead1[i];
		p = k1.notes[m1].pit;
		m2 = mhead2[i];
		p2 = job != 2 ? k2.notes[m2].pit : p;
function draw_note_ties(not1, job) {
    var	m, x1, x2, s, y, h, time,
	not2 = not1.tie_n,
	p = job == 2 ? not1.pit : not2.pit,
	dir = (not1.tie_ty & 0x07) == C.SL_ABOVE ? 1 : -1,
	s1 = not1.s,
	st = s1.st,
	s2 = not2.s,
	x2 = s2.x,
		dir = (k1.notes[m1].ti1 & 0x07) == C.SL_ABOVE ? 1 : -1;
	sh = not1.shhd			// head shift

		x1 = k1.x;
		sh = k1.notes[m1].shhd		/* head shift */
		if (dir > 0) {
			if (m1 < k1.nhd && p + 1 == k1.notes[m1 + 1].pit)
				if (k1.notes[m1 + 1].shhd > sh)
					sh = k1.notes[m1 + 1].shhd
		} else {
			if (m1 > 0 && p == k1.notes[m1 - 1].pit + 1)
				if (k1.notes[m1 - 1].shhd > sh)
					sh = k1.notes[m1 - 1].shhd
		}
		x1 += sh * .6;
	for (m = 0; m < s1.nhd; m++)
		if (s1.notes[m] == not1)
			break
	if (dir > 0) {
		if (m < s1.nhd && p + 1 == s1.notes[m + 1].pit)
			if (s1.notes[m + 1].shhd > sh)
				sh = s1.notes[m + 1].shhd
	} else {
		if (m > 0 && p == s1.notes[m - 1].pit + 1)
			if (s1.notes[m - 1].shhd > sh)
				sh = s1.notes[m - 1].shhd
	}
	x1 = s1.x + sh * .6

		x2 = k2.x
		if (job != 2) {
			sh = k2.notes[m2].shhd
			if (dir > 0) {
				if (m2 < k2.nhd && p2 + 1 == k2.notes[m2 + 1].pit)
					if (k2.notes[m2 + 1].shhd < sh)
						sh = k2.notes[m2 + 1].shhd
			} else {
				if (m2 > 0 && p2 == k2.notes[m2 - 1].pit + 1)
					if (k2.notes[m2 - 1].shhd < sh)
						sh = k2.notes[m2 - 1].shhd
			}
			x2 += sh * .6
		}
	if (job != 2) {
		for (m = 0; m < s2.nhd; m++)
			if (s2.notes[m] == not2)
				break
		sh = s2.notes[m].shhd
		if (dir > 0) {
			if (m < s2.nhd && p + 1 == s2.notes[m + 1].pit)
				if (s2.notes[m + 1].shhd < sh)
					sh = s2.notes[m + 1].shhd
		} else {
			if (m > 0 && p == s2.notes[m - 1].pit + 1)
				if (s2.notes[m - 1].shhd < sh)
					sh = s2.notes[m - 1].shhd
		}
		x2 += sh * .6
	}

		st = k1.st
		switch (job) {
		case 0:
	switch (job) {
	case 0:
			if (p != p2 && !(p & 1))
				p = p2
			break
		case 3:				/* clef or staff change */
			dir = -dir
			// fall thru
		case 1:				/* no starting note */
			x1 = k1.x
			if (x1 > x2 - 20)
				x1 = x2 - 20;
			p = p2;
			st = k2.st
			break
/*		case 2:				 * no ending note */
		default:
			if (k1 != k2) {
				x2 -= k2.wl
				if (k2.type == C.BAR)
					x2 += 5
			} else {
				time = k1.time + k1.dur
				for (k = k1.ts_next; k; k = k.ts_next)
		p = (not1.pit & 1) ? not1.pit : not2.pit
		break
	case 3:				/* clef or staff change */
		dir = -dir
		// fall thru
	case 1:				/* no starting note */
		x1 = s1.x
		if (x1 > x2 - 20)
			x1 = x2 - 20
		p = not2.pit
		st = s2.st
		break
/*	case 2:				 * no ending note */
	default:
		if (s1 != s2) {
			x2 -= s2.wl
			if (s2.type == C.BAR)
				x2 += 5
		} else {
			time = s1.time + s1.dur
			for (s = s1.ts_next; s; s = s.ts_next)
//(fixme: must check if the staff continues??)
					if (k.time > time)
						break
				x2 = k ? k.x : realwidth
			}
			if (x2 < x1 + 16)
				x2 = x1 + 16
			break
		}
		if (x2 - x1 > 20) {
			x1 += 3.5;
			x2 -= 3.5
		} else {
			x1 += 1.5;
			x2 -= 1.5
		}
				if (s.time > time)
					break
			x2 = s ? s.x : realwidth
		}
		if (x2 < x1 + 16)
			x2 = x1 + 16
		break
	}
	if (x2 - x1 > 20) {
		x1 += 3.5
		x2 -= 3.5
	} else {
		x1 += 1.5
		x2 -= 1.5
	}

	if (s1.dots && !(not1.pit & 1)
	 && ((dir > 0 && !s1.dot_low)
	  || (dir < 0 && s1.dot_low)))
		x1 += 5

		y = 3 * (p - 18)
	y = staff_tb[st].y + 3 * (p - 18)

		h = (.04 * (x2 - x1) + 10) * dir;
//		anno_start(k1, 'slur');
		slur_out(x1, staff_tb[st].y + y,
	h = (.03 * (x2 - x1) + 16) * dir
//	anno_start(k1, 'slur')
	slur_out(x1, y, x2, y, dir, h, not1.tie_ty & C.SL_DOTTED)
			 x2, staff_tb[st].y + y,
			 dir, h, k1.notes[m1].ti1 & C.SL_DOTTED)
//		anno_stop(k1, 'slur')
//	anno_stop(k1, 'slur')
	}
}

/* -- draw ties between neighboring notes/chords -- */
function draw_ties(k1, k2,
			job) {	// 0: normal
				// 1: no starting note
				// 2: no ending note
				// 3: no start for clef or staff change
	var	k3, i, j, m1, pit, pit2, tie2,
    var	k3, i, j, not1, not3, time, pit, pit2,
		mhead1 = [],
		mhead2 = [],
		mhead3 = [],
		nh1 = k1.nhd,
	mhead3 = [],
	nh1 = k1.nhd
		time = k1.time + k1.dur

	/* half ties from last note in line or before new repeat */
	if (job == 2) {
		for (i = 0; i <= nh1; i++) {
			if (k1.notes[i].ti1)
				mhead3.push(i)
		}
			not1 = k1.notes[i]
			if (not1.tie_ty) {
				k3 = not1.tie_n
				not1.tie_n = { s: k2 || k1 }
				draw_note_ties(not1, job)
				not1.tie_n = k3
			}
		draw_note_ties(k1, k2 || k1, mhead3, mhead3, job)
		}
		return
	}

	/* set up list of ties to draw */
	// draw the ties or memorize the ones without ending
	for (i = 0; i <= nh1; i++) {
		if (!k1.notes[i].ti1)
		not1 = k1.notes[i]
			continue
		tie2 = -1;
		if (!not1.tie_ty)
		pit = k1.notes[i].opit || k1.notes[i].pit
		for (m1 = k2.nhd; m1 >= 0; m1--) {
			pit2 = k2.notes[m1].opit || k2.notes[m1].pit
			switch (pit2 - pit) {
			case 1:			/* maybe ^c - _d */
			case -1:		/* _d - ^c */
				if (k1.notes[i].acc != k2.notes[m1].acc)
					tie2 = m1
			default:
				continue
			case 0:
				tie2 = m1
			continue
		if (!not1.s)
			not1.s = k1
				break
			}
			break
		}
		if (tie2 >= 0) {		/* 1st or 2nd choice */
		if (not1.tie_n)
			mhead1.push(i);
			mhead2.push(tie2)
		} else {
			mhead3.push(i)		/* no match */
		}
	}
			draw_note_ties(not1, job)
		else
			mhead3.push(not1)	/* no match */
	}


	/* draw the ties */
	draw_note_ties(k1, k2, mhead1, mhead2, job)

	/* if any bad tie, try an other voice of the same staff */
	if (!mhead3.length)
		return				/* no bad tie */

	time = k1.time + k1.dur
	k3 = k1.ts_next
	if (job != 1)
		job = 0
	while (k3 && k3.time < time)
		k3 = k3.ts_next
	while (k3 && k3.time == time) {
		if (k3.type != C.NOTE
		 || k3.st != k1.st) {
			k3 = k3.ts_next
			continue
		}
		mhead1.length = 0;
		mhead2.length = 0
		for (i = mhead3.length; --i >= 0; ) {
			j = mhead3[i];
			pit = k1.notes[j].opit || k1.notes[j].pit
			for (m1 = k3.nhd; m1 >= 0; m1--) {
				pit2 = k3.notes[m1].opit || k3.notes[m1].pit
			not1 = mhead3[i]
			pit = not1.opit || not1.pit
			for (j = k3.nhd; j >= 0; j--) {
				not3 = k3.notes[j]
				pit2 = not3.opit || not3.pit
				if (pit2 == pit) {
					mhead1.push(j);
					mhead2.push(m1);
					mhead3[i] = mhead3.pop()
					not1.tie_n = not3
					if (!not3.s)
						not3.s = k3
					draw_note_ties(not1, job)
					mhead3.splice(i, 1)
					break
				}
			}
		}
		if (mhead1.length > 0) {
			draw_note_ties(k1, k3,
					mhead1, mhead2,
					job == 1 ? 1 : 0)
			if (mhead3.length == 0)
				return
		if (!mhead3.length)
			break
		}
		k3 = k3.ts_next
	}

	if (mhead3.length != 0)
	if (mhead3.length)
		error(1, k1, "Bad tie")
}

/* -- try to get the symbol of a ending tie when combined voices -- */
function tie_comb(s) {
	var	s1, time, st;

	time = s.time + s.dur;
	st = s.st
	for (s1 = s.ts_next; s1; s1 = s1.ts_next) {
		if (s1.st != st)
			continue
		if (s1.time == time) {
			if (s1.type == C.NOTE)
				return s1
			continue
		}
		if (s1.time > time)
			return s		// bad tie
	}
	return //null				// no ending tie
}

/* -- draw all ties between neighboring notes -- */
function draw_all_ties(p_voice) {
	var s1, s2, s3, clef_chg, time, s_rtie, s_tie, x, dx
    var s, s1, s2, clef_chg, time, x, dx, s_next, m

	function draw_ties_g(s1, s2, job) {
		var g

		if (s1.type == C.GRACE) {
			for (g = s1.extra; g; g = g.next) {
				if (g.ti1)
			for (var g = s1.extra; g; g = g.next) {
				if (g.tie_s)
					draw_ties(g, s2, job)
			}
		} else {
			draw_ties(s1, s2, job)
		}
	} // draw_ties_g()

	for (s1 = p_voice.sym; s1; s1 = s1.next) {
		switch (s1.type) {
		case C.CLEF:
		case C.KEY:
		case C.METER:
			continue
		}
		break
	}
	s_rtie = p_voice.s_rtie			/* tie from 1st repeat bar */
	for (s2 = s1; s2; s2 = s2.next) {
		if (s2.dur
		 || s2.type == C.GRACE)
			break
		if (s2.type != C.BAR
		 || !s2.text)			// not a repeat bar
			continue
		if (s2.text[0] == '1')		/* 1st repeat bar */
			s_rtie = p_voice.s_tie
		else
			p_voice.s_tie = s_rtie
	}
	if (!s2)
		return
	if (p_voice.s_tie) {			/* tie from previous line */
		p_voice.s_tie.x = s1.x + s1.wr;
		s1 = p_voice.s_tie;
		p_voice.s_tie = null;
		s1.st = s2.st;
		s1.ts_next = s2.ts_next;	/* (for tie to other voice) */
		s1.time = s2.time - s1.dur;	/* (if after repeat sequence) */
		draw_ties(s1, s2, 1)		/* tie to 1st note */
	}

	/* search the start of ties */
//	clef_chg = false
	s_next = p_voice.sym
	set_color(s_next.color)
	while (1) {
		for (s1 = s2; s1; s1 = s1.next) {
			if (s1.ti1)
				break
			if (!s_rtie)
				continue
			if (s1.type != C.BAR
			 || !s1.text)			// not a repeat bar
				continue
			if (s1.text[0] == '1') {	/* 1st repeat bar */
				s_rtie = null
				continue
		for (s1 = s_next; s1; s1 = s1.next) {
			if (s1.ti2			// if no tie start
			 && (s1 != s_next
			  || !s_next.prev)) {		// (at start of line)
				s = s1.ti2
				s.x = s1.x
				s2 = s.next
				s.next = s1
				s.st = s1.st
				s.time = s1.time - s.dur
				draw_ties(s, s1, 1)
				s.next = s2	// restore the linkage for play
			}
			if (s1.bar_type == '|')
			if (s1.tie_s)
				continue		// not a repeat
			for (s2 = s1.next; s2; s2 = s2.next)
				if (s2.type == C.NOTE)
					break
				break
			if (!s2) {
				s1 = null
				break
			}
			s_tie = clone(s_rtie);
			s_tie.x = s1.x;
			s_tie.next = s2;
			s_tie.st = s2.st;
			s_tie.time = s2.time - s_tie.dur;
			draw_ties(s_tie, s2, 1)
		}
		if (!s1)
			break

		/* search the end of the tie
		 * and notice the clef changes (may occur in an other voice) */
		time = s1.time + s1.dur
		for (s2 = s1.next; s2; s2 = s2.next) {
			if (s2.dur)
				break
		// check if the tied note is in this line
		s2 = s1.tie_s				// ending note of the tie
		if (s2.v == s1.v) {
			s_next = s2
			s = s1
			while (1) {
				if (!s.next) {
					s2 = s
					s = null
					break
			if (s2.type == C.BAR && s2.text) {	// repeat bar
				if (s2.text[0] != '1')
				}
				s = s.next
				if (s == s2)
					break
				s_rtie = s1		/* 1st repeat bar */
			}
		}
		} else {
		if (!s2) {
			for (s2 = s1.ts_next; s2; s2 = s2.ts_next) {
				if (s2.st != s1.st)
					continue
				if (s2.time < time)
			s_next = s1.next
			s = s1
			while (1) {
				if (!s.ts_next) {
					continue
				if (s2.time > time) {
					s2 = null
					s = null
					break
				}
				s = s.ts_next
				if (s2.dur)
				if (s == s2)
					break
			}
			if (!s2) {
				draw_ties_g(s1, null, 2);
			if (!s) {
				s2 = s1
				while (s2.next)
					s2 = s2.next
			}
		}

		if (!s) {				// end of line
			draw_ties_g(s1, s2, 2);
				p_voice.s_tie = s1
				break
			}
			break
		}
		} else {
			if (s2.type != C.NOTE
			 && s2.type != C.BAR) {
				error(1, s1, "Bad tie")
				continue
			}

			if (s2.time != time) {
				s3 = tie_comb(s1)
				if (s3 == s1) {
					error(1, s1, "Bad tie")
					continue
				}
				s2 = s3
		// check if some clef changes (may occur in an other voice)
		time = s1.time + s.dur
			}
		}
		for (s3 = s1.ts_next; s3; s3 = s3.ts_next) {
			if (s3.st != s1.st)
		for (s = s1.ts_next; s != s2; s = s.ts_next) {
			if (s.st != s1.st)
				continue
			if (s3.time > time)
			if (s.time > time)
				break
			if (s3.type == C.CLEF) {
			if (s.type == C.CLEF) {
				clef_chg = true
				continue
				break
			}
		}

		/* ties with clef or staff change */
		if (clef_chg || s1.st != s2.st) {
			clef_chg = false;
			dx = (s2.x - s1.x) * .4;
︙
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962

2963
2964
2965
2966
2967
2968
2969
2970
2971

2972
2973

2974
2975
2976
2977
2978
2979
2980
2773
2774
2775
2776
2777
2778
2779




































































2780
2781
2782
2783
2784
2785
2786
2787
2788

2789

2790
2791
2792
2793
2794
2795
2796
2797
2798

2799

2800
2801
2802
2803
2804
2805
2806
2807
2808







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-









-

-
+








-
+
-

+







				s1.x = s2.x - 24.;
			draw_ties(s1, s2, 3);
			s1.x = x
			continue
		}
		draw_ties_g(s1, s2, s2.type == C.NOTE ? 0 : 2)
	}
	p_voice.s_rtie = s_rtie
}

/* -- draw all phrasing slurs for one staff -- */
/* (the staves are not yet defined) */
function draw_all_slurs(p_voice) {
	var	k, i, m2,
		s = p_voice.sym,
		slur_type = p_voice.slur_start,
		slur_st = 0

	if (!s)
		return

	/* the starting slur types are inverted */
	if (slur_type) {
		p_voice.slur_start = 0
		while (slur_type != 0) {
			slur_st <<= 4;
			slur_st |= (slur_type & 0x0f);
			slur_type >>= 4
		}
	}

	/* draw the slurs inside the music line */
	draw_slurs(s, undefined)

	/* do unbalanced slurs still left over */
	for ( ; s; s = s.next) {
		while (s.slur_end || s.sl2) {
			if (s.slur_end) {
				s.slur_end--;
				m2 = -1
			} else {
				for (m2 = 0; m2 <= s.nhd; m2++)
					if (s.notes[m2].sl2)
						break
				s.notes[m2].sl2--;
				s.sl2--
			}
			slur_type = slur_st & 0x0f;
			k = prev_scut(s);
			draw_slur(k, s, -1, m2, slur_type)
			if (k.type != C.BAR
			 || (k.bar_type[0] != ':'
			  && k.bar_type != "|]"
			  && k.bar_type != "[|"
			  && (!k.text || k.text[0] == '1')))
				slur_st >>= 4
		}
	}
	s = p_voice.sym
	while (slur_st != 0) {
		slur_type = slur_st & 0x0f;
		slur_st >>= 4;
		k = next_scut(s);
		draw_slur(s, k, -1, -1, slur_type)
		if (k.type != C.BAR
		 || (k.bar_type[0] != ':'
		  && k.bar_type != "|]"
		  && k.bar_type != "[|"
		  && (!k.text || k.text[0] == '1'))) {
			if (!p_voice.slur_start)
				p_voice.slur_start = 0;
			p_voice.slur_start <<= 4;
			p_voice.slur_start += slur_type
		}
	}
}

/* -- draw the symbols near the notes -- */
/* (the staves are not yet defined) */
/* order:
 * - scaled
 *   - beams
 *   - decorations near the notes
 *   - measure bar numbers
 *   - n-plets
 *   - decorations tied to the notes
 *   - slurs
 *   - tuplets and slurs
 * - not scaled
 *   - chord symbols
 *   - staff decorations
 *   - lyrics
 *   - measure numbers
 * The buffer output is delayed until the definition of the staff system
 */
function draw_sym_near() {
	var p_voice, p_st, s, v, st, y, g, w, i, st, dx, top, bot, output_sav;
    var	p_voice, p_st, s, v, st, y, g, w, i, st, dx, top, bot,

	output_sav = output;

	output = ""

	/* calculate the beams but don't draw them (the staves are not yet defined) */
	for (v = 0; v < voice_tb.length; v++) {
		var	bm = {},
			first_note = true;

︙
3083
3084
3085
3086
3087
3088
3089


3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103

3104
3105
3106


3107
3108
3109
3110
3111
3112
3113


3114
3115

3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932

2933
2934


2935
2936







2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956


2957
2958
2959
2960
2961
2962
2963







+
+













-
+

-
-
+
+
-
-
-
-
-
-
-
+
+


+















-
-







			else
				y -= 5
			if (s.ymn > y)
				s.ymn = y;
			y_set(s.st, false, s.x, 0, y)
		}
	}

	draw_deco_note()

	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v];
		s = p_voice.sym
		if (!s)
			continue
		set_color(s.color);
		st = p_voice.st;
//  if (st == undefined) {
//error(1, s, "BUG: no staff for voice " + p_voice.id)
//    continue
//  }

		/* draw the tuplets near the notes */
		// draw the slurs and tuplets
		for ( ; s; s = s.next) {
			if (s.tp0)
				draw_tuplet(s, 0)
			if (s.tp)
				draw_tuplet(s)
		}
		draw_all_slurs(p_voice)

		/* draw the tuplets over the slurs */
		for (s = p_voice.sym; s; s = s.next) {
			if (s.tp0)
				draw_tuplet(s, 0)
			if (s.sls || s.sl1)
				draw_slurs(s)
		}
	}
	set_color()

	/* set the top and bottom out of the staves */
	for (st = 0; st <= nstaff; st++) {
		p_st = staff_tb[st];
		top = p_st.topbar + 2;
		bot = p_st.botbar - 2
/*fixme:should handle stafflines changes*/
		for (i = 0; i < YSTEP; i++) {
			if (top > p_st.top[i])
				p_st.top[i] = top
			if (bot < p_st.bot[i])
				p_st.bot[i] = bot
		}
	}

	set_color();
	draw_deco_note()
	if (cfmt.measurenb >= 0)
		draw_measnb();
	draw_deco_staff();

	/* if any lyric, draw them now as unscaled */
	set_dscale(-1)
//	set_sscale(-1)
︙
3147
3148
3149
3150
3151
3152
3153
3154
3155


3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167

3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183

3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207


3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219




3220
3221
3222
3223

3224
3225
3226
3227


3228
3229
3230
3231

3232
3233
3234
3235
3236
3237
3238
2971
2972
2973
2974
2975
2976
2977


2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990

2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006

3007
3008
3009
3010
3011
3012
3013
3014
3015
3016







3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034




3035
3036
3037
3038




3039




3040
3041
3042
3043
3044

3045
3046
3047
3048
3049
3050
3051
3052







-
-
+
+











-
+















-
+









-
-
-
-
-
-
-








+
+








-
-
-
-
+
+
+
+
-
-
-
-
+
-
-
-
-
+
+



-
+








	set_dscale(-1);
	output = output_sav
}

/* -- draw the name/subname of the voices -- */
function draw_vname(indent) {
	var	p_voice, n, st, v, a_p, p, y, name_type,
		staff_d = []
    var	p_voice, n, st, v, a_p, p, y, name_type, h, h2,
	staff_d = []

	for (st = cur_sy.nstaff; st >= 0; st--) {
		if (cur_sy.st_print[st])
			break
	}
	if (st < 0)
		return

	// check if full or sub names
	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (!p_voice.sym)
		if (!p_voice.sym || !cur_sy.voices[v])
			continue
		st = cur_sy.voices[v].st
		if (!cur_sy.st_print[st])
			continue
		if (p_voice.new_name) {
			name_type = 2
			break
		}
		if (p_voice.snm)
			name_type = 1
	}
	if (!name_type)
		return
	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (!p_voice.sym)
		if (!p_voice.sym || !cur_sy.voices[v])
			continue
		st = cur_sy.voices[v].st
		if (!cur_sy.st_print[st])
			continue
		if (p_voice.new_name)
			delete p_voice.new_name;
		p = name_type == 2 ? p_voice.nm : p_voice.snm
		if (!p)
			continue
		if (cur_sy.staves[st].flags & CLOSE_BRACE2) {
			while (!(cur_sy.staves[st].flags & OPEN_BRACE2))
				st--
		} else if (cur_sy.staves[st].flags & CLOSE_BRACE) {
			while (!(cur_sy.staves[st].flags & OPEN_BRACE))
				st--
		}
		if (!staff_d[st])
			staff_d[st] = p
		else
			staff_d[st] += "\\n" + p
	}
	if (staff_d.length == 0)
		return
	set_font("voice");
	h = gene.curfont.size
	h2 = h / 2
	indent = -indent * .5			/* center */
	for (st = 0; st < staff_d.length; st++) {
		if (!staff_d[st])
			continue
		a_p = staff_d[st].split('\\n');
		y = staff_tb[st].y
			+ staff_tb[st].topbar * .5
				* staff_tb[st].staffscale
			+ 9 * (a_p.length - 1)
			- gene.curfont.size * .3;
		n = st
		if (cur_sy.staves[st].flags & OPEN_BRACE2) {
			+ h2 * (a_p.length - 2)

		// if instrument with 2 staves, center the voice name
		if ((cur_sy.staves[st].flags & OPEN_BRACE)
			while (!(cur_sy.staves[n].flags & CLOSE_BRACE2))
				n++
		} else if (cur_sy.staves[st].flags & OPEN_BRACE) {
			while (!(cur_sy.staves[n].flags & CLOSE_BRACE))
		 && (cur_sy.staves[st + 1].flags & CLOSE_BRACE)
				n++
		}
		if (n != st)
			y -= (staff_tb[st].y - staff_tb[n].y) * .5
		 && !staff_d[st + 1])
			y -= (staff_tb[st].y - staff_tb[st + 1].y) * .5
		for (n = 0; n < a_p.length; n++) {
			p = a_p[n];
			xy_str(indent, y, p, "c");
			y -= 18
			y -= h
		}
	}
}

// -- set the y offset of the staves and return the height of the whole system --
function set_staff() {
	var	s, i, st, prev_staff, v,
︙
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374



3375
3376
3377
3378
3379
3380
3381
3382
3383
3179
3180
3181
3182
3183
3184
3185



3186
3187
3188


3189
3190
3191
3192
3193
3194
3195







-
-
-
+
+
+
-
-







/* -- draw the staff systems and the measure bars -- */
function draw_systems(indent) {
	var	s, s2, st, x, x2, res, sy,
		staves_bar, bar_force,
		xstaff = [],
		bar_bot = [],
		bar_height = [],
		sb = [],
		db = [],
		thb = [],
		ba = [],		// bars [symbol, bottom, height]
		sb = [],
		thb = []
		gl = [],
		rn = []

	/* -- set the bottom and height of the measure bars -- */
	function bar_set() {
		var	st, staffscale, top, bot,
			dy = 0

		for (st = 0; st <= cur_sy.nstaff; st++) {
︙
3408
3409
3410
3411
3412
3413
3414
3415


3416
3417
3418
3419
3420
3421
3422
3220
3221
3222
3223
3224
3225
3226

3227
3228
3229
3230
3231
3232
3233
3234
3235







-
+
+







		if (!/[\[|]/.test(stafflines))
			return				// no line
		w = x2 - x1;
		set_sscale(st);
		ws = w / stv_g.scale

		// check if default staff
		if (cache && cache.st_l == stafflines && cache.st_ws == ws) {
		if (cache && cache.st_l == stafflines
		 && cache.st_ws == (ws | 0)) {
			xygl(x1, staff_tb[st].y, 'stdef' + cfmt.fullsvg)
			return
		}
		for (i = 0; i < l; i++, y -= 6) {
			if (stafflines[i] == '.')
				continue
			dy = 0
︙
3431
3432
3433
3434
3435
3436
3437
3438

3439
3440
3441
3442
3443

3444
3445
3446
3447

3448
3449
3450

3451
3452



3453

3454
3455
3456
3457

3458
3459
3460
3461
3462

3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477

3478


3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491

3492
3493

3494
3495
3496


3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509










3510

3511
3512
3513
3514
3515




3516

3517
3518
3519

3520

3521
3522
3523
3524
3525

3526
3527
3528
3529
3530
3531
3532


3533












3534

3535
3536

3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557

3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3244
3245
3246
3247
3248
3249
3250

3251
3252
3253
3254
3255

3256
3257
3258
3259

3260
3261
3262

3263
3264
3265
3266
3267
3268

3269
3270
3271
3272

3273
3274
3275
3276
3277

3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292

3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308

3309
3310

3311
3312


3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323




3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344

3345
3346
3347
3348
3349

3350
3351
3352
3353
3354

3355
3356
3357
3358
3359
3360


3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378

3379
3380
3381
3382
3383
3384
3385
3386











3387
3388

3389
3390
3391
3392
3393
3394
3395

























3396
3397
3398
3399
3400
3401
3402







-
+




-
+



-
+


-
+


+
+
+
-
+



-
+




-
+














-
+

+
+












-
+

-
+

-
-
+
+









-
-
-
-
+
+
+
+
+
+
+
+
+
+

+





+
+
+
+
-
+



+
-
+




-
+





-
-
+
+

+
+
+
+
+
+
+
+
+
+
+
+

+

-
+







-
-
-
-
-
-
-
-
-
-
-


-
+






-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







						'h' + ws.toFixed(1);
					dy = 0
					continue
				}
				if (ty != undefined)
					ln += '"/>\n';
				ty = stafflines[i]
				ln += '<path class="stroke ' +
				ln += '<path class="' +
					(ty == '[' ? 'slthW' : 'slW') +
					'" d="m0 ' + y + 'h' + ws.toFixed(1);
				dy = 0
			}
			ln += '"/>\n'
			ln += '"/>'
		}
		y = staff_tb[st].y
		if (!cache
		 && w == get_lwidth()) {
		 && w > get_lwidth() - 10) {
			cache = {
				st_l: stafflines,
				st_ws: ws
				st_ws: ws | 0
			}
			i = 'stdef' + cfmt.fullsvg;
			if (ln.indexOf('<path', 1) < 0)
				glyphs[i] = ln.replace('path', 'path id="' + i + '"')
			else
			glyphs[i] = '<g id="' + i + '">\n' + ln + '</g>';
				glyphs[i] = '<g id="' + i + '">\n' + ln + '\n</g>';
			xygl(x1, y, i)
			return
		}
		out_XYAB('<g transform="translate(X, Y)">\n' + ln + '</g>\n', x1, y)
		out_XYAB('<g transform="translate(X, Y)">\n' + ln + '\n</g>\n', x1, y)
	} // draw_staff()

	// draw a measure bar
	function draw_bar(s, bot, h) {
	    var	i, s2, yb,
	    var	i, s2, yb, w,
		bar_type = s.bar_type,
		st = s.st,
		p_staff = staff_tb[st],
		x = s.x

		// don't put a line between the staves if there is no bar above
		if (st != 0
		 && s.ts_prev
//fixme: 's.ts_prev.st != st - 1' when floating voice in lower staff
//	 && (s.ts_prev.type != C.BAR || s.ts_prev.st != st - 1))
		 && s.ts_prev.type != C.BAR)
			h = p_staff.topbar * p_staff.staffscale;

		s.ymx = s.ymn + h;
		set_sscale(-1);

		anno_start(s)
		if (s.color)
			set_color(s.color);

		// compute the middle vertical offset of the staff
		yb = p_staff.y + 12;
		if (p_staff.stafflines != '|||||')
			yb += (p_staff.topbar + p_staff.botbar) / 2 - 12 // bottom

		// if measure repeat, draw the '%' like glyphs
		if (s.bar_mrep) {
			set_sscale(st)
			if (s.bar_mrep == 1) {
				for (s2 = s.prev; s2.type != C.REST; s2 = s2.prev)
					;
				gl.push([s2.x, yb, st, "mrep"])
				xygl(s2.x, yb, "mrep")
			} else {
				gl.push([x, yb, st, "mrep2"])
				xygl(x, yb, "mrep2")
				if (s.v == cur_sy.top_voice)
					rn.push([x, yb + p_staff.topbar - 9,
						st, s.bar_mrep.toString()])
					xy_str(x, yb + p_staff.topbar - 9,
						s.bar_mrep.toString(), "c")
			}
		}

		if (bar_type == '||:')
			bar_type = '[|:'

		for (i = bar_type.length; --i >= 0; ) {
			switch (bar_type[i]) {
			case "|":
				if (s.bar_dotted)
					db.push(new Float32Array([x, bot, h,
						p_staff.staffscale]))
				else
				if (s.bar_dotted) {
					set_sscale(-1);
					w = (5 * p_staff.staffscale).toFixed(1);
					out_XYAB(
			'<path class="bW" stroke-dasharray="A,A" d="MX Yv-G"/>\n',
						x, bot, w, h)
				} else if (s.color) {
					out_XYAB('<path class="bW" d="MX Yv-F"/>\n',
						x, bot, h)
				} else {
					sb.push(new Float32Array([x, bot, h]))
				}
				break
			default:
//			case "[":
//			case "]":
				x -= 3;
				if (s.color)
					out_XYAB('<path class="bthW" d="MX Yv-F"/>\n',
						x + 1.5, bot, h)
				else
				thb.push(new Float32Array([x + 1.5, bot, h]))
					thb.push(new Float32Array([x + 1.5, bot, h]))
				break
			case ":":
				x -= 2;
				set_sscale(st);
				gl.push([x + 1, yb - 12, st, "rdots"])
				xygl(x + 1, yb - 12, "rdots")
				break
			}
			x -= 3
		}
		set_sscale(-1);
		set_color();
		anno_stop(s)
	} // draw_bar()

	// output all the bars
	function out_bars() {
	    var	i, b, w, bx,
		l = sb.length;
	    var	i, b, bx,
		l = ba.length

		set_font("annotation");
		if (gene.curfont.box) {
			gene.curfont.box = false
			bx = true
		}
		for (i = 0; i < l; i++) {
			b = ba[i];		// symbol, bottom, height
			draw_bar(b[0], b[1], b[2])
		}
		if (bx)
			gene.curfont.box = true

		set_sscale(-1)
		l = sb.length
		if (l) {			// single bars [x, y, h]
			output += '<path class="stroke bW" d="'
			output += '<path class="bW" d="'
			for (i = 0; i < l; i++) {
				b = sb[i];
				out_XYAB('MX Yv-F', b[0], b[1], b[2])
			}
			output += '"/>\n'
		}

		l = db.length
		if (l) {			// dotted bars [x, y, h, scale]
			for (i = 0; i < l; i++) {
				b = db[i];
				w = (5 * b[3]).toFixed(1);
				out_XYAB('<path class="stroke bW" ' +
					'stroke-dasharray="' + w + ',' + w + '" d="' +
					'MX Yv-F"/>\n', b[0], b[1], b[2])
			}
		}

		l = thb.length
		if (l) {			// thick bars [x, y, h]
			output += '<path class="stroke bthW" d="'
			output += '<path class="bthW" d="'
			for (i = 0; i < l; i++) {
				b = thb[i];
				out_XYAB('MX Yv-F', b[0], b[1], b[2])
			}
			output += '"/>\n'
		}

		l = gl.length
		if (l) {			// glyphs [x, y, staff, glyph]
			for (i = 0; i < l; i++) {
				b = gl[i];
				set_sscale(b[2]);
				xygl(b[0], b[1], b[3])
			}
		}
			
		l = rn.length
		if (l) {			// repeat number [x, y, staff, number]
			set_font("annotation");
			if (gene.curfont.box) {
				gene.curfont.box = false
				bx = true
			}
			for (i = 0; i < l; i++) {
				b = rn[i];
				set_sscale(b[2]);
				xy_str(b[0], b[1], b[3], "c")
			}
			if (bx)
				gene.curfont.box = true
		}
	} // out_bars()

	// draw_systems()
	draw_vname(indent)

	/* draw the staff, skipping the staff breaks */
	for (st = 0; st <= nstaff; st++)
︙
3648
3649
3650
3651
3652
3653
3654
3655

3656
3657
3658
3659

3660
3661
3662


3663
3664
3665
3666
3667
3668


3669
3670
3671
3672
3673
3674
3675
3455
3456
3457
3458
3459
3460
3461

3462
3463
3464


3465
3466
3467

3468
3469
3470
3471
3472
3473
3474

3475
3476
3477
3478
3479
3480
3481
3482
3483







-
+


-
-
+


-
+
+





-
+
+







				draw_staff(st, x, x2)
				if (sy.st_print[st])
					xstaff[st] = x2
			}
			cur_sy = sy;
			bar_set()
			continue
		case C.BAR:
		case C.BAR:		// display the bars after the staves
			if (s.second || s.invis || !s.bar_type)
				break
			st = s.st;
			draw_bar(s, bar_bot[st], bar_height[st])
			ba.push([s, bar_bot[s.st], bar_height[s.st]])
			break
		case C.STBRK:
			if (cur_sy.voices[s.v].range == 0) {
			if (cur_sy.voices[s.v]
			 && cur_sy.voices[s.v].range == 0) {
				if (s.xmx > 14) {

					/* draw the left system if stbrk in all voices */
					var nv = 0
					for (var i = 0; i < voice_tb.length; i++) {
						if (cur_sy.voices[i].range > 0)
						if (cur_sy.voices[i]
						  && cur_sy.voices[i].range > 0)
							nv++
					}
					for (s2 = s.ts_next; s2; s2 = s2.ts_next) {
						if (s2.type != C.STBRK)
							break
						nv--
					}
︙
3709
3710
3711
3712
3713
3714
3715
3716

3717
3718
3719
3720
3721

3722
3723
3724
3725
3726
3727
3728
3517
3518
3519
3520
3521
3522
3523

3524
3525
3526
3527
3528

3529
3530
3531
3532
3533
3534
3535
3536







-
+




-
+







		if (x < 0 || x >= realwidth)
			continue
		draw_staff(st, x, realwidth)
	}

	// and the bars
	out_bars()
	set_sscale(-1)
//	set_sscale(-1)
}

/* -- draw remaining symbols when the staves are defined -- */
// (possible hook)
function draw_symbols(p_voice) {
Abc.prototype.draw_symbols = function(p_voice) {
	var	bm = {},
		s, g, x, y, st;

//	bm.s2 = undefined
	for (s = p_voice.sym; s; s = s.next) {
		if (s.invis) {
			switch (s.type) {
︙
3810
3811
3812
3813
3814
3815
3816
3817

3818
3819
3820
3821
3822
3823
3824
3618
3619
3620
3621
3622
3623
3624

3625
3626
3627
3628
3629
3630
3631
3632







-
+







			if (s.second
			 || s.invis
			 || !staff_tb[s.st].topbar)
				break
			set_color();
			set_sscale(s.st);
			anno_start(s);
			draw_keysig(x, s);
			self.draw_keysig(x, s);
			anno_stop(s)
			break
		case C.MREST:
			set_scale(s);
			x += 32;
			anno_start(s);
			xygl(x, staff_tb[s.st].y + 12, "mrest");
︙
3868
3869
3870
3871
3872
3873
3874
3875
3876


3877
3878
3879


3880
3881
3882
3883
3884
3885
3886
3887


3888
3889

3890
3891
3892
3893
3894
3895
3896
3897
3898

3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913





3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928


3929
3930
3931
3932
3933
3934
3935
3936



3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947


3948
3949
3950
3951



3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963

3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975


3976
3977
3978
3979
3980



3981
3982
3983
3984
3985
3986
3987
3676
3677
3678
3679
3680
3681
3682


3683
3684
3685


3686
3687
3688
3689
3690
3691
3692
3693


3694
3695
3696

3697
3698
3699
3700
3701
3702
3703
3704
3705

3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716





3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734


3735
3736
3737
3738
3739
3740
3741



3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753


3754
3755
3756



3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770

3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781


3782
3783
3784
3785
3786


3787
3788
3789
3790
3791
3792
3793
3794
3795
3796







-
-
+
+

-
-
+
+






-
-
+
+

-
+








-
+










-
-
-
-
-
+
+
+
+
+













-
-
+
+





-
-
-
+
+
+









-
-
+
+

-
-
-
+
+
+











-
+










-
-
+
+



-
-
+
+
+







	}

	draw_all_deco();
	set_sscale(-1)				/* restore the scale */
}

/* -- set the tie directions for one voice -- */
function set_tie_dir(sym) {
	var s, i, ntie, dir, sec, pit, ti
function set_tie_dir(s) {
    var i, ntie, dir, sec, pit, ty

	for (s = sym; s; s = s.next) {
		if (!s.ti1)
	for ( ; s; s = s.next) {
		if (!s.tie_s)
			continue

		/* if other voice, set the ties in opposite direction */
		if (s.multi != 0) {
			dir = s.multi > 0 ? C.SL_ABOVE : C.SL_BELOW
			for (i = 0; i <= s.nhd; i++) {
				ti = s.notes[i].ti1;
				if (!((ti & 0x07) == C.SL_AUTO))
				ty = s.notes[i].tie_ty
				if (!((ty & 0x07) == C.SL_AUTO))
					continue
				s.notes[i].ti1 = (ti & C.SL_DOTTED) | dir
				s.notes[i].tie_ty = (ty & C.SL_DOTTED) | dir
			}
			continue
		}

		/* if one note, set the direction according to the stem */
		sec = ntie = 0;
		pit = 128
		for (i = 0; i <= s.nhd; i++) {
			if (s.notes[i].ti1) {
			if (s.notes[i].tie_ty) {
				ntie++
				if (pit < 128
				 && s.notes[i].pit <= pit + 1)
					sec++;
				pit = s.notes[i].pit
			}
		}
		if (ntie <= 1) {
			dir = s.stem < 0 ? C.SL_ABOVE : C.SL_BELOW
			for (i = 0; i <= s.nhd; i++) {
				ti = s.notes[i].ti1
				if (ti) {
					if ((ti & 0x07) == C.SL_AUTO)
						s.notes[i].ti1 =
							(ti & C.SL_DOTTED) | dir
				ty = s.notes[i].tie_ty
				if (ty) {
					if ((ty & 0x07) == C.SL_AUTO)
						s.notes[i].tie_ty =
							(ty & C.SL_DOTTED) | dir
					break
				}
			}
			continue
		}
		if (sec == 0) {
			if (ntie & 1) {
/* in chords with an odd number of notes, the outer noteheads are paired off
 * center notes are tied according to their position in relation to the
 * center line */
				ntie = (ntie - 1) / 2;
				dir = C.SL_BELOW
				for (i = 0; i <= s.nhd; i++) {
					ti = s.notes[i].ti1
					if (ti == 0)
					ty = s.notes[i].tie_ty
					if (!ty)
						continue
					if (ntie == 0) {	/* central tie */
						if (s.notes[i].pit >= 22)
							dir = C.SL_ABOVE
					}
					if ((ti & 0x07) == C.SL_AUTO)
						s.notes[i].ti1 =
							(ti & C.SL_DOTTED) | dir
					if ((ty & 0x07) == C.SL_AUTO)
						s.notes[i].tie_ty =
							(ty & C.SL_DOTTED) | dir
					if (ntie-- == 0)
						dir = C.SL_ABOVE
				}
				continue
			}
/* even number of notes, ties divided in opposite directions */
			ntie /= 2;
			dir = C.SL_BELOW
			for (i = 0; i <= s.nhd; i++) {
				ti = s.notes[i].ti1
				if (ti == 0)
				ty = s.notes[i].tie_ty
				if (!ty)
					continue
				if ((ti & 0x07) == C.SL_AUTO)
					s.notes[i].ti1 =
						(ti & C.SL_DOTTED) | dir
				if ((ty & 0x07) == C.SL_AUTO)
					s.notes[i].tie_ty =
						(ty & C.SL_DOTTED) | dir
				if (--ntie == 0)
					dir = C.SL_ABOVE
			}
			continue
		}
/*fixme: treat more than one second */
/*		if (nsec == 1) {	*/
/* When a chord contains the interval of a second, tie those two notes in
 * opposition; then fill in the remaining notes of the chord accordingly */
			pit = 128
			for (i = 0; i <= s.nhd; i++) {
				if (s.notes[i].ti1) {
				if (s.notes[i].tie_ty) {
					if (pit < 128
					 && s.notes[i].pit <= pit + 1) {
						ntie = i
						break
					}
					pit = s.notes[i].pit
				}
			}
			dir = C.SL_BELOW
			for (i = 0; i <= s.nhd; i++) {
				ti = s.notes[i].ti1
				if (ti == 0)
				ty = s.notes[i].tie_ty
				if (!ty)
					continue
				if (ntie == i)
					dir = C.SL_ABOVE
				if ((ti & 0x07) == C.SL_AUTO)
					s.notes[i].ti1 = (ti & C.SL_DOTTED) | dir
				if ((ty & 0x07) == C.SL_AUTO)
					s.notes[i].tie_ty =
						(ty & C.SL_DOTTED) | dir
			}
/*fixme..
			continue
		}
..*/
/* if a chord contains more than one pair of seconds, the pair farthest
 * from the center line receives the ties drawn in opposition */
︙
3998
3999
4000
4001
4002
4003
4004
4005

4006
4007

4008

4009
4010
4011


4012
4013
4014
4015
4016
4017
4018
3807
3808
3809
3810
3811
3812
3813

3814
3815
3816
3817

3818
3819
3820

3821
3822
3823
3824
3825
3826
3827
3828
3829







-
+


+
-
+


-
+
+







		if (!s)
			continue
		s = s.next
		if (!s)
			continue
		set_tie_dir(s)
		for ( ; s; s = s.next) {
			if (!s.ti1)
			if (!s.tie_s)
				continue
			if (s.notes[0].pit < 20
			 && s.notes[0].tie_ty
			 && (s.notes[0].ti1 & 0x07) == C.SL_BELOW)
			 && (s.notes[0].tie_ty & 0x07) == C.SL_BELOW)
				;
			else if (s.notes[s.nhd].pit > 24
			      && (s.notes[s.nhd].ti1 & 0x07) == C.SL_ABOVE)
			      && s.notes[s.nhd].tie_ty
			      && (s.notes[s.nhd].tie_ty & 0x07) == C.SL_ABOVE)
				;
			else
				continue
			s2 = s.next
			while (s2 && s2.type != C.NOTE)
				s2 = s2.next
			if (s2) {
︙
Changes to core/format.js.
︙
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28
29
30
31
32

33

34
35
36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
62
63
64
65
66

67
68
69
70

71
72
73

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112
113
114
115

116
117

118
119
120
121
122
123
124
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

33
34
35
36
37
38
39

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60
61
62
63
64
65
66
67

68
69
70


71
72
73

74
75
76
77
78
79









80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

95
96
97
98
99
100
101
102
103
104
105
106

107
108

109
110
111
112
113
114
115
116







+











-
+

+




-
+














-
+












-
+


-
-
+


-
+





-
-
-
-
-
-
-
-
-















-
+











-
+

-
+







// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

var	font_tb = [],
	font_st = {},	// font style => font_tb index for incomplete user fonts
	font_scale_tb = {
		serif: 1,
		serifBold: 1,
		'sans-serif': 1,
		'sans-serifBold': 1,
		Palatino: 1.1,
		monospace: 1.35
	},
	fmt_lock = {}

var cfmt = {
	annotationfont: { name: "sans-serif", size: 12 },
	annotationfont: {name: "sans-serif", size: 12 },
	aligncomposer: 1,
	beamslope: .4,			// max slope of a beam
//	botmargin: .7 * IN,		// != 1.8 * CM,
	breaklimit: .7,
	breakoneoln: true,
	cancelkey: true,
	composerfont: { name: "serifItalic", size: 14 },
	composerfont: { name: "serif", style: "italic", size: 14 },
	composerspace: 6,
//	contbarnb: false,
	dblrepbar: ':][:',
	decoerr: true,
	dynalign: true,
	footerfont: { name: "serif", size: 16 },
	fullsvg: '',
	gchordfont: { name: "sans-serif", size: 12 },
	gracespace: new Float32Array([4, 8, 11]), // left, inside, right
	graceslurs: true,
	headerfont: { name: "serif", size: 16 },
	historyfont: { name: "serif", size: 16 },
	hyphencont: true,
	indent: 0,
	infofont: {name: "serifItalic", size: 14 },
	infofont: {name: "serif", style: "italic", size: 14 },
	infoname: 'R "Rhythm: "\n\
B "Book: "\n\
S "Source: "\n\
D "Discography: "\n\
N "Notes: "\n\
Z "Transcription: "\n\
H "History: "',
	infospace: 0,
	keywarn: true,
	leftmargin: 1.4 * CM,
	lineskipfac: 1.1,
	linewarn: true,
	maxshrink: .65,
	maxshrink: .65,		// nice scores
	maxstaffsep: 2000,
	maxsysstaffsep: 2000,
	measurefirst: 1,
	measurefont: {name: "serifItalic", size: 10},
	measurefont: {name: "serif", style: "italic", size: 10},
	measurenb: -1,
	musicspace: 6,
//	notespacingfactor: 1.414,
//	notespacingfactor: 1.33
	partsfont: {name: "serif", size: 15},
	parskipfac: .4,
	partsspace: 8,
//	pageheight: 29.7 * CM,
	pagewidth: 21 * CM,
//	pos: {
//		dyn: 0,
//		gch: 0,
//		gst: 0,
//		orn: 0,
//		stm: 0,
//		voc: 0,
//		vol: 0
//	},
	printmargin: 0,
	rightmargin: 1.4 * CM,
	rbdbstop: true,
	rbmax: 4,
	rbmin: 2,
	repeatfont: {name: "serif", size: 13},
	scale: 1,
	slurheight: 1.0,
	staffsep: 46,
	stemheight: 21,			// one octave
	stretchlast: .25,
	stretchstaff: true,
	subtitlefont: {name: "serif", size: 16},
	subtitlespace: 3,
	sysstaffsep: 34,
	tempofont: {name: "serifBold", size: 15},
	tempofont: {name: "serif", weight: "bold", size: 12},
	textfont: {name: "serif", size: 16},
//	textoption: undefined,
	textspace: 14,
	titlefont: {name: "serif", size: 20},
//	titleleft: false,
	titlespace: 6,
	titletrim: true,
//	transp: 0,			// global transpose
//	topmargin: .7 * IN,
	topspace: 22,
	tuplets: [0, 0, 0, 0],
	vocalfont: {name: "serifBold", size: 13},
	vocalfont: {name: "serif", weight: "bold", size: 13},
	vocalspace: 10,
	voicefont: {name: "serifBold", size: 13},
	voicefont: {name: "serif", weight: "bold", size: 13},
//	voicescale: 1,
	writefields: "CMOPQsTWw",
	wordsfont: {name: "serif", size: 16},
	wordsspace: 5
}

function get_bool(param) {
︙
147
148
149
150
151
152
153
154
155


156
157
158
159
160
161
162
163
164
165
166
167


168
169
170



171
172
173
174
175
176
177
178

179
180
181
182
183
184

185
186
187

188
189
190

191
192

193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208






















209

210




211
212
213
214
215





216

217
218
219
220
221
222






223
224
225
226
227
228
229
230
231
232
233
234

235
236
237

238
239
240
241
242
243
244
139
140
141
142
143
144
145


146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162


163
164
165
166
167


168
169
170

171
172
173
174
175
176

177
178
179

180
181
182

183
184

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223

224
225
226
227
228
229





230
231
232
233
234
235
236






237
238
239
240
241
242


243









244



245
246
247
248
249
250
251
252







-
-
+
+












+
+

-
-
+
+
+


-
-



-
+





-
+


-
+


-
+

-
+
















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+

+
+
+
+
-
-
-
-
-
+
+
+
+
+

+
-
-
-
-
-
-
+
+
+
+
+
+
-
-

-
-
-
-
-
-
-
-
-
+
-
-
-
+








	if (!scale)
		scale = 1.1;
	font.swfac = font.size * scale
}

// %%xxxfont fontname|* [encoding] [size|*]
function param_set_font(xxxfont, param) {
    var	font, old_fn, n, a, new_name, new_fn, new_size, scale
function param_set_font(xxxfont, p) {
    var	font, n, a

	// "setfont-<n>" goes to "u<n>font"
	if (xxxfont[xxxfont.length - 2] == '-') {
		n = xxxfont[xxxfont.length - 1]
		if (n < '1' || n > '9')
			return
		xxxfont = "u" + n + "font"
	}

	// create a new font
	font = cfmt[xxxfont];
	if (!font) {			// set-font-<n> or new element
		font = {}
	} else {
		font = {
			name: "sans-serif",
			size: 12
			name: font.name,
			size: font.size,
			box: font.box
		}
	}
	font = Object.create(font);
	font.fid = font.used = undefined;
	cfmt[xxxfont] = font;

	// fill the values
	a = param.match(/\s+(no)?box(\s|$)/)
	a = p.match(/\s+(no)?box(\s|$)/)
	if (a) {				// if box
		if (a[1])
			font.box = false	// nobox
		else
			font.box = true;
		param = param.replace(a[0], a[2])
		p = p.replace(a[0], a[2])
	}

	a = param.match(/\s+class=(.*?)(\s|$)/)
	a = p.match(/\s+class=(.*?)(\s|$)/)
	if (a) {
		font.class = a[1];
		param = param.replace(a[0], a[2])
		p = p.replace(a[0], a[2])
	}
	a = param.match(/\s+wadj=(.*?)(\s|$)/)
	a = p.match(/\s+wadj=(.*?)(\s|$)/)
	if (a) {
	    if (typeof document == "undefined")	// useless if in browser
		switch (a[1]) {
		case 'none':
			font.wadj = ''
			break
		case 'space':
			font.wadj = 'spacing'
			break
		case 'glyph':
			font.wadj = 'spacingAndGlyphs'
			break
		default:
			syntax(1, errs.bad_val, "%%" + xxxfont)
			break
		}
		p = p.replace(a[0], a[2])
	}

	// the font size is the last item
	a = p.match(/\s+([0-9.]+|\*)$/)
	if (a) {
		if (a[1] != "*") {
			font.size = Number(a[1])
			font.swfac = 0
		}
		p = p.replace(a[0], "")
	}

	a = p.match(/[- ]?[nN]ormal/)
	if (a) {
		font.normal = true
		p = p.replace(a[0], '')
	}

	a = p.match(/[- ]?[bB]old/)
	if (a) {
		font.weight = "bold"
		param = param.replace(a[0], a[2])
		p = p.replace(a[0], '')
	}
	a = p.match(/[- ]?[iI]talic/)
	if (a) {
		font.style = "italic"
		p = p.replace(a[0], '')

	a = info_split(param)
	if (!a) {
		syntax(1, errs.bad_val, "%%" + xxxfont)
		return
	}
	a = p.match(/[- ]?[oO]blique/)
	if (a) {
		font.style = "oblique"
		p = p.replace(a[0], '')
	}
	switch (p) {
	new_name = a[0]
	if (new_name != "*") {
		new_name = new_name.replace('Times-Roman', 'serif');
		new_name = new_name.replace('Times', 'serif');
		new_name = new_name.replace('Helvetica', 'sans-serif');
		new_name = new_name.replace('Courier', 'monospace');
	case "":
	case "*": return
	case "Times-Roman":
	case "Times":	p = "serif"; break
	case "Helvetica": p = "sans-serif"; break
	case "Courier": p = "monospace"; break
		font.name = new_name
		font.swfac = 0
	}
	if (a.length > 1) {
		new_size = a[a.length - 1]
		if (new_size != '*') {
			new_size = Number(new_size)
			if (isNaN(new_size)) {
				syntax(1, errs.bad_val, "%%" + xxxfont)
			} else {
				font.size = new_size;
				font.swfac = 0
	font.swfac = 0
			}
		}
	}
	font.name = p
}

// get a length with a unit - return the number of pixels
function get_unit(param) {
	var v = parseFloat(param)

	switch (param.slice(-2)) {
︙
306
307
308
309
310
311
312
313

314
315
316
317
318
319
320
314
315
316
317
318
319
320

321
322
323
324
325
326
327
328







-
+







}

/* -- set the position of elements in a voice -- */
function set_pos(k, v) {		// keyword, value
	k = k.slice(0, 3)
	if (k == "ste")
		k = "stm"
	self.set_v_param("pos", k + ' ' + v)
	set_v_param("pos", k + ' ' + v)
}

// set/unset the fields to write
function set_writefields(parm) {
	var	c, i,
		a = parm.split(/\s+/)

︙
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
338
339
340
341
342
343
344

345
346
347
348
349
350
351







-







			if (cfmt.writefields.indexOf(c) >= 0)
				cfmt.writefields = cfmt.writefields.replace(c, '')
		}
	}
}

// set a voice specific parameter
// (possible hook)
function set_v_param(k, v) {
	if (curvoice) {
		self.set_vp([k + '=', v])
		return
	}
	k = [k + '=', v];
	var vid = '*'
︙
368
369
370
371
372
373
374
375

376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411

412
413

414
415
416
417

418
419
420
421
422
423
424
375
376
377
378
379
380
381

382
383
384
385





386
387
388
389
390
391
392
393
394

395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411

412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427







-
+



-
-
-
-
-









-

















-
+


+




+







		img.width = img.lm + img.rm + 150
	}
	set_posx()
} // set_page()

// set a format parameter
// (possible hook)
function set_format(cmd, param, lock) {
Abc.prototype.set_format = function(cmd, param) {
	var f, f2, v, i

//fixme: should check the type and limits of the parameter values
	if (lock) {
		fmt_lock[cmd] = true
	} else if (fmt_lock[cmd])
		return

	if (/.+font(-[\d])?$/.test(cmd)) {
		param_set_font(cmd, param)
		return
	}

	switch (cmd) {
	case "aligncomposer":
	case "barsperstaff":
	case "infoline":
	case "measurefirst":
	case "measurenb":
	case "rbmax":
	case "rbmin":
	case "shiftunison":
		v = parseInt(param)
		if (isNaN(v)) {
			syntax(1, "Bad integer value");
			break
		}
		cfmt[cmd] = v
		break
	case "microscale":
		f = parseInt(param)
		if (isNaN(f) || f < 4 || f > 256 || f % 1) {
			syntax(1, errs.bad_val, "%%" + cmd)
			break
		}
		self.set_v_param("uscale", f)
		set_v_param("uscale", f)
		break
	case "bgcolor":
	case "fgcolor":
	case "dblrepbar":
	case "titleformat":
		cfmt[cmd] = param
		break
	case "beamslope":
	case "breaklimit":			// float values
	case "lineskipfac":
	case "maxshrink":
	case "pagescale":
	case "parskipfac":
	case "scale":
	case "slurheight":
︙
455
456
457
458
459
460
461

462
463
464
465
466
467
468
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472







+







	case "dynalign":
	case "flatbeams":
	case "graceslurs":
	case "graceword":
	case "hyphencont":
	case "keywarn":
	case "linewarn":
	case "quiet":
	case "rbdbstop":
	case "singleline":
	case "squarebreve":
	case "splittune":
	case "straightflags":
	case "stretchstaff":
	case "timewarn":
︙
533
534
535
536
537
538
539
540

541
542
543
544
545
546
547
537
538
539
540
541
542
543

544
545
546
547
548
549
550
551







-
+







		set_pos(cmd, param)
		break
	case "font":
		get_font_scale(param)
		break
	case "fullsvg":
		if (parse.state != 0) {
			syntax(1, "Cannot have %%fullsvg inside a tune")
			syntax(1, errs.not_in_tune, "%%fullsvg")
			break
		}
//fixme: should check only alpha, num and '_' characters
		cfmt[cmd] = param
		break
	case "gracespace":
		v = param.split(/\s+/)
︙
621
622
623
624
625
626
627
628

629
630
631

632
633
634
635
636
637
638
639
640
641
642
643
644
645



646
647
648
649


650
651
652
653
654


655
656
657
658

659
660
661
662





663
664
665
666
667
668
669
625
626
627
628
629
630
631

632
633
634

635
636
637
638
639
640
641
642
643
644
645
646



647
648
649

650


651
652





653
654




655




656
657
658
659
660
661
662
663
664
665
666
667







-
+


-
+











-
-
-
+
+
+
-

-
-
+
+
-
-
-
-
-
+
+
-
-
-
-
+
-
-
-
-
+
+
+
+
+







		else
			cfmt[cmd] = v
		break
	case "combinevoices":
		syntax(1, "%%combinevoices is deprecated - use %%voicecombine instead")
		break
	case "voicemap":
		self.set_v_param("map", param)
		set_v_param("map", param)
		break
	case "voicescale":
		self.set_v_param("scale", param)
		set_v_param("scale", param)
		break
	default:		// memorize all global commands
		if (parse.state == 0)		// (needed for modules)
			cfmt[cmd] = param
		break
	}
}

// font stuff

// build a font style
function style_font(font) {
    var	fn = font.name,
	r = '',
function st_font(font) {
    var	n = font.name,
	r = ""
	a = fn.match(/-?[bB]old/)

	if (a) {
		r += "bold ";
	if (font.weight)
		r += font.weight + " "
		fn = fn.replace(a[0], '')
	}
	a = fn.match(/-?[iI]talic/)
	if (a) {
		r += "italic ";
	if (font.style)
		r += font.style + " "
		fn = fn.replace(a[0], '')
	}
	a = fn.match(/-?[oO]blique/)
	if (a) {
	if (n.indexOf(' ') > 0)
		r += "oblique ";
		fn = fn.replace(a[0], '')
	}
	return 'font:' + r + font.size.toFixed(1) + 'px ' + fn
		n = '"' + n.replace(/"/g, "") + '"'	// '
	return r + font.size.toFixed(1) + 'px ' + n
}
function style_font(font) {
	return 'font:' + st_font(font)
}
Abc.prototype.style_font = style_font

// build a font class
function font_class(font) {
	if (font.class)
		return 'f' + font.fid + cfmt.fullsvg + ' ' + font.class
︙
682
683
684
685
686
687
688
689
690
691
692





































693
694
695
680
681
682
683
684
685
686




687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726







-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



		}
		font_style += "\n.f" + font.fid + cfmt.fullsvg +
			" {" + style_font(font) + "}"
	}
}

// get the font of the 'xxxfont' parameter
function get_font(xxx) {
    var	fn = xxx + "font",
	font = cfmt[fn];

function get_font(fn) {
    var	font, font2, fid, st

	fn += "font"
	font = cfmt[fn]
	if (!font) {
		syntax(1, "Unknown font $1", '$' + fn[1]);
		return gene.curfont
	}

	if (!font.name || !font.size) {		// if incomplete user font
		font2 = Object.create(gene.curfont)
		if (font.name)
			font2.name = font.name
		if (font.normal) {
			if (font2.weight)	// !! don't use delete !!
				font2.weight = null
			if (font2.style)
				font2.style = null
		} else {
			if (font.weight)
				font2.weight = font.weight
			if (font.style)
				font2.style = font.style
		}
		if (font.class)
			font2.class = font.class
		if (font.size)
			font2.size = font.size
		st = st_font(font2)
		fid = font_st[st]
		if (fid != undefined)
			return font_tb[fid]
		font_st[st] = font_tb.length
		font2.fid = font2.used = undefined
		font = font2
	}
	use_font(font)
	return font
}
Changes to core/front.js.
︙
12
13
14
15
16
17
18




19
20
21
22
23
24
25
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29







+
+
+
+







// abc2svg-core is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

    var	sav = {},	// save global (between tunes) definitions
	mac = {},	// macros (m:)
	maci = {}	// first letters of macros

// translation table from the ABC draft version 2.2
var abc_utf = {
	"=D": "Đ",
	"=H": "Ħ",
	"=T": "Ŧ",
	"=d": "đ",
︙
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
62
63
64
65
66
67
68

69
70
71
72
73
74
75
76







-
+







	"5": "&#x1d12b;"
}

// convert the escape sequences to utf-8
function cnv_escape(src) {
	var	c, c2,
		dst = "",
		i, j = 0, codeUnits
		i, j = 0

	while (1) {
		i = src.indexOf('\\', j)
		if (i < 0)
			break
		dst += src.slice(j, i);
		c = src[++i]
︙
83
84
85
86
87
88
89
90

91
92
93
94
95
96
97

98
99
100
101
102

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118



119
120
121
122


123
124
125
126


127
128
129
130


131
132
133
134


135
136
137
138


139
140
141
142


143
144
145
146


147
148
149
150


151
152
153
154


155
156
157
158


159
160
161
162


163
164
165
166


167
168
169
170


171
172
173
174


175
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216

217
218

219
220
221
222
223
224
225
226
227

228
229
230
231
232
233
234
235
236
237
238
239
87
88
89
90
91
92
93

94
95
96
97




98





99


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118


119
120
121
122


123
124
125
126


127
128
129
130


131
132
133
134


135
136
137
138


139
140
141
142


143
144
145
146


147
148
149
150


151
152
153
154


155
156
157
158


159
160
161
162


163
164
165
166


167
168
169
170


171
172
173
174
175
176
177
178
179


180











181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

214
215
216
217


218
219
220
221
222
223
224







-
+



-
-
-
-
+
-
-
-
-
-
+
-
-














+
+
+


-
-
+
+


-
-
+
+


-
-
+
+


-
-
+
+


-
-
+
+


-
-
+
+


-
-
+
+


-
-
+
+


-
-
+
+


-
-
+
+


-
-
+
+


-
-
+
+


-
-
+
+


-
-
+
+







-
-
+
-
-
-
-
-
-
-
-
-
-
-
















-





+


+








-
+



-
-







				j = i + 3
				continue
			}
			break
		case 'u':
			j = Number("0x" + src.slice(i + 1, i + 5));
			if (isNaN(j) || j < 0x20) {
				dst += src[++i] + "\u0306"	// breve
				dst += src[++i] + "\u0306"	// breve accent
				j = i + 1
				continue
			}
			codeUnits = [j]
			if (j >= 0xd800 && j <= 0xdfff) {	// surrogates
				j = Number("0x" + src.slice(i + 7, i + 11));
				if (isNaN(j))
			dst += String.fromCharCode(j);
					break		// bad surrogate
				codeUnits.push(j);
				j = i + 11
			} else {
				j = i + 5
			j = i + 5
			}
			dst += String.fromCharCode.apply(null, codeUnits)
			continue
		case 't':			// TAB
			dst += ' ';
			j = i + 1
			continue
		default:
			c2 = abc_utf[src.slice(i, i + 2)]
			if (c2) {
				dst += c2;
				j = i + 2
				continue
			}

			// try unicode combine characters
			c2 = src[i + 1]
			if (!/[A-Za-z]/.test(c2))
				break
			switch (c) {
			case '`':
				dst += src[++i] + "\u0300"	// grave
				j = i + 1
				dst += c2 + "\u0300"	// grave
				j = i + 2
				continue
			case "'":
				dst += src[++i] + "\u0301"	// acute
				j = i + 1
				dst += c2 + "\u0301"	// acute
				j = i + 2
				continue
			case '^':
				dst += src[++i] + "\u0302"	// circumflex
				j = i + 1
				dst += c2 + "\u0302"	// circumflex
				j = i + 2
				continue
			case '~':
				dst += src[++i] + "\u0303"	// tilde
				j = i + 1
				dst += c2 + "\u0303"	// tilde
				j = i + 2
				continue
			case '=':
				dst += src[++i] + "\u0304"	// macron
				j = i + 1
				dst += c2 + "\u0304"	// macron
				j = i + 2
				continue
			case '_':
				dst += src[++i] + "\u0305"	// overline
				j = i + 1
				dst += c2 + "\u0305"	// overline
				j = i + 2
				continue
			case '.':
				dst += src[++i] + "\u0307"	// dot
				j = i + 1
				dst += c2 + "\u0307"	// dot
				j = i + 2
				continue
			case '"':
				dst += src[++i] + "\u0308"	// dieresis
				j = i + 1
				dst += c2 + "\u0308"	// dieresis
				j = i + 2
				continue
			case 'o':
				dst += src[++i] + "\u030a"	// ring
				j = i + 1
				dst += c2 + "\u030a"	// ring
				j = i + 2
				continue
			case 'H':
				dst += src[++i] + "\u030b"	// hungarumlaut
				j = i + 1
				dst += c2 + "\u030b"	// hungarumlaut
				j = i + 2
				continue
			case 'v':
				dst += src[++i] + "\u030c"	// caron
				j = i + 1
				dst += c2 + "\u030c"	// caron
				j = i + 2
				continue
//			case ',':
//				dst += src[++i] + "\u0326"	// comma below
//				j = i + 1
//				dst += c2 + "\u0326"	// comma below
//				j = i + 2
//				continue
			case 'c':
				dst += src[++i] + "\u0327"	// cedilla
				j = i + 1
				dst += c2 + "\u0327"	// cedilla
				j = i + 2
				continue
			case ';':
				dst += src[++i] + "\u0328"	// ogonek
				j = i + 1
				dst += c2 + "\u0328"	// ogonek
				j = i + 2
				continue
			}
			break
		}
		dst += '\\' + c;
		j = i + 1
	}
	// cleanup for XML treatment
	return (dst + src.slice(j)).replace(/<|>|  |&.*?;|&/g, function(c) {
	return dst + src.slice(j)
			switch (c) {
			case '<': return "&lt;"
			case '>': return "&gt;"
			case '&': return "&amp;"
			case '  ': return '  '		// space + nbspace
			case "&lt;":
			case "&gt;":
			case "&amp;": return c
			}
			return "&amp;" + c.slice(1)
		})
}

// ABC include
var include = 0

function do_include(fn) {
	var file, parse_sav

	if (!user.read_file) {
		syntax(1, "No read_file support")
		return
	}
	if (include > 2) {
		syntax(1, "Too many include levels")
		return
	}
	include++;
	file = user.read_file(fn)
	if (!file) {
		syntax(1, "Cannot read file '$1'", fn)
		return
	}
	include++;
	parse_sav = clone(parse);
	tosvg(fn, file);
	parse_sav.state = parse.state;
	parse = parse_sav;
	include--
}

// parse ABC code
function tosvg(in_fname,		// file name
		file,			// file content
		bol, eof) {		// beginning/end of file
	var	i, c, bol, eol, end,
	var	i, c, eol, end,
		select,
		line0, line1,
		last_info, opt, text, a, b, s,
		cfmt_sav, info_sav, char_tb_sav, glovar_sav, maps_sav,
		mac_sav, maci_sav,
		pscom,
		txt_add = '\n'		// for "+:"

	// check if a tune is selected
	function tune_selected() {
		var	re, res,
			i = file.indexOf('K:', bol)
︙
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285









286
287
288
289
290




























































































291
292
293
294
295
296
297
238
239
240
241
242
243
244

245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262







263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375







-


















-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







		else
			eol = eof
		return false
	} // tune_selected()

	// remove the comment at end of text
	function uncomment(src, do_escape) {
	    var i
		if (!src)
			return src
		if (src.indexOf('%') >= 0)
			src = src.replace(/([^\\])%.*/, '$1')
				 .replace(/\\%/g, '%');
		src = src.replace(/\s+$/, '')
		if (do_escape && src.indexOf('\\') >= 0)
			return cnv_escape(src)
		return src
	} // uncomment()

	function end_tune() {
		generate()
		if (info.W)
			put_words(info.W);
		put_history();
		blk_flush();
		parse.state = 0;		// file header
		cfmt = cfmt_sav;
		info = info_sav;
		char_tb = char_tb_sav;
		glovar = glovar_sav;
		maps = maps_sav;
		mac = mac_sav;
		maci = maci_sav;
		cfmt = sav.cfmt;
		info = sav.info;
		char_tb = sav.char_tb;
		glovar = sav.glovar;
		maps = sav.maps;
		mac = sav.mac;
		maci = sav.maci;
		parse.tune_v_opts = null;
		parse.scores = null;
		init_tune()
		img.chg = true;
		set_page();
	} // end_tune()

	// get %%voice
	function do_voice(select, in_tune) {
	    var	opt, bol
		if (select == "end")
			return		// end of previous %%voice

		// get the options
		if (in_tune) {
			if (!parse.tune_v_opts)
				parse.tune_v_opts = {};
			opt = parse.tune_v_opts
		} else {
			if (!parse.voice_opts)
				parse.voice_opts = {};
			opt = parse.voice_opts
		}
		opt[select] = []
		while (1) {
			bol = ++eol
			if (file[bol] != '%')
				break
			eol = file.indexOf('\n', eol);
			if (file[bol + 1] != line1)
				continue
			bol += 2
			if (eol < 0)
				text = file.slice(bol)
			else
				text = file.slice(bol, eol);
			a = text.match(/\S+/)
			switch (a[0]) {
			default:
				opt[select].push(uncomment(text, true))
				continue
			case "score":
			case "staves":
			case "tune":
			case "voice":
				bol -= 2
				break
			}
			break
		}
		eol = parse.eol = bol - 1
	} // do_voice()

	// apply the options to the current tune
	function tune_filter() {
	    var	o, opts, j, pc, h,
		i = file.indexOf('K:', bol)

		i = file.indexOf('\n', i);
		h = file.slice(parse.bol, i)	// tune header

		for (i in parse.tune_opts) {
			if (!parse.tune_opts.hasOwnProperty(i))
				continue
			if (!(new RegExp(i)).test(h))
				continue
			opts = parse.tune_opts[i]
			for (j = 0; j < opts.t_opts.length; j++) {
				pc = opts.t_opts[j]
				switch (pc.match(/\S+/)[0]) {
				case "score":
				case "staves":
					if (!parse.scores)
						parse.scores = [];
					parse.scores.push(pc)
					break
				default:
					self.do_pscom(pc)
					break
				}
			}
			opts = opts.v_opts
			if (!opts)
				continue
			for (j in opts) {
				if (!opts.hasOwnProperty(j))
					continue
				if (!parse.tune_v_opts)
					parse.tune_v_opts = {};
				if (!parse.tune_v_opts[j])
					parse.tune_v_opts[j] = opts[j]
				else
					parse.tune_v_opts[j] =
						parse.tune_v_opts[j].
								concat(opts[j])
			}
		}
	} // tune_filter()

	// export functions and/or set module hooks
	if (abc2svg.modules
	 && (abc2svg.modules.hooks.length || abc2svg.modules.g_hooks.length))
		set_hooks()

	// initialize
	parse.file = file;		// used for errors
︙
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376

377

378
379
380

381
382
383

384
385
386

387
388
389
390
391
392


393
394
395
396
397

398
399
400
401

402
403
404
405
406
407
408
409
410

411
412
413

414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433

434
435
436

437
438

439
440

441
442
443
444
445
446
447
448

449
450
451
452
453
454



455
456
457
458
459



460
461
462
463
464
465
466
467
468
469
470
471
472
473
474








475
476

477
478
479
480
481
482
483
484
485
486
487




488
489















490
491
492
493
494
495
496
438
439
440
441
442
443
444









445
446

447
448


449
450
451

452
453
454

455
456
457
458
459


460
461
462
463
464
465

466
467
468
469

470
471
472
473
474
475
476
477
478

479
480
481

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497



498

499
500
501

502
503

504
505

506



507
508
509
510

511
512





513
514
515





516
517
518

519
520
521
522
523
524
525
526
527
528
529
530


531
532
533
534
535
536
537
538
539

540
541
542






543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573







-
-
-
-
-
-
-
-
-

+
-
+

-
-
+


-
+


-
+




-
-
+
+




-
+



-
+








-
+


-
+















-
-
-

-
+


-
+

-
+

-
+
-
-
-




-
+

-
-
-
-
-
+
+
+
-
-
-
-
-
+
+
+
-












-
-
+
+
+
+
+
+
+
+

-
+


-
-
-
-
-
-



+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







			pscom = true
		}

		// pseudo-comments
		if (pscom) {
			pscom = false;
			bol += 2		// skip %%/I:
			while (1) {
				switch (file[bol]) {
				case ' ':
				case '\t':
					bol++
					continue
				}
				break
			}
			text = file.slice(bol, eol)
			a = text.match(/([^\s]+)\s*(.*)/)
			if (!text || text[0] == '%')
			if (!a || a[1][0] == '%')
				continue
			a = text.split(/\s+/, 2)
			switch (a[0]) {
			switch (a[1]) {
			case "abcm2ps":
			case "ss-pref":
				parse.prefix = a[1]	// may contain a '%'
				parse.prefix = a[2]	// may contain a '%'
				continue
			case "abc-include":
				do_include(uncomment(a[1]))
				do_include(uncomment(a[2]))
				continue
			}

			// beginxxx/endxxx
			if (a[0].slice(0, 5) == 'begin') {
				b = a[0].substr(5);
			if (a[1].slice(0, 5) == 'begin') {
				b = a[1].substr(5);
				end = '\n' + line0 + line1 + "end" + b;
				i = file.indexOf(end, eol)
				if (i < 0) {
					syntax(1, "No $1 after %%$2",
							end.slice(1), a[0]);
							end.slice(1), a[1]);
					parse.eol = eof
					continue
				}
				self.do_begin_end(b, uncomment(a[1]),
				self.do_begin_end(b, uncomment(a[2]),
					file.slice(eol + 1, i).replace(
						new RegExp('^' + line0 + line1, 'gm'),
										''));
				parse.eol = file.indexOf('\n', i + 6)
				if (parse.eol < 0)
					parse.eol = eof
				continue
			}
			switch (a[0]) {
			switch (a[1]) {
			case "select":
				if (parse.state != 0) {
					syntax(1, "%%select ignored")
					syntax(1, errs.not_in_tune, "%%select")
					continue
				}
				select = uncomment(text.slice(7))
				if (select[0] == '"')
					select = select.slice(1, -1);
				if (!select) {
					delete parse.select
					continue
				}
				select = select.replace(/\(/g, '\\(');
				select = select.replace(/\)/g, '\\)');
//				select = select.replace(/\|/g, '\\|');
				parse.select = new RegExp(select, 'm')
				continue
			case "tune":
				syntax(1, "%%tune not treated yet")
				continue
			case "voice":
				if (parse.state != 0) {
					syntax(1, "%%voice ignored")
					syntax(1, errs.not_in_tune, "%%tune")
					continue
				}
				select = uncomment(text.slice(6))
				select = uncomment(a[2])

				/* if void %%voice, free all voice options */
				// if void %%tune, free all tune options
				if (!select) {
					if (parse.cur_tune_opts)
					parse.tune_opts = {}
						parse.cur_tune_opts.voice_opts = null
					else
						parse.voice_opts = null
					continue
				}
				
				if (select == "end")
					continue	/* end of previous %%voice */
					continue	// end of previous %%tune

				/* get the voice options */
				if (parse.cur_tune_opts) {
					if (!parse.cur_tune_opts.voice_opts)
						parse.cur_tune_opts.voice_opts = {}
					opt = parse.cur_tune_opts.voice_opts
				if (!parse.tune_opts)
					parse.tune_opts = {};
				parse.tune_opts[select] = opt = {
				} else {
					if (!parse.voice_opts)
						parse.voice_opts = {}
					opt = parse.voice_opts
				}
						t_opts: []
//						v_opts: {}
					};
				opt[select] = []
				while (1) {
					bol = ++eol
					if (file[bol] != '%')
						break
					eol = file.indexOf('\n', eol);
					if (file[bol + 1] != line1)
						continue
					bol += 2
					if (eol < 0)
						text = file.slice(bol)
					else
						text = file.slice(bol, eol);
					a = text.match(/\S+/)
					switch (a[0]) {
					a = text.match(/([^\s]+)\s*(.*)/)
					switch (a[1]) {
					case "tune":
						break
					case "voice":
						do_voice(uncomment(a[2],
								true), true)
						continue
					default:
						opt[select].push(
						opt.t_opts.push(
							uncomment(text, true))
						continue
					case "score":
					case "staves":
					case "tune":
					case "voice":
						bol -= 2
						break
					}
					break
				}
				if (parse.tune_v_opts) {
					opt.v_opts = parse.tune_v_opts;
					parse.tune_v_opts = null
				}
				parse.eol = bol - 1
				continue
			case "voice":
				if (parse.state != 0) {
					syntax(1, errs.not_in_tune, "%%voice")
					continue
				}
				select = uncomment(a[2])

				/* if void %%voice, free all voice options */
				if (!select) {
					parse.voice_opts = null
					continue
				}
				
				do_voice(select)
				continue
			}
			self.do_pscom(uncomment(text, true))
			continue
		}

		// music line (or free text)
		if (line1 != ':' || !/[A-Za-z+]/.test(line0)) {
︙
534
535
536
537
538
539
540
541

542
543
544
545
546
547
548






549
550


551
552
553
554
555
556

557
558
559
560
561
562
563
611
612
613
614
615
616
617

618







619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642







-
+
-
-
-
-
-
-
-
+
+
+
+
+
+


+
+






+







				eol = file.indexOf('\nX:', parse.eol)
				if (eol < 0)
					eol = eof;
				parse.eol = eol
				continue
			}

			cfmt_sav = clone(cfmt);
			sav.cfmt = clone(cfmt);
			cfmt.pos = clone(cfmt.pos);
			info_sav = clone(info, 1);
			char_tb_sav = clone(char_tb);
			glovar_sav = clone(glovar);
			maps_sav = clone(maps, 1);
			mac_sav = clone(mac);
			maci_sav = new Int8Array(maci);
			sav.info = clone(info, 2)	// (level 2 for info.V[])
			sav.char_tb = clone(char_tb);
			sav.glovar = clone(glovar);
			sav.maps = clone(maps, 1);
			sav.mac = clone(mac);
			sav.maci = clone(maci);
			info.X = text;
			parse.state = 1			// tune header
			if (parse.tune_opts)
				tune_filter()
			continue
		case 'T':
			switch (parse.state) {
			case 0:
				continue
			case 1:
			case 2:
				if (info.T == undefined)	// (keep empty T:)
					info.T = text
				else
					info.T += "\n" + text
				continue
			}
			s = new_block("title");
︙
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600

601
602
603
604
605
606
607
663
664
665
666
667
668
669



670
671
672
673
674
675

676
677
678
679
680
681
682
683







-
-
-






-
+







			break

		case 'm':
			if (parse.state >= 2) {
				syntax(1, errs.ignored, line0)
				continue
			}
			if ((!cfmt.sound || cfmt.sound != "play")
			 && cfmt.writefields.indexOf(line0) < 0)
				break
			a = text.match(/(.*?)[= ]+(.*)/)
			if (!a || !a[2]) {
				syntax(1, errs.bad_val, "m:")
				continue
			}
			mac[a[1]] = a[2];
			maci[a[1].charCodeAt(0)] = 1	// first letter
			maci[a[1][0]] = true	// first letter
			break

		// info fields in tune body only
		case 's':
			if (parse.state != 3
			 || cfmt.writefields.indexOf(line0) < 0)
				break
︙
617
618
619
620
621
622
623
624

625
626
627
628
629
630
631
693
694
695
696
697
698
699

700
701
702
703
704
705
706
707







-
+







				last_info = line0
				continue
			}
			break
		case '|':			// "|:" starts a music line
			if (parse.state < 2)
				continue
			parse.line.buffer = uncomment(file.slice(bol, eol), true);
			parse.line.buffer = text
			parse_music_line()
			continue
		default:
			if ("ABCDFGHNOSZ".indexOf(line0) >= 0) {
				if (parse.state >= 2) {
					syntax(1, errs.ignored, line0)
					continue
︙
Changes to core/gchord.js.
︙
161
162
163
164
165
166
167
168
169
170



171
172
173
174




175
176
177
178
179


180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

196
197

198
199
200
201

202
203
204
205
206
207




208
209
210
211
212
213
214

215
216
217
218
219
220
221
222
223
224
225
226


227
228
229
230

231
232
233

234
235
236

237
238
239
240
241
242

243
244
245
246
247
248
249
161
162
163
164
165
166
167



168
169
170
171



172
173
174
175





176
177

178
179
180
181
182
183
184
185
186






187


188




189






190
191
192
193







194












195
196
197
198
199

200

201

202
203
204

205
206
207
208
209
210

211
212
213
214
215
216
217
218







-
-
-
+
+
+

-
-
-
+
+
+
+
-
-
-
-
-
+
+
-









-
-
-
-
-
-
+
-
-
+
-
-
-
-
+
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+



-
+
-

-
+


-
+





-
+







	}
}

// transpose a chord symbol
var	note_names = "CDEFGAB",
	acc_name = ["bb", "b", "", "#", "##"]

	function gch_tr1(p, i2) {
	    var	new_txt,
			n, i1, i3, i4, ix, a, ip, ip2
	function gch_tr1(p, transp) {
	    var	i, o, n, a, ip, b40,
		csa = p.split('/')

		/* main chord */
		n = note_names.indexOf(p[0])
		if (n < 0) {
		for (i = 0; i < csa.length; i++) {	// main and optional bass
			p = csa[i];
			o = p.search(/[ABCDEFG]/);
			if (o < 0)
			if (p[0] != '/')
				return p
		}

		ip = 1
				continue		// strange chord symbol!
			ip = o + 1
		if (n >= 0) {		// if some chord
			a = 0
			while (p[ip] == '#') {
				a++;
				ip++
			}
			while (p[ip] == 'b') {
				a--;
				ip++
			}
//			if (p[ip] == '=')
//				ip++
			i3 = cde2fcg[n] + i2 + a * 7;
			i4 = cgd2cde[(i3 + 16 * 7) % 7];	// note
			i1 = ((((i3 + 22) / 7) | 0) + 159) % 5;	// accidental
			new_txt = note_names[i4] + acc_name[i1]
			n = note_names.indexOf(p[o]) + 16
		}

			b40 = (abc2svg.pab40(n, a) + transp + 200) % 40
		ip2 = p.indexOf('/', ip)	// skip 'm'/'dim'..
		if (ip2 < 0)
			return new_txt + p.slice(ip);

			b40 = abc2svg.b40k[b40]
		/* bass */
		n = note_names.indexOf(p[++ip2])
		if (n < 0)
			return new_txt + p.slice(ip);

		new_txt += p.slice(ip, ip2);
			csa[i] = p.slice(0, o) +
					note_names[abc2svg.b40_p[b40]] +
					acc_name[abc2svg.b40_a[b40] + 2] +
					p.slice(ip)
		a = 0
		if (p[++ip2] == '#') {
			a++
			if (p[++ip2] == '#') {
				a++;
				ip2++
			}
		}
		} else if (p[ip2] == 'b') {
			a--
			if (p[++ip2] == 'b') {
				a--;
				ip2++
			}
		}
		i3 = cde2fcg[n] + i2 + a * 7;
		i4 = cgd2cde[(i3 + 16 * 7) % 7];	// note
		i1 = ((((i3 + 22) / 7) | 0) + 159) % 5;	// accidental
		return new_txt + note_names[i4] + acc_name[i1] + p.slice(ip2)
	} // get_tr1
		return csa.join('/')
	} // gch_tr1

function gch_transp(s) {
    var	gch,
		i = 0,
	i = s.a_gch.length
		i2 = curvoice.ckey.k_sf - curvoice.okey.k_sf

	for (i = 0; i < s.a_gch.length; i++) {
	while (--i >= 0) {
		gch = s.a_gch[i]
		if (gch.type == 'g')
			gch.text = gch_tr1(gch.text, i2)
			gch.text = gch_tr1(gch.text, curvoice.vtransp)
	}
}

// -- build the chord indications / annotations --
// (possible hook)
function gch_build(s) {
Abc.prototype.gch_build = function(s) {

	/* split the chord indications / annotations
	 * and initialize their vertical offsets */
	var	gch, wh, xspc, ix,
		pos = curvoice.pos.gch == C.SL_BELOW ? -1 : 1,
		y_above = 0,
		y_below = 0,
︙
266
267
268
269
270
271
272
273

274
275
276
277
278
279

280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300

301
302
303
304
305
306
307
235
236
237
238
239
240
241

242
243
244
245
246
247
248
249
250
251
252


253



254
255
256
257
258
259
260
261
262
263
264

265
266
267
268
269
270
271
272







-
+






+



-
-

-
-
-











-
+







			if (cfmt.chordnames) {
				gch.otext = gch.text;	// save for %%diagram
				gch.text = gch.text.replace(/A|B|C|D|E|F|G/g,
					function(c){return cfmt.chordnames[c]})
				if (cfmt.chordnames.B == 'H')
					gch.text = gch.text.replace(/Hb/g, 'Bb')
			}
			gch.text = gch.text.replace(/##|#|=|bb|b/g,
			gch.text = gch.text.replace(/##|#|=|bb|b|  /g,
				function(x) {
					switch (x) {
					case '##': return "&#x1d12a;"
					case '#': return "\u266f"
					case '=': return "\u266e"
					case 'b': return "\u266d"
					case '  ': return '  '
					}
					return "&#x1d12b;"
				});
			if (gch.font.box)
				gch.box = true
		} else {
			gch.text = cnv_escape(gch.text);
			if (gch.font.box)
				gch.box = true
			if (gch.type == '@'
			 && !user.anno_start && !user.anno_stop) {
				gch.wh = [0, 0]
				continue		/* no width */
			}
		}

		/* set the offsets and widths */
		set_font(gch.font);
		wh = strwh(gch.text);
		gch.wh = wh
		if (gch.box)
		if (gch.font.box)
			wh[1] += 4
		switch (gch.type) {
		case '@':
			break
		case '^':			/* above */
			xspc = wh[0] * GCHPRE
			if (xspc > 8)
︙
367
368
369
370
371
372
373
374

375
376
377
378
379
380
381
382
383
384
385
386
387
388
389


390
391
392
393
394
395
396
397
398
399
400
401

402
403
404
405
406
407
408
332
333
334
335
336
337
338

339
340
341
342
343
344
345
346
347
348
349
350
351
352


353
354
355
356
357
358
359
360
361
362
363
364
365

366
367
368
369
370
371
372
373







-
+













-
-
+
+











-
+







	}
}

// -- draw the chord symbols and annotations
// (the staves are not yet defined)
// (unscaled delayed output)
// (possible hook)
function draw_gchord(s, gchy_min, gchy_max) {
Abc.prototype.draw_gchord = function(s, gchy_min, gchy_max) {
    var	gch, text, ix, x, y, y2, hbox, h, y_above, y_below,

	// adjust the vertical offset according to the chord symbols
	w = 0,
		yav = s.dur ?
			(((s.notes[s.nhd].pit + s.notes[0].pit) >> 1) - 18) * 3 :
			12		// fixed offset on measure bars

	for (ix = 0; ix < s.a_gch.length; ix++) {
		gch = s.a_gch[ix]
		if (gch.wh[0] > w)
			w = gch.wh[0]
	}
	y_above = y_get(s.st, 1, s.x - 2, w);
	y_below = y_get(s.st, 0, s.x - 2, w)
	y_above = y_get(s.st, 1, s.x - 3, w);
	y_below = y_get(s.st, 0, s.x - 3, w)
	if (y_above < gchy_max)
		y_above = gchy_max
	if (y_below > gchy_min)
		y_below = gchy_min;

	set_dscale(s.st);
	for (ix = 0; ix < s.a_gch.length; ix++) {
		gch = s.a_gch[ix];
		use_font(gch.font);
		set_font(gch.font);
		h = gch.font.size;
		hbox = gch.box ? 2 : 0;
		hbox = gch.font.box ? 2 : 0;
		w = gch.wh[0];
		x = s.x + gch.x;
		text = gch.text
		switch (gch.type) {
		case '_':			/* below */
			y = gch.y + y_below;
			y_set(s.st, 0, x, w, y - hbox)
︙
Changes to core/lyrics.js.
︙
495
496
497
498
499
500
501
502

503
504
505
506
507
508
509
495
496
497
498
499
500
501

502
503
504
505
506
507
508
509







-
+







	return y / sc
}

// -- draw all the lyrics --
/* (the staves are not yet defined) */
function draw_all_lyrics() {
	var	p_voice, s, v, nly, i, x, y, w, a_ly, ly,
		lyst_tb = new Array(nstaff),
		lyst_tb = new Array(nstaff+ 1),
		nv = voice_tb.length,
		h_tb = new Array(nv),
		nly_tb = new Array(nv),
		above_tb = new Array(nv),
		rv_tb = new Array(nv),
		top = 0,
		bot = 0,
︙
Added core/mei.js.









































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// abc2svg - mei.js - MEI front-end
//
// Copyright (C) 2019 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg-core is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

// convert the xml source into a tree
function xml2tree(xml) {
    var	i, j, item, n, no, an,
	e = 0,
	stack = [],
	root = {
		name: "root"
	},
	o = root

	while (1) {
		i = xml.indexOf('<', e)		// start of tag
		if (i < 0)
			break
		if (i > e + 1) {		// if some content
			item = xml.slice(e + 1, i)
				.replace(/\s*/, '') // remove left spaces
			if (item) {
				if (!o.children)
					o.children = []
				o.children.push({
					name: "text",
					content: item
				})
			}
		}
		e = xml.indexOf('>', i)		// end
		if (e < 0)
//fixme: error
			return root

		switch (xml[i + 1]) {
		case '?':
			continue		// XML header
		case '!':
			e = xml.indexOf('-->', i) // XML comment
			if (e < 0)
				return root
			continue
		case '/':
			item = xml.slice(i + 2, e)
			if (item != o.name) {
				error(1, null, 'Found tag </' + item +
					'> instead of </'+o.name + '>')
				continue
			}
			o = stack.pop()
			if (!o)
				error(1, null, 'No start tag "' + item + '"')
			continue
		}
		item = xml.slice(i + 1, e)
		n = item.match(/(\w+)[\s]*([^]*)/)
		no = {
			name: n[1],
			ix: i
		}
		if (!o.children)
			o.children = []
		o.children.push(no)
		if (n[2].slice(-1) != '/') {	// if some content
			stack.push(o)
			o = no
		} else {
			n[2] = n[2].slice(0, -1)
		}
		if (!n[2])
			continue
		n = n[2].match(/[^\s"=]+=?|".*?"/g)	// "
		for (j = 0; j < n.length; j += 2) {
			an = n[j].slice(0, -1)
			no[an] = n[j + 1].slice(1, -1)
		}
	}
	return root
}

// convert MEI to music suitable to the abc2svg generator
Abc.prototype.mei2mus = function(mei) {
    var	curr = {	// various elements while parsing the MEI source
		beam: 0,
		delayed: {},
		fnt: {},
		ftn: 0,
		ids: {},
		meastim: 0,
		mu: C.BLEN / 4,
		pt: .01,		// default unit ! en cm
		tp: []			// tuplets
	},
	acc_ty = {
		"ff": -2,
		"f": -1,
		"s": 1,
		"x": 2,
//		"ss": 2,
		"n": 3
	},
	bar_ty = {
		dashed: ".|",
		dbl: "||",
		dbldashed: ".||",
		dbldotted: ".||",
		dotted: ".|",
		end: "|]",
		invis: "[]",
		rptstart: "|:",
		rptboth: "::",
		rptend: ":|",
		single: "|"
	},
	fn = "",

	// --- functions associated to the start of MIE tags
	fns = {

	// accidental
	accid: function(tag) {
	    var	s = curr.s,
		note = s.notes[s.nhd]

		if (acc_ty[tag.accid])
			note.acc = acc_ty[tag.accid]
	}, // accid()

	arpeg: function(tag) {
	    var	s
		if (!tag.plist)
			return
// plist = list of notes of the chord
		s = tag.plist.split(/\s+/)[0].slice(1)	// top note
		s = curr.ids[s]			// always defined
		s = s.s				// chord
		add_dcn(s, "arpeggio")
	}, // arpeg()

	// articulation inside chord or note
	artic: function(tag) {
		artic(curr.chord || curr.s, tag)
	}, // artic()

	barLine: function(tag) {
	    var	s, ty

		if (tag.form)
			ty = bar_ty[tag.form]
		if (!ty)
			ty = "|"
		s = {
			type: C.BAR,
			istart: tag.ix,
			bar_type: ty,
			dur: 0,
			multi: 0
		}
		sym_link(s)
		if (ty[0] == '.') {
			s.bar_type = ty.slice(1)
			s.bar_dotted = true
		}
	}, // barLine()

	// start of beam
	beam: function(tag) {
		curr.beam = 1
	}, // beam()

	// beam crossing staves or measure bars
	beamSpan: function(tag) {
	    var	s = get_ref(tag),
		s2 = get_ref(tag, s)
		if (!s || !s2)
			return
		s.beam_st = true
		if (s.st == s2.st) {
			while (s != s2) {
				if (s.type == C.BAR)
					s.beam_on = true
			}
//		} else {
//fixme: beams on 2 voices do not work!
		}
		s2.beam_end = true
	}, // beamSpan()

	breath: function(tag) {
	    var	s = get_ref(tag)
		if (!s)
			return
		if (s.s)		// !
			s = s.s
//		tag.place above/below
		add_dcn(s, "breath")
	}, // breath()

	// start of chord
	chord: function(tag) {
	    var	dur = durcnv(tag),
		s = {
			type: C.NOTE,
			istart: tag.ix,
			stem: 0,
			multi: 0,
			nhd: -1,
			xmx: 0,
			dur: dur,
			dur_orig: dur,
			notes: []
		}
		switch (tag["stem.dir"]) {
		case "up": s.stem = 1; break
		case "down": s.stem = -1; break
		}
		switch (curr.beam) {
		case 0:
			s.beam_st = true
			s.beam_end = true
			break
		case 1:
			s.beam_st = true
			curr.beam = 2
			break
		}
//if (0) {
//		switch (tag.tie) {
//		case "i":
//		case "m":
//			curvoice.tie_s = s
//			break
//		}
//}
		if (tag.artic)
			artic(s, tag)
//fixme:cross-staff chord
//		tag.stem.with
//fixme: staff change?
//		tag.staff
		if (tag.grace)
			s.grace = true
		sym_link(s)
		curr.chord = s
	}, // chord()

	clef: function(tag) {
	    var	s = new_clef(tag.shape + tag.line)
		if (s)
			get_clef(s)
	}, // clef()

	composer: set_text,

	// annotation
	dir: set_text,

	// start of division
	div: set_text,

	dynam: set_text,

	// variant ending
	ending: function(tag) {
		curr.ending = tag.n	// "1", "2"
	}, // ending()

	// single element of figured bass - in <harm>
	f: function(tag) {
		if (curr.text)
			curr.text += "\n"
	}, // f()

//	figured bass - in <harm>
//	fb: function(tag) {
//	}, // fb()

	fileDesc: function(tag) {
		return true	// skip
	}, // fileDesc()

	fing: set_text,

	fermata: function(tag) {
	    var	s = get_ref(tag)
		if (!s)
			return
		if (s.s)
			s = s.s
//		tag.place above/below
		add_dcn(s, tag.form != "inv" ? "fermata" : "invertedfermata")
	}, // fermata()

	// group of grace notes
	graceGrp: function(tag) {
//		tag.attach	pre
	},

	// crescendo, diminuendo
	hairpin: function(tag) {
	    var	s = get_ref(tag),
		s2 = get_ref(tag, s)

		if (!s || !s2)
			return
		if (s.s)			// on notes!
			s = s.s
		if (s2.s)
			s2 = s2.s

		switch (tag.place) {
		case "above": s.pos.dyn = C.SL_ABOVE; break
		case "below": s.pos.dyn = C.SL_BELOW; break
		}
		switch (tag.form) {
		default:
//		case "cres":
			add_dcn(s, "<(")
			add_dcn(s2, "<)")
			break
		case "dim":
			add_dcn(s, ">(")
			add_dcn(s2, ">)")
			break
		}
	}, // hairpin()

	// chord symbol (harmony)
	harm: set_text,

	// start of incipit
	incip: function(tag) {
//fixme: skip
return true
//		tosvg(fn, "X:1")
//		parse.state = 1		// inside tune
	}, // incip()

	// start of line or start of line group
	l: function(tag) {
		if (curr.text)
			curr.text += "\n"
	}, // l()

	// start of label or labelAbbr
	label: function(tag) {
		if (curr.st == undefined	// not in staff definition
		 && !curr.staffGrp)		// not in staffGrp
			return true
		set_text(tag)
	}, // label()

	// layer (voice) - must be inside staff
	layer: function(tag) {
	    var	v, p_v, old_sy, s, vid, t,
		st = curr.st,			// ABC staff number
		staff = par_sy.staves[st],
		n = tag.n

		// set the (ABC) start of tune
		if (parse.state < 2) {
			staves_found = 0// don't generate a dummy staff system
			if (staff["meter.sym"] || staff["meter.count"]) {
				new_meter(metercnv(staff))
				curr.mu = meteru(staff)
				delete staff["meter.sym"]
				delete staff["meter.count"]
			}
			get_key(staff["key.sig"] ? keycnv(staff) : "C")
		}

		// build the voice ID = "#staff[#layer]"
		vid = (curr.st + 1).toString()
		if (n && n != "1")
			vid += n

		// if the voice is not in the current staff system, update it
		p_v = new_voice(vid)

		// if a new voice, do a full definition
		if (p_v.new) {
			v = p_v.v
			p_v.st = p_v.cst = st
			p_v.time = curr.meastim

//			// if inside tune, add a new staff system
//			if (voice_tb[v].time != 0 && !curr.newsy) {
//				curr.newsy = true
//				old_sy = par_sy
//				par_sy = clone(par_sy)
//				par_sy.voices = clone(par_sy.voices)
//				par_sy.staves = clone(par_sy.staves)
//				old_sy.next = par_sy
//			}

			if (!par_sy.voices[0])
				par_sy.top_voice = v	// first voice
			if (n)
				n = Number(n) - 1
			else
				n = 0
			par_sy.voices[v] = {
				st: st
//				range: // set below
//				sep:
//				maxsep:
			}

			// sort the voices
			if (!par_sy.rv)
				par_sy.rv = []
			par_sy.rv.push({
				v: v,
				r: st * 10 + n
			})
			par_sy.rv.sort(function(n1, n2) {
				return n1.r - n2.r
			})
			for (n = 0; n < par_sy.rv.length; n++) {
				v = par_sy.rv[n].v
				par_sy.voices[v].range = n
			}
			v = p_v.v
			while (--v >= 0) {
				if (par_sy.voices[v].st == st) {
					p_v.second =
						par_sy.voices[p_v.v].second = true
					break
				}
			}
		}
		if (tag.label) {
			t = tag
		} else if (staff.label) {
			t = staff.label
			staff.label = null
		}
		if (t) {
			p_v.nm = t.label
			p_v.new_name = true
			if (t["label.abbr"])
				p_v.snm = t["label.abbr"]
		}

		get_voice(vid)

		if (staff["clef.shape"]) {		// if clef change
			s = new_clef(clefcnv(staff))
			delete staff["clef.shape"]	// one clef per staff
			if (s) {
				get_clef(s)

				// put the clef before the measure bar
				if (s.prev && s.prev.bar_type) {
					s.next = s.prev
					if (s.prev.prev)
						s.prev.prev.next = s
					else
						s.p_v.sym = s
					s.prev = s.prev.prev
					s.next.prev = s
					s.next.next = null
					curvoice.last_sym = s.next
				}
			}
		}

//if (0) {
//		// if a new voice inside tune, add a measure bar
//		if (new_v)
//			sym_link({
//				type: C.BAR,
//				meas: curr.meas,
////fixme: the bar type depends on the previous measure
//				bar_type: "|",
//				dur: 0,
//				multi: 0
//			})
//}
	}, // layer()

	lb: function(tag) {
//fixme: here for chord symbol/annotation
// may be for page header/footer or ...
//		curr.text += ";"
		curr.text += "\n"
	}, // lb()

	mei: function(tag) {
		if (!tag.meiversion || tag.meiversion[0] != "4")
			error(0, null, "Bad MEI version " + tag.meiversion)
	},

	meter: function(tag) {
	    var	m
		if (tag.count) {
			m = tag.count
			if (tag.unit)
				m += "/" + tag.unit
			new_meter(m)
			m = C.BLEN / (tag.unit ? Number(tag.unit) : 1)
		} else switch (tag.sym) {
			case "common": new_meter("C"); m = 4; break
			case "cut": new_meter("C|"); m = 2; break
		}
		if (m)
			curr.mu = C.BLEN / m
	}, // meter()

	mRest: function(tag) {
	    var	s = {
			type: C.REST,
			istart: tag.ix,
			stem: 0,
			multi: 0,
			nhd: 0,
			xmx: 0,
			dur: curvoice.wmeasure,
			dur_orig: curvoice.wmeasure
		}
//		tag.cutout
		sym_link(s)
		if (curvoice.wmeasure == 1)		// if no meter
			curr.mrest = true
		if (par_sy.staves[s.st].invis)
			s.invis = true
	}, // mRest()

	// new measure
	measure: function(tag) {
		// anacrusis when n="0" type="upbeat"
		curr.meas = tag.n			// for errors
	}, // measure()

	mordent: function(tag) {
//fixme: handle note in chord
	    var	ty,
		s = get_ref(tag)

		if (!s)
			return
		if (s.s)
			s = s.s
//fixme: handle mordent in chord

		switch (tag.form) {
		default:
//		case "upper":
				ty = "uppermordent"; break
		case "lower": ty = "lowermordent"; break
		}
		add_dcn(s, ty)
	}, // mordent()

	multiRest: function(tag) {
	    var	dur = curvoice.wmeasure * tag.num,
		s = {
			type: C.MREST,
			istart: tag.ix,
			stem: 0,
			multi: 0,
			nhd: 0,
			xmx: 0,
			dur: dur,
			dur_orig: dur
		}
		sym_link(s)
	}, // multiRest()

	// start of the tune
	music: function(tag) {
		info.X = "1"
		parse.fname = "mei"
		parse.state = 1		// inside tune
	}, // music()

	note: function(tag) {
	    var	note, s, acc, pit, dur, v, i, vi, gr, ref, st,
		oct = tag.oct,
		id = tag["xml:id"]

		if (!tag.pname) {
			if (tag["pname.ges"]) {
				tag.pname = tag["pname.ges"]	//fixme
//				return
			} else if (tag.sameas) {
				ref = tag.sameas
				if (ref[0] == '#')
					ref = ref.slice(1)
				s = clone(curr.ids[ref])	// always defined
				if (!s.type) {			// if in chord
					note = s
					s = null
				} else {
					s.notes = clone(s.notes)
					s.notes[0] = note = clone(s.notes[0])
					note = s
				}
			} else {
// may be
// <note cue="true" head.shape="slash" stem.dir="up"/>
				return
			}
		}

		if (!note) {
			if (!oct)
				oct = curr.oct
			note = {
				pit: "cdefgab".indexOf(tag.pname.toLowerCase()) +
					oct * 7 - 12,
				shhd: 0,
				shac: 0
			}
		}
		if (curr.chord) {
			s = curr.chord
			dur = s.dur
//fixme: tie in chord?
//			if (s.tie_s)
//				note.tie_ty = C.SL_AUTO
			s.notes[++s.nhd] = note
		} else {
			dur = durcnv(tag)
			if (!s) {
				s = {
					type: C.NOTE,
					stem: 0,
					multi: 0,
					nhd: 0,
					xmx: 0,
					dur: dur,
					dur_orig: dur,
					notes: [note]
				}
			}
			s.istart = tag.ix

			switch (tag["stem.dir"]) {
			case "up": s.stem = 1; break
			case "down": s.stem = -1; break
			}
			if (tag.grace) {	// if in a grace note sequence
				gr = curvoice.last_sym
				if (gr && gr.type != C.GRACE) {
					gr = {			// new grace note
						type: C.GRACE,
						istart: tag.ix,
						dur: 0,
						stem: 0,
						multi: 0
					}
//fixme
//					if (tag["stem.mod"] == "1slash")
//						gr.sappo = true
					sym_link(gr)
					gr.extra = s
				} else {
					gr = gr.extra
					while (gr.next)		// add the note
						gr = gr.next
					gr.next = s
					s.prev = gr
				}
				s.cst = s.st = curvoice.st
				s.v = curvoice.v
				s.p_v = curvoice
			} else {
				switch (curr.beam) {
				case 0:
					s.beam_st = true
					s.beam_end = true
					break
				case 1:
					s.beam_st = true
					curr.beam = 2
					break
				}
				sym_link(s)
			}
		}
		if (id) {
			note.s = s
			do_delayed(id, curr.chord ? note : s)
		}
		note.dur = dur
		if (acc_ty[tag.accid])
			note.acc = acc_ty[tag.accid]

//fixme: may have notes of the chord on different staves
		if (tag.staff)
			s.st = curr.st_cnv[tag.staff - 1] // ABC staff number

//fixme:
//		head.auth
//			"smufl"
//fixme:
// if auth == smufl,
//   head.shape is a glyph reference as "#xE000" or "U+E000"
//		switch (tag["head.shape"]) {
//		case "slash":			// ??
//		}
//fixme:
//		switch (tag["head.fill"]) {
//		case "void":
//		case "solid":
//		case "top":
//		case "bottom":
//		case "left":
//		case "right":
//		}
//fixme:
//		tag["head.visible"]
		if (tag.grace)
			s.grace = true
		switch (tag.tuplet) {
		case "i1":
			curr.tp.push(s)
			break
//		case "m1":
//			ignored
		case "t1":
			if (curr.tp.length)
				tp_set(curr.tp.pop(), {
						num: 3,
						numbase: 2
						})
//fixme: else error
			break
		}

//fixme: always <tie> ?
//if (0) {
//		switch (tag.tie) {
//		case "t":
//		case "m":
//			s.tie_s = curvoice.tei_s
////fixme: KO if chord
//			s.notes[0].tie_m = s.notes.length - 1
//			if (tag.tie == "t")
//				break
//			// fall thru
//		case "i":
//			note.tie_ty = C.SL_AUTO
//			curvoice.tei_s = s
//			break
//		}
//}

// used for play ?
//		if (tag.slur) {
//		}

		if (tag.color)
			s.color = tag.color

		curr.s = s			// last note
		curr.oct = oct			// last octave

		// other attributes
		if (tag.artic)
			artic(s, tag)
//fixme:cross-staff chord
//		tag.stem.with
		switch (tag.breaksec) {
		case "1": s.beam_br1 = true; break
		case "2": s.beam_br2 = true; break
		}
//fixme: tag.staff may be used in chords for cross-staff chords
//		tag.staff	# new staff

//fixme: not visual?
//		if (tag.fermata)
//			add_dcn(s, "fermata")
	}, // note()

//	part: function(tag) {
//	}, // part()

//	parts: function(tag) {
//	}, // parts()

	// new page for text
	pb: function(tag) {
	    var	s
//fixme: tag.n (page number)
		if (parse.state == 3) {
			s = new_block("newpage")
			s.param = tag.n || ""
		} else {
			blk_flush()
			block.newpage = true
		}
	}, // pb()

	pedal: function(tag) {
	    var	dcn,
		s = get_ref(tag)
		if (!s)
			return
		if (s.s)
			s = s.s
		switch (tag.dir) {
		default:
//		case "up":
				dcn = "ped)"; break
		case "down": dcn = "ped("; break
		}
		add_dcn(s, dcn)
	}, // pedal()

	rend: function(tag) {
	    var	font, n,
		f = ""

		if (tag.fontfam)
			f += " " + tag.fontfam
		if (tag.fontname)
			f += " " + tag.fontname
		if (tag.fontweight)
			f += " " + tag.fontweight
		if (tag.fontstyle)
			f += " " + tag.fontstyle
		if (!f)
			f = "*"
		else
			f = f.slice(1)
		if (tag.fontsize) {
			n = parseInt(tag.fontsize) * 1.33	// 72 -> 96 DPI
			f += " " + n.toString()
		} else {
			f += " *"
		}
		if (f != "* *") {
			n = curr.fnt[f]
			if (!n) {
				n = ++curr.ftn
				curr.fnt[f] = n
				param_set_font("setfont-" + n, f)
			}
			curr.text += '$' + n
			curr.rend = true
		}
	}, // rend()

	rest: function(tag) {
	    var	id = tag["xml:id"],
		dur = durcnv(tag),
		s = {
			type: C.REST,
			istart: tag.ix,
			stem: 0,
			multi: 0,
			nhd: 0,
			xmx: 0,
			dur: dur,
			dur_orig: dur
		}

		if (tag.name == "rest") {
			switch (curr.beam) {
			case 0:
				s.beam_st = true
				s.beam_end = true
				break
			case 1:
				s.beam_st = true
				curr.beam = 2
				break
			}
		} else {			// space
			s.invis = true
		}

		sym_link(s)

		if (id)
			do_delayed(id, s)

		switch (tag.tuplet) {
		case "i1":
			curr.tp.push(s)
			break
//		case "m1":
//			ignored
		case "t1":
			if (curr.tp.length)
				tp_set(curr.tp.pop(), {
						num: 3,
						numbase: 2
						})
//fixme: else error
			break
		}
	}, // rest()

	revisionDesc: function(tag) {
		return true	// skip
	}, // revisionDesc()

	// newline for music
	sb: function(tag) {
		curvoice.last_sym.eoln = true
	}, // sb()

//	score: function(tag) {
//		tag.n
//		tag.id
//	}, // score()

	// score definition (= new staff system)
	// may contain a staffGrp (no staffGrp in <section>?)
	// may appear in section/(ending?) or between section/ending (in score/part)
	scoreDef: function(tag) {
	    var	w, h, v, tmp

		curr.scoreDef = tag

		if (parse.state >= 2) {
//			if (tag["clef.shape"]) {
//				s = new_clef(clefcnv(tag))
//				if (s) {
//					get_clef(s)
////fixme: move the clef - see layer:
//				}
//			}
			if (tag["key.sig"]) {
				tmp = keycnv(tag)
				for (v = 0; v < voice_tb.length; v++) {
					curvoice = voice_tb[v]
					get_key(tmp)
				}
			}
//			if (tag["meter.count"]) {
//				new_meter(metercnv(tag))
//				curr.mu = meteru(tag)
//			}
		}

//		// init the staff system
//		if (!curr.st_cnv)
//			curr.st_cnv = []

		// page layout
//fixme: the value of vu.height is odd!
		// recompute the height/width conversion
		if (tag["page.height"] && tag["page.width"]) {
			h = tag["page.height"]
			w = tag["page.width"]
			if (/^\d+$/.test(h + w))
				curr.pt = h / w > 1.3 ?
						1123.66 / h :	// A4
						1056 / h	// US letter
		}

//		if (tag["page.botmar"])
//			module
//		if (tag["page.height"])
//			module
		if (tag["page.leftmar"])
			self.set_format("leftmargin",
					unitcnv(tag["page.leftmar"]))
		if (tag["page.rightmar"])
			self.set_format("rightmargin",
					unitcnv(tag["page.rightmar"]))
		if (tag["page.scale"])
			self.set_format("pagescale", tag["page.scale"])
//		if (tag["page.topmar"])
//			module
		if (tag["page.width"])
			self.set_format("pagewidth",
					unitcnv(tag["page.width"]))

//		if (tag["music.name"])
//			musicfont = tag["music.name"]
//		if (tag["music.size"])
//fixme

	}, // scoreDef()

//	section: function(tag) {
//	}, // section()

	slur: function(tag) {
	    var	sl,
		s1 = get_ref(tag),
		s2 = get_ref(tag, s1),
		ty = C.SL_AUTO

		if (!s1 || !s2)
			return

		switch (tag.curvedir) {
		case "above": ty = C.SL_ABOVE; break
		case "below": ty = C.SL_BELOW; break
		}
		switch (tag.lform) {
		case "dashed":
		case "dotted": ty |= C.SL_DOTTED; break
		}

		sl = { ty: ty }
		if (s2.s) {
			sl.is_note = true	// to note
			sl.note = s2
		} else {
			s2.notes[0].s = s2
			sl.note = s2.notes[0]	// to chord
		}

		if (!s1.sls)
			s1.sls = []
		s1.sls.push(sl)
		if (s1.s)			// if from note of chord
			s1.s.sl1 = true
//fixme: ko if grace note
	}, // slur()

	sourceDesc: function(tag) {
		return true	// skip
	},

	// staff - must be inside measure and must contain layers
	staff: function(tag) {
		curr.st = curr.st_cnv[tag.n - 1]	// ABC staff number
		if (curr.st == undefined) {
			error(1, null, "Unknown staff " + tag.n)
			return true
		}
	    var	staff = par_sy.staves[curr.st]
		switch (tag.visible) {
		case "false":
			staff.invis = true
			break
		case "true":
			staff.invis = false
			break
		}
	}, // staff()

	// staff definition
	// the order in the staffGrp is the (ABC) staff number
	// may appear in scoreDef, measure, staffGrp
	//	between measures (in section/ending)
	//	and between section/ending (in score/part)
	staffDef: function(tag) {
	    var	k, st, def, staff, sy,
		n = tag.n - 1,			// MEI staff number
		grp = curr.staffGrp

		if (!curr.st_cnv)
			curr.st_cnv = []
		else
			st = curr.st_cnv[n]	// ABC staff number
		if (st == undefined) {		// if new staff
			staff = {
				stafflines: '|||||',
				staffscale: 1,
				flags: 0
			}
			if (grp) {
				staff.flags = grp.flags
				grp.flags &= STOP_BAR
			}

			if (parse.state == 3 && !curr.newsy) {
				curr.newsy = true
				sy = par_sy
				par_sy = clone(sy)
				sy.next = par_sy
				par_sy.voices = clone(par_sy.voices)
				par_sy.staves = clone(par_sy.staves)
			}

			st = par_sy.staves.length	// ABC staff number
			par_sy.staves.push(staff)	// new staff
			par_sy.nstaff = st
			curr.st_cnv[n] = st		// MEI to ABC staff conversion
		} else {
			staff = par_sy.staves[st]
		}

		// set the default values from the scoreDef
		def = curr.scoreDef
		if (def) {
			for (k in def) {
				switch (k.slice(0, 3)) {
				case "cle":
				case "key":
				case "met":
					staff[k] = def[k]
					break
				}
			}
		}
		// set the values from the current element
		for (k in tag) {
			switch (k.slice(0, 3)) {
			case "cle":
			case "key":
			case "met":
				staff[k] = tag[k]
				break
			}
		}

//fixme
//		if (!staff["clef.shape"]) {
//			error(1, null, "No clef in staff " + tag.n)
//			staff["clef.shape"] = "G"
//			staff["clef.line"] = "2"
//			staff.lines = "5"
//		}

		if (tag.label) {
			staff.label = tag
		} else if (grp && grp.label) {
			staff.label = {
				label: grp.label
			}
			delete grp.label
		}

//		if (tag["music.name"])
//fixme
//		if (tag["music.size"])
//fixme
		if (tag.spacing) {
//fixme: no unit is 10th of mm ?
		    var	v = tag.spacing.slice(-1)
			if (v >= '0' && v <= '9')
				staff.maxsep = Number(tag.spacing) / 100 * CM
			else
				staff.maxsep = get_unit(tag.spacing)
		}
		if (tag.children)
			curr.st = st
	}, // staffDef()

	// group of staves
	// may be contained in a scoreDef
	staffGrp: function(tag) {
		if (!tag.symbol)
			return			// unused?
	    var	grp = curr.staffGrp
		if (!grp) {
			curr.staffGrp = grp = {
				lvl: 0,
				flags: 0
			}
			switch (tag.symbol) {
			case "brace": grp.flags |= OPEN_BRACE; break
			case "bracket": grp.flags |= OPEN_BRACKET; break
			}
		} else {
			grp.lvl++
			switch (tag.symbol) {
			case "brace": grp.flags |= OPEN_BRACE2; break
			case "bracket": grp.flags |= OPEN_BRACKET2; break
			}
		}
		if (tag["bar.thru"] == "false")
			grp.flags |= STOP_BAR
	}, // staffGrp()

	syl: set_text,

	// start of tempo and title
	tempo: function(tag) {
		set_text(tag)
		if (!tag.children)
			fne[tag.name](tag)
	}, // tempo()

	text: function(tag) {
		if (curr.text != undefined)
			curr.text += tag.content.replace(/[ \t\n]+/g, ' ')
	},

	// tie
	tie: function(tag) {
	    var	s1 = get_ref(tag),
		s2 = get_ref(tag, s1),
		ty = C.SL_AUTO

		if (!s1 || !s2)
			return
		switch (tag.curvedir) {
		case "above": ty = C.SL_ABOVE; break
		case "below": ty = C.SL_BELOW; break
		}
		switch (tag.lform) {
		case "dashed":
		case "dotted": ty |= C.SL_DOTTED; break
		}
		if (s1.s) {
			s1.tie_ty = ty
			if (s2.s) {			// note - note
				s1.s.tie_s = s2.s
				s1.tie_n = s2
				s2.s.ti2 = s1.s
			} else {			// note - single note
				s1.s.tie_s = s2
				s1.tie_n = s2.notes[0]
				s2.ti2 = s1.s
			}
		} else {
			s1.notes[0].tie_ty = ty
			if (s2.s) {			// single note - note
				s1.tie_s = s2.s
				s1.notes[0].tie_n = s2
				s2.s.ti2 = s1
			} else {			// single note - single note
				s1.tie_s = s2
				s1.notes[0].tie_n = s2.notes[0]
				s2.ti2 = s1
			}
		}
	}, // tie()

	trill: function(tag) {
	    var	s2,
		s = get_ref(tag)

		if (!s)
			return
		if (tag.dur || tag["dur.ges"] || tag.endid || tag.tstamp2) {
			s2 = get_ref(tag, s)
			if (!s2)
				return
		}
		if (s.s)			// on note
			s = s.s
		switch (tag.place) {
		case "above": s.pos.orn = C.SL_ABOVE; break
		case "below": s.pos.orn = C.SL_BELOW; break
		}
		if (!s2) {
			add_dcn(s, "trill")
			return
		}
		if (s.time >= s2.time) {
			error(1, null, "Bad trill endings")
			return
		}
		add_dcn(s, "trill(")
		add_dcn(s2, "trill)")
	}, // trill()

	// start of tuplet (one nested level only)
	tuplet: function(tag) {
		curr.tp.push(curvoice.last_sym || true)
	}, //tuplet()

	// start of tupletSpan
	tupletSpan: function(tag) {
	    var	last, next, f,
		s = get_ref(tag),
		s2 = get_ref(tag, s)

		if (!s || !s2)
			return

		if (s.tp.length) {
			tp_fl(s.tp[0].f, tag)
			return
		}

		last = curvoice.last_sym
		curvoice.last_sym = s2
		next = s2.next
		s2.next = null
		tp_set(s, tag)
		s2.next = next
		curvoice.last_sym = last
	}, // tupletSpan()

	verse: function(tag) {
		curr.verse = tag
	}, // verse()

	work: function(tag) {
		curr.work = true
	}, // work()

	}, // fns

	// --- functions associated to the end of MIE tags
	fne = {

	// end of beam
	beam: function(tag) {
		curr.s.beam_end = true
		curr.beam = 0
	}, // beam()

	// end of chord
	chord: function(tag) {
	    var	id = tag["xml:id"]
		if (id)
			do_delayed(id, curr.chord)
		curr.chord = null
	}, // chord()

	// end of composer
	composer: function(tag) {
		if (!curr.text)
			return
		if (info.C == undefined)
			info.C = curr.text
		else
			info.C += "\n" + curr.text
		curr.text = undefined
	}, // composer()

	// end of annotation (dir) / chord symbol (harm) / annotation (dynam)
	dir: function(tag) {
	    var	gch, ty, t, i,
		font = gene.deffont,
		s = get_ref(tag)

		if (!s)
			return
		if (s.s)
			s = s.s
		switch (tag.name) {
		case "dir":
		case "dynam":
			switch (tag.place) {
			default:
//			case "above":
					ty = "^"; break
			case "below": ty = "_"; break
			}
			break
		default:
			ty = "g"
			switch (tag.place) {
			default:
//			case "above":
					s.pos.gch = C.SL_ABOVE; break
			case "below": s.pos.gch = C.SL_BELOW; break
			}
			break
		}
		a_gch = s.a_gch || []
		t = curr.text.split('\n')
		if (t[0][0] == ' ')
			t[0] = t[0].slice(1)
		for (i = 0; i < t.length; i++) {
			gch = {
				type: ty,
				font: font,
				text: t[i]
			}
			a_gch.push(gch)
		}
		self.gch_build(s)
		curr.text = undefined
	}, // dir()

	// end of division
	div: function(tag) {
	    var s
		switch (tag.type) {
		case undefined:
			s = new_block("text")
			s.text = curr.text
			break
		case "verse":
			if (info.W == undefined)
				info.W = ""
			if (tag.label)
				info.W += tag.label + " "
			info.W += curr.text + "\n\n"
			break
		default:
			error(1, null, "Tag <div> type "+ tag.type + "not treated")
			break
		}
		curr.text = undefined
	}, // div()

	// end of dynam
	dynam: function(tag) {
	    var	t,
		s = get_ref(tag)
		if (!s)
			return
		if (s.s)
			s = s.s
		t = curr.text.replace(/[ \t\n]+/g, ' ')
		if (t[0] == ' ')
			t = t.slice(1)
		switch (tag.place) {
		case "above": s.pos.dyn = C.SL_ABOVE; break
		case "below": s.pos.dyn = C.SL_BELOW; break
		}
		add_dcn(s, t, tag)
		curr.text = undefined
	}, //dynam()

	// end of variant ending
	ending: function(tag) {
	    var	v = voice_tb.length
		while (--v >= 0) {
			if (voice_tb[v].last_sym)
				voice_tb[v].last_sym.rbstop = 2
		}
	}, // ending()

	fing: function(tag) {
	    var	s = get_ref(tag)
		if (!s)
			return
		if (s.s)
			s = s.s
		switch (tag.place) {
		case "above": s.pos.orn = C.SL_ABOVE; break
		case "below": s.pos.orn = C.SL_BELOW; break
		}
		add_dcn(s, curr.text)
		curr.text = undefined
	}, // fing()

//	// end of incipit
//	incip: function(tag) {
//		tosvg(fn, "\n")			// generate
//		curr.st_cnv = null
//		curr.meastim = 0
//		curr.mu = C.BLEN / 4
//	}, // incip()

	// end of label (voice name)
	label: function(tag) {
		if (!curr.text)
			return
	    var	grp = curr.staffGrp,
		st = curr.st			// ABC staff number
		if (st  == undefined) {
			if (grp) {
				grp.label = curr.text
				curr.text = undefined
			}
			return
		}
	    var	staff = par_sy.staves[st]
		if (!staff.label)
			staff.label = {}
		staff.label.label = curr.text
		curr.text = undefined
	}, // label()

	labelAbbr: function(tag) {
		if (!curr.text)
			return
	    var	st = curr.st,			// ABC staff number
		staff = par_sy.staves[st]
		if (!staff.label)
			staff.label = {}
		staff.label["label.abbr"] = curr.text
		curr.text = undefined
	}, // labelAbbr()

	// end of measure
	measure: function(tag) {
	    var	v, s, s2, s3, p_v, tim, ty, dotted,
		n = voice_tb.length

		if (curr.tp.length) {
			error(1, null, "No end of tuplet")
			curr.tp = []
		}

		// change the bars at start of the measure (or start of tune)
		if (tag.left) {
			ty = bar_ty[tag.left]
			if (ty && ty[0] == '.') {
				ty = ty.slice(1)
				dotted = true
			}
		}
		if (ty || curr.ending) {
			for (v = 0; v < n; v++) {
				p_v = voice_tb[v]
				s2 = s = p_v.last_sym
				if (s)
					s = s.prev
				while (s && !s.bar_type)
					s = s.prev
				if (!s) {	// start of tune: add a measure bar
					s3 = p_v.sym
					p_v.last_sym = p_v.sym = null
					curvoice = p_v
					sym_link({
						type: C.BAR,
						istart: tag.ix,
						bar_type: ty || "|",
						dur: 0,
						multi: 0
					})
					s = p_v.last_sym
					p_v.last_sym = s2
					s.next = s3
					if (s3)
						s3.prev = s
					s.time = curr.meastim
				} else if (ty) {
					s.bar_type = ty
				}
				if (dotted)
					s.bar_dotted = true
				if (curr.ending) {
					s.rbstop = 2
					s.rbstart = 2
					s.text = curr.ending
				}
			}
			dotted = false
			ty = null
			delete curr.ending
		}

		// add the ending bar
		if (tag.right)
			ty = bar_ty[tag.right]
		if (!ty)
			ty = "|"

		// if a new staff system, add it at start of the measure
//		// (before the - previous - measure bar)
		// (just after the - previous - measure bar)
		if (curr.newsy) {
			curr.newsy = false
			for (s = voice_tb[par_sy.top_voice].last_sym;
			     s;
			     s = s.prev) {
				if (s.type == C.BAR)
					break
			}
			if (s) {			// ?
				s2 = clone(s)
				s2.type = C.STAVES
				s2.bar_type = 0
				s2.sy = par_sy

//				s2.prev = s.prev
//				s2.prev.next = s2
//				s2.next = s
//				s.prev = s2
				s2.next = s.next
				if (s.next)
					s2.next.prev = s2
				s.next = s2
				s2.prev = s
			}
		}

		// synchronize the voices
		// adjust the duration of the full measure rests
		// add the measure bars
		tim = 0
		for (v = 0; v < n; v++) {
			p_v = voice_tb[v]
			if (p_v.time > tim)
				tim = p_v.time
		}
		if (curr.mrest) {		// if some full measure rests
			curr.mrest = false
			for (v = 0; v < n; v++) {
				p_v = voice_tb[v]
				s = p_v.last_sym // there may be only rest and bar
				if (s && s.type == C.BAR)
					s = s.prev
				if (!s || s.type != C.REST || s.time != curr.meastim)
					continue
				s.dur = s.dur_orig = tim - curr.meastim
//fixme: should set a flag saying 'full measure rest'
				if (s.next)
					s.next.time = tim	// bar time
			}
		}
		if (ty[0] == '.') {
			ty = ty.slice(1)
			dotted = true
		}
		for (v = 0; v < n; v++) {
			p_v = voice_tb[v]
			p_v.time = tim

			s = p_v.last_sym
			if (s && s.time == tim && s.bar_type)
				continue
//			get_voice(p_v.id)
			curvoice = p_v
			s = {
				type: C.BAR,
				istart: tag.ix,
				bar_type: ty,
				dur: 0,
				multi: 0
			}
			if (dotted)
				s.bar_dotted = true
			if (v == par_sy.top_voice)
				s.bar_num = Number(tag.n) + 1
			sym_link(s)
		}
		curr.meastim = tim
	}, // measure()

	// end of the tune
	music: function(tag) {
		sanitize()

//fixme: copy of end_tune()
		generate()
		if (info.W)
			put_words(info.W)
		put_history()
		blk_flush()
	}, // music()

//	// end of page footer
//	pgFoot: function(tag) {
//		<p>
//	}, // pgFoot()

//	// end of page header
//	pgHead: function(tag) {
//		<title>
//		<persName role>
//	}, // pgHead()

	// end of rendition
	rend: function(tag) {
		if (curr.rend) {
			curr.rend = false
			curr.text += "$0"
		}
	}, // rend()

	// end of score definition
	scoreDef: function(tag) {
		curr.scoreDef = null
	}, //scoreDef()

	// end of staff definition
	staffDef: function(tag) {
		delete curr.st
	}, //staffDef()

	// end of staff group
	staffGrp: function(tag) {
	    var	grp = curr.staffGrp,
		st = par_sy.staves.length - 1,	// last staff
		staff = par_sy.staves[st]

		if (!grp)
			return
		if (--grp.lvl < 0) {
			curr.staffGrp = null
			switch (tag.symbol) {
			case "brace": staff.flags |= CLOSE_BRACE; break
			case "bracket": staff.flags |= CLOSE_BRACKET; break
			}
			return
		}
		switch (tag.symbol) {
		case "brace": staff.flags |= CLOSE_BRACE2; break
		case "bracket": staff.flags |= CLOSE_BRACKET2; break
		}
	}, // staffGrp()

	// end of syllabe
	syl: function(tag) {
	    var	t = curr.text.replace(/[ \t\n]+/g, ' ')
		curr.text = undefined

		if (t[0] == ' ')
			t = t.slice(1)

	    var	l, wh, c,
		s = curr.s		// note
		curvoice.have_ly = true
		wh = strwh(t)

		if (curr.verse && curr.verse.n)
			l = curr.verse.n - 1	// verse number (0...n-1)
		else
			l = 0			// ('n' omitted)

		if (t.slice(-1) == '-')
			t = t.slice(0, -1) + '\n'
		switch (tag.con) {		// connector
		case "s":
			t += ' '
			break
		case "d":
			t += '-'
			break
		case "u":
			t += '_'
			break
//		case "t":
//			c = '~'
//			break
//		case "c":
//			break
//		case "v":
//			break
//		case "i":
//			break
//		case "b":
//			break
		}
		switch (tag.wordpos) {
		case "i":			// initial
		case "m":			// middle
			if (t.slice(-2) != "\n") {
				if (c == '_')
					t += c
				t += "\n"
			}
			break
//		case "t":
//			break
		}

		if (!s.a_ly)
			s.a_ly = []
		s.a_ly[l] = {
			t: t,
			font: gene.curfont,
			wh: wh
		}
	}, // syl()

	// end of tempo
	tempo: function(tag) {
	    var	t = curr.text.replace(/[ \t\n]+/g, ' ')
		curr.text = undefined

//		if (t[0] == ' ')
//			t = t.slice(1)
//		if (!t || t == "undefined")
//			return

	    var	s,
		s2 = {
			type: C.TEMPO,
			dur: 0
		}
		if (t)
			s2.tempo_str1 = t
		if (tag.mm) {
			s2.tempo_notes = [curr.mu]
			s2.tempo = tag.mm
		}
		if (parse.state <= 1) {
			info.Q = '"' + t + '"'
			glovar.tempo = s2
			return
		}

		// insert the tempo before the reference
		s = get_ref(tag)
		if (!s)
			return
		s2.prev = s.prev
		s.prev = s2
		s2.next = s
		if (s2.prev)
			s2.prev.next = s2
		else
			s.p_v.sym = s2
		s2.v = s.v
		s2.p_v = s.p_v
		s2.st = s.st
		s2.time = s.time
	}, // tempo()

	// end of title
	title: function(tag) {
	    var	t = curr.text.replace(/[ \t]+/g, ' ')
		curr.text = undefined
		if (t[0] == ' ')
			t = t.slice(1)
		if (parse.state != 3) {
			if (info.T == undefined)
				info.T = t
			else
				info.T += "\n" + t
		} else {
		    var	s = new_block("title")
			s.text = t
		}
	}, // title()

	// end of tuplet
	tuplet: function(tag) {
	   var	s = curr.tp.pop() // symbol before the start of the tuplet
				  // or 'true' if the tune starts with a tuplet

		if (s && (tag.dur || tag.num))
			tp_set(s == true ? curvoice.sym : s.next, tag)
	}, // tuplet()

	// end of work
	work: function(tag) {
		curr.work = false
	}, // work()

	} // fne

	// common code
	fns.lg = fns.l			// start of line group
	fns.labelAbbr = fns.label	// start of labelAbbr
	fns.space = fns.rest		// space
	fns.title = fns.tempo

	fne.harm = fne.dir		// end of chord symbol (harmony)

	// add a decoration into a symbol
	function add_dcn(s, dcn, tag) {
	    var	dd = dd_tb[dcn]

		// build a dynamic decoration
		function build_dyn(t) {
		    var	i,
			str = "",
			dd = {
				name: t,
				func: 6,
				glyph: t,
				h: 18,
				wl: t.length * .3,
				wr: t.length * .7
			}
			for (i = 0; i < t.length; i++) {
				switch (t[i]) {
				case 'f': str += "\ue522"; break
				case 'p': str += "\ue520"; break
				case 'r': str += "\ue523"; break
				case 's': str += "\ue524"; break
				}
			}
			tgls[t] = {
				x: -4,
				y: -6,
				c: str
			}
			return dd
		} // build_dyn()

		if (!dd) {
			if (decos[dcn]) {		// if known decoration
				dd = deco_def(dcn)
			} else {
				if (!tag)
//fixme: error
					return
				if (tag.name != "dynam"
				 || /[^fprs]/.test(dcn)) {
					fne.dir(tag)	// display as annotation
					return
				}
				dd_tb[dcn] = dd = build_dyn(dcn)
			}
		}
		if (!s.a_dd)
			s.a_dd = []
//		switch (tag.place) {
//		default:
////		case "above":
//		case "below":
//fixme: how to force the place of the decoration?
//		}
		s.a_dd.push(dd)
	} // add_dcn()

	// articulation (inside a chord/note)
	function artic(s, tag) {
	    var	dcn, i,
		d_tb = {
		acc: "accent",
		dnbow: "downbow",
		marc: "marcato",
		open: "open",
		snap: "snap",
		spic: "wedge",
		stacc: "dot",
		ten: "tenuto",
		upbow: "upbow",
		},
		d = tag.artic.split(/\s+/)

		switch (tag.place) {
		case "above": s.pos.orn = C.SL_ABOVE; break
		case "below": s.pos.orn = C.SL_BELOW; break
		}
		for (i = 0; i < d.length; i++) {
			dcn = d_tb[d[i]]
			if (dcn)
				add_dcn(s, dcn)
			else
				error(1, null, "Articulation " + d[i] + " not handled")
		}
	} // artic()

	// define a new element reference and
	// resolve its definition in the associated elements
	function do_delayed(id, s) {
	    var	i, n, tag,
		delayed_a = curr.delayed[id]

		curr.ids[id] = s
		if (!delayed_a)
			return		// no element is waiting for this element
		curr.delayed[id] = null

		n = delayed_a.length
		for (i = 0; i < n; i++) {
			tag = delayed_a[i]
			fns[tag.name](tag)
		}
	}

	// convert a clef
	function clefcnv(tag) {
	    var clef = {"G": "G",
			"GG": "G",
			"F": "F",
			"C": "C",
			"perc": "P",
			"TAB": "tab"}[tag["clef.shape"]]
		if (tag["clef.line"])
			clef += tag["clef.line"]
		if (tag["clef.dis"]) {
			if (tag["clef.dis.place"] == "above")
				clef += '+' + tag["clef.dis"]
			else
				clef += '-' + tag["clef.dis"]
		}
		return clef
	} // clefcnv()

	// convert a (CMN) duration
	function durcnv(tag) {
	    var dur

		switch (tag.dur) {
		case "breve": dur = C.BLEN * 2; break
		case "long": dur = C.BLEN * 4; break
//fixme: beat?
		case undefined: dur = C.BLEN / 4; break
		default: dur = C.BLEN / tag.dur
		}
		switch (tag.dots) {
		case "1": dur *= 3/2; break
		case "2": dur *= 7/4; break
		case "3": dur *= 15/8; break
		}
if (isNaN(dur))
error(1, null, "Bad duration " + tag.dur + " tag:" + tag.name)
		return dur
	} // durcnv()

	// change the source reference in error messages
    var	err_old
	function error_new(sev, s, msg, a1, a2, a3, a4) {
		if (!s)
			s = {}
		if (!s.fname)
			s.fname = "(no ref)"
		if (!s.istart)
			s.istart = parse.istart
		err_old(sev, s, msg, a1, a2, a3, a4)
	}
	err_old = error
	error = error_new

	// get the symbol (chord, note or rest) pointed to
	// by startid/endid or tstamp/tstamp2
	// @end is the pointer to the start symbol
	function get_ref(tag, end) {
	    var	s, st, v_rg, v, voice, tim,
		rg = 100,
		ref = tag[end ? "endid" : "startid"]

		if (ref) {
			if (ref[0] == '#')
				ref = ref.slice(1)
			s = curr.ids[ref]	// symbol or note
			if (!s) {		// the symbol is not defined yet
				if (!curr.delayed[ref])
					curr.delayed[ref] = []
				curr.delayed[ref].push(tag)
				return // null
			}
		} else {			// try a time stamp
			ref = end ? tag.tstamp2 : tag.tstamp
			if (!ref) {
				error(1, null, "No "+ (end ? "end" : "start") +
					" reference tag:" + tag.name)
				return //
			}

			if (ref[1] == 'm') {
				if (ref[0] == '0' && ref[2] == '+')
					ref = ref.slice(3)
				else
//fixme: tstamp2 may have more values
					return
			}

			// convert the beat into internal time
			tim = curr.meastim + curr.mu * (ref - 1)

			// search the voice
			st = curr.st_cnv[tag.staff ? (tag.staff - 1) : 0]
//fixme: may be simplified using the voice ID (main voice of the staff is V:<#staff>
			for (v = 0; v < par_sy.voices.length; v++) {
				voice = par_sy.voices[v]
				if (!voice || voice.st != st)
					continue
				if (voice.range < rg) {
					v_rg = v
					rg = voice.range
				}
			}

			if (rg >= 100) {
				error(1, null, "No voice in staff " + st +
					" tag:" + tag.name)
				return
			}

			// and the symbol
			for (s = voice_tb[v_rg].last_sym; s; s = s.prev) {
				if (s.time <= tim) {
					if (ref != 0)
						break
					while (s && s.type != C.BAR)
						s = s.prev
					break
				}
			}
		}

		if (!s) {
			error(1, null, "No symbol at beat " + ref +
				" in staff " + tag.staff + " tag:" + tag.name)
			return
		}

		if (!end)
			return s

		if (s == end) {
			error(1, s, "References on a same element")
			if (!s.next)
				return
			s = s.next
		}
		if (s.time <= end.time) {
			error(0, s, "References going back in time")
			return
		}
		return s
	} // get_ref()

	// convert a key
	function keycnv(tag) {
	    var	sig2kM = {
			"0" : "C",
			"1s": "G",
			"2s": "D",
			"3s": "A",
			"4s": "E",
			"5s": "B",
			"6s": "F#",
			"7s": "C#",
			"1f": "F",
			"2f": "Bb",
			"3f": "Eb",
			"4f": "Ab",
			"5f": "Db",
			"6f": "Gb",
			"7f": "Cb"
		},
		sig2km = {
			"0" : "Am",
			"1s": "Em",
			"2s": "Bm",
			"3s": "F#m",
			"4s": "C#m",
			"5s": "G#m",
			"6s": "D#m",
			"7s": "A#m",
			"1f": "Dm",
			"2f": "Gm",
			"3f": "Cm",
			"4f": "Fm",
			"5f": "Bbm",
			"6f": "Ebm",
			"7f": "Abm"
		}
		return tag["key.mode"] != "minor" ?
			sig2kM[tag["key.sig"]] :
			sig2km[tag["key.sig"]]
	} // keycnv()

	// convert a meter
	function metercnv(tag) {
		switch (tag["meter.sym"]) {
		case "common": return "C"
		case "cut": return "C|"
		}
		if (tag["meter.unit"])
			return tag["meter.count"] + "/" + tag["meter.unit"]
		return tag["meter.count"]
	} // metercnv()

	// get the unit of a meter
	function meteru(tag) {
	    var	mu = 1
		switch (tag["meter.sym"]) {
		case "common": mu = 4; break
		case "cut": mu = 2; break
		}
		if (tag["meter.unit"])
			mu = Number(tag["meter.unit"])
		return C.BLEN / mu
	} // meteru()

	function set_text(tag) {
	    var	f
		switch (tag.name) {
		case "dir": f = "annotation"; break
		case "dynam": f = "dynam"; break
//		case "fing": f = null; break
		case "harm": f = "gchord"; break
		case "syl": f = "vocal"; break
//		case "tempo": f = null; break
		case "title": f = "title"; break
		}
		if (f)
			set_font(f)
		curr.text = ""
	}

	// set tuplet flags
	function tp_fl(f, tag) {
		if (tag["num.visible"] == "false") {
			f[0] = 1			// when = never
		} else {
			if (tag["bracket.visible"] == "false")
				f[1] = 2		// what = nothing
			if (tag["num.format"] == "ratio")
				f[2] = 2		// which = ratio
		}
		switch (tag["bracket.place"]) {
		case "above": f[3] = 1; break		// where = above
		case "below": f[3] = 2; break		// where = below
		}
	} // tp_fl()

	// handle the end of one tuplet
	function tp_set(s, tag) {
	    var	fact, s2, s3, n,
		nbas = tag.numbase || 2,	// numbas default = 2 ?
		f = Object.create(cfmt.tuplets)

		while (s.grace)
			s = s.next

		if (!s.tp)
			s.tp = []
		s.tp.push({
			p: tag.num,
			q: nbas,
			f: f
		})

		tp_fl(f, tag)

		// adjust the duration of the symbols
		if (tag.dur)
			fact = durcnv(tag) / (curvoice.time - s1.time)
		else
			fact = nbas / tag.num

		tp_adj(s, fact)

		s2 = curvoice.last_sym
		if (!s2.dur) {	// if some odd element at end of the tuplet
			for (s3 = s2; !s3.dur; s3 = s3.prev)
				;
			s3.tpe = s2.tpe	// move the end of tuplet
			delete s2.tpe
		}

		n = 0
		for (s3 = s; s3; s3 = s3.next) {
			if (s3.dur)
				n++
		}
		s.tp[s.tp.length - 1].ro = n	// (needed for toabc)
	} // tp_set()

//fixme: no unit is 10th of mm ?
	function unitcnv(v) {
	    var	nv = v.slice(-1)
		if (nv >= '0' && nv <= '9')
			return (v * curr.pt).toString()
		return v
	}

	// check for bugs in the source
	function sanitize() {
	    var	i, v,
		used_st = []

		// check for used staves
		for (v = 0; v < voice_tb.length; v++) {
			i = voice_tb[v].st
			if (i)
				used_st[i] = true
		}
		for (i = par_sy.nstaff; i > 0; i--) {
			if (used_st[i])
				break
		}
		if (i != par_sy.nstaff)
			error(1, null, "Unused staves "+(i+1)+".."+par_sy.nstaff)
		par_sy.nstaff = i
	} // sanitize()

	// convert to music
	function parse_mei(tag) {
		if (fns[tag.name]) {
			parse.istart = tag.ix
			if (fns[tag.name](tag))
				return			// skip when error
		}
		if (!tag.children)
			return
	    var	i,
		n = tag.children.length
		for (i = 0; i < n; i++)
			parse_mei(tag.children[i])
		if (fne[tag.name])
			fne[tag.name](tag)
	} // parse_mei()

	// main code of mei2mus()

	parse.file = mei
	cfmt.graceslurs = false
	glovar.ulen = C.BLEN / 4
	param_set_font("dynamfont", "serifItalicBold 16")

	parse_mei(xml2tree(mei)) // convert to internal music representation
} // mei2mus()
Changes to core/modules.js.
︙
13
14
15
16
17
18
19


20

21
22

23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
13
14
15
16
17
18
19
20
21

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41







+
+
-
+


+








+







// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

// empty function
if (!abc2svg.loadjs) {
abc2svg.loadjs = function(fn, onsuccess, onerror) {
    abc2svg.loadjs = function(fn, onsuccess, onerror) {
	if (onerror)
		onerror()
    }
}

abc2svg.modules = {
		ambitus: { fn: 'ambitus-1.js' },
		beginps: { fn: 'psvg-1.js' },
		break: { fn: 'break-1.js' },
		capo: { fn: 'capo-1.js' },
		clip: { fn: 'clip-1.js' },
	clairnote: { fn: 'clair-1.js' },
		voicecombine: { fn: 'combine-1.js' },
		diagram: { fn: 'diag-1.js' },
	equalbars: { fn: 'equalbars-1.js' },
		grid: { fn: 'grid-1.js' },
		grid2: { fn: 'grid2-1.js' },
		MIDI: { fn: 'MIDI-1.js' },
	pageheight: { fn: 'page-1.js' },
︙
Changes to core/music.js.
︙
23
24
25
26
27
28
29
30
31
32






33
34
35
36
37
38
39
23
24
25
26
27
28
29



30
31
32
33
34
35
36
37
38
39
40
41
42







-
-
-
+
+
+
+
+
+







	tsnext,			// next line when cut
	realwidth,		// real staff width while generating
	insert_meter,		// insert time signature (1) and indent 1st line (2)
	spf_last,		// spacing for last short line

/* width of notes indexed by log2(note_length) */
	space_tb = new Float32Array([
		7, 10, 14.15, 20, 28.3,
		40,			/* crotchet (whole note / 4) */
		56.6, 80, 100, 120
// factor 1.3 instead of 1.414 and crotchet 30px instead of 40px
// change again:
// factor 1.33 instead of 1.3 and crotchet 35px instead of 30px
		8.4, 11.2, 14.9, 19.8, 26.3,
		35,
		46.6, 61.9, 82.3, 109.5
	]),
	smallest_duration

/* -- decide whether to shift heads to other side of stem on chords -- */
/* this routine is called only once per tune */

// distance for no overlap - index: [prev acc][cur acc]
︙
66
67
68
69
70
71
72













































73
74
75
76
77
78
79
80

81
82
83
84
85
86
87
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127

128
129
130
131
132
133
134
135







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







-
+







var w_note = new Float32Array([
	3.5,		// FULL
	3.7,		// EMPTY
	5,		// OVAL
	6,		// OVALBARS
	7		// SQUARE
])

// get head type, dots, flags of note/rest for a duration
function identify_note(s, dur_o) {
    var	head, flags,
	dots = 0,
	dur = dur_o

	if (dur % 12 != 0)
		error(1, s, "Invalid note duration $1", dur);
	dur /= 12			/* see C.BLEN for values */
	if (!dur)
		error(1, s, "Note too short")
	for (flags = 5; dur != 0; dur >>= 1, flags--) {
		if (dur & 1)
			break
	}
	dur >>= 1
	if ((dur + 1) & dur)
		error(0, s, "Non standard note duration $1", dur_o)
	while (dur >> dots > 0)
		dots++

	flags -= dots
	if (flags >= 0) {
		head = C.FULL
	} else switch (flags) {
	default:
		error(1, s, "Note too long");
		flags = -4
		/* fall thru */
	case -4:
		head = C.SQUARE
		break
	case -3:
		head = cfmt.squarebreve ? C.SQUARE : C.OVALBARS
		break
	case -2:
		head = C.OVAL
		break
	case -1:
		head = C.EMPTY
		break
	}
	return [head, dots, flags]
}

function set_head_shift(s) {
	var	i, i1, i2, d, ps, dx,
		dx_head = dx_tb[s.head],
		dir = s.stem,
		n = s.nhd

	if (n == 0)
	if (!n)
		return			// single note

	/* set the head shifts */
	dx = dx_head * .78
	if (s.grace)
		dx *= .5
	if (dir >= 0) {
︙
95
96
97
98
99
100
101
102

103
104
105
106
107
108
109
143
144
145
146
147
148
149

150
151
152
153
154
155
156
157







-
+







		ps = s.notes[n].pit
	}
	var	shift = false,
		dx_max = 0
	for (i = i1; i != i2; i += dir) {
		d = s.notes[i].pit - ps;
		ps = s.notes[i].pit
		if (d == 0) {
		if (!d) {
			if (shift) {		/* unison on shifted note */
				var new_dx = s.notes[i].shhd =
						s.notes[i - dir].shhd + dx
				if (dx_max < new_dx)
					dx_max = new_dx
				continue
			}
︙
130
131
132
133
134
135
136
137
138


139
140
141
142
143
144
145
178
179
180
181
182
183
184


185
186
187
188
189
190
191
192
193







-
-
+
+







		}
	}
	s.xmx = dx_max				/* shift the dots */
}

// set the accidental shifts for a set of chords
function acc_shift(notes, dx_head) {
	var	i, i1, dx, dx1, ps, p1, acc,
		n = notes.length
    var	i, i1, i2, dx, dx1, dx2, ps, p1, acc,
	n = notes.length

	// set the shifts from the head shifts
	for (i = n - 1; --i >= 0; ) {	// (no shift on top)
		dx = notes[i].shhd
		if (!dx || dx > 0)
			continue
		dx = dx_head - dx;
︙
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172






















































173
174
175
176
177
178
179
201
202
203
204
205
206
207













208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268







-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







			if (p1 > ps + 3)
				continue
			if (notes[i1].shac < dx)
				notes[i1].shac = dx
		}
	}

	// set the shifts from accidental shifts
	for (i = n; --i >= 0; ) {		// from top to bottom
		acc = notes[i].acc
		if (!acc)
			continue
		dx = notes[i].shac
		if (!dx) {
			dx = notes[i].shhd
			if (dx < 0)
				dx = dx_head - dx
			else
				dx = dx_head
		}
	// set the shifts of the highest and lowest notes
	for (i1 = n; --i1 >= 0; ) {
		if (notes[i1].acc) {
			p1 = notes[i1].pit	// top note with accidental
			dx1 = notes[i1].shac
			if (!dx1) {
				dx1 = notes[i1].shhd
				if (dx1 < 0)
					dx1 = dx_head - dx1
				else
					dx1 = dx_head
			}
			break
		}
	}
	if (i1 < 0)				// no accidental
		return
	for (i2 = 0; i2 < i1; i2++) {
		if (notes[i2].acc) {
			ps = notes[i2].pit	// bottom note with accidental
			dx2 = notes[i2].shac
			if (!dx2) {
				dx2 = notes[i2].shhd
				if (dx2 < 0)
					dx2 = dx_head - dx2
				else
					dx2 = dx_head
			}
			break
		}
	}
	if (i1 == i2) {			// only one accidental
		notes[i1].shac = dx1
		return
	}

	if (p1 > ps + 4) {		// if interval greater than a sixth
		if (dx1 > dx2)
			dx2 = dx1	// align the accidentals
		notes[i1].shac = notes[i2].shac = dx2
	} else {
		notes[i1].shac = dx1
		notes[i2].shac = dx2 = dx1 + 7
	}
	dx2 += 7

	// shift the remaining accidentals
	for (i = i1; --i > i2; ) {		// from top to bottom
		acc = notes[i].acc
		if (!acc)
			continue
		dx = notes[i].shac
		if (dx < dx2)
			dx = dx2
		ps = notes[i].pit
		for (i1 = n; --i1 > i; ) {
			if (!notes[i1].acc)
				continue
			p1 = notes[i1].pit
			if (p1 >= ps + 4) {	// pitch far enough
				if (p1 > ps + 4	// if more than a fifth
︙
190
191
192
193
194
195
196
197

198
199
200
201
202
203
204
279
280
281
282
283
284
285

286
287
288
289
290
291
292
293







-
+







		notes[i].shac = dx
	}
}

/* set the horizontal shift of accidentals */
/* this routine is called only once per tune */
function set_acc_shft() {
	var s, s2, st, i, acc, st, t, dx_head;
    var	s, s2, st, i, acc, st, t, dx_head, notes

	// search the notes with accidentals at the same time
	s = tsfirst
	while (s) {
		if (s.type != C.NOTE
		 || s.invis) {
			s = s.ts_next
︙
223
224
225
226
227
228
229
230
231
232
233
234
235
236

237
238
239
240
241



242
243
244
245
246
247
248
312
313
314
315
316
317
318



319
320


321

322



323
324
325
326
327
328
329
330
331
332







-
-
-


-
-
+
-

-
-
-
+
+
+







		}
		if (!acc) {
			s = s2
			continue
		}

		dx_head = dx_tb[s.head]
//		if (s.dur >= C.BLEN * 2 && s.head == C.OVAL)
//		if (s.dur >= C.BLEN * 2)
//			dx_head = 15.8;

		// build a pseudo chord and shift the accidentals
		st = {
			notes: []
		notes = []
		}
		for ( ; s != s2; s = s.ts_next)
			st.notes = st.notes.concat(s.notes);
		sort_pitch(st);
		acc_shift(st.notes, dx_head)
			Array.prototype.push.apply(notes, s.notes)
		notes.sort(abc2svg.pitcmp)
		acc_shift(notes, dx_head)
	}
}

// link a symbol before an other one
function lkvsym(s, next) {	// voice linkage
	s.next = next;
	s.prev = next.prev
︙
482
483
484
485
486
487
488
489

490
491


492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507



508
509
510
511
512
513
514
515
516
517
518

519
520

521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536

537
538
539
540
541

542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558

559
560





561
562
563
564
565
566
567
566
567
568
569
570
571
572

573
574

575
576
577
578
579
580
581
582
583
584
585
586
587
588
589



590
591
592
593
594
595
596
597
598
599
600
601
602

603
604

605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620

621
622
623
624
625

626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642

643
644
645
646
647
648
649
650
651
652
653
654
655
656
657







-
+

-
+
+













-
-
-
+
+
+










-
+

-
+















-
+




-
+
















-
+


+
+
+
+
+







}

// Compute the smallest spacing between symbols according to chord symbols
//	so that they stay at the same offset.
// Constraints:
// - assume the chord symbols are only in the first staff
// - treat only the first chord symbol of each symbol
// - the chord symbol under the staff are ignored
// - the chord symbols under the staff are ignored
function set_w_chs(s) {
    var	i, ch, ch0, s0, dw, x,
    var	i, ch, w0, s0, dw,
	x = 0,
	n = 0

	for ( ; s; s = s.ts_next) {
		if (s.shrink) {
			x += s.shrink;
			n++
		}
		if (!s.a_gch)
			continue
		for (i = 0; i < s.a_gch.length; i++) {
			ch = s.a_gch[i]
			if (ch.type != 'g' || ch.y < 0) // upper chord symbol only
				continue
			if (ch0) {
				if (ch0.w > x + ch.x) {
					dw = (ch0.w - x - ch.x) / n
			if (w0) {
				if (w0 > x + ch.x) {
					dw = (w0 - x - ch.x) / n
					while (1) {
						s0 = s0.ts_next
						if (s0.shrink)
							s0.shrink += dw
						if (s0 == s)
							break
					}
				}
			}
			s0 = s;
			ch0 = ch;
			w0 = ch.wh[0];
			n = 0;
//			x = ch.box ? -2 : 0
//			x = ch.font.box ? -2 : 0
			x = 0
			break
		}
	}
}

// compute the width needed by the left and right annotations
function gchord_width(s, wlnote, wlw) {
    var	gch, w, ix,
	arspc = 0

	for (ix = 0; ix < s.a_gch.length; ix++) {
		gch = s.a_gch[ix]
		switch (gch.type) {
		case '<':		/* left */
			w = gch.w + wlnote
			w = gch.wh[0] + wlnote
			if (w > wlw)
				wlw = w
			break
		case '>':		/* right */
			w = gch.w + s.wr
			w = gch.wh[0] + s.wr
			if (w > arspc)
				arspc = w
			break
		}
	}
	if (s.wr < arspc)
		s.wr = arspc

	return wlw
}

/* -- set the width of a symbol -- */
/* This routine sets the minimal left and right widths wl,wr
 * so that successive symbols are still separated when
 * no extra glue is put between them */
// (possible hook)
function set_width(s) {
Abc.prototype.set_width = function(s) {
    var	s2, i, m, xx, w, wlnote, wlw, acc,
	bar_type, meter, last_acc, n1, n2, esp, tmp

	if (s.play) {			// if play symbol
		s.wl = s.wr = 0
		return
	}

	switch (s.type) {
	case C.NOTE:
	case C.REST:

		/* set the note widths */
		s.wr = wlnote = hw_tb[s.head]
︙
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632

633
634

635
636
637
638
639
640
641
691
692
693
694
695
696
697


698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715


716


717
718

719
720
721
722
723
724
725
726







-
-


















-
-

-
-
+

-
+







			case C.BAR:
			case C.CLEF:
			case C.KEY:
			case C.METER:
				wlnote -= 3
				break
			}
		} else {
			wlnote += 8		// (start of tune)
		}

		/* room for the decorations */
		if (s.a_dd)
			wlnote += deco_width(s)

		/* space for flag if stem goes up on standalone note */
		if (s.beam_st && s.beam_end
		 && s.stem > 0 && s.nflags > 0) {
			if (s.wr < s.xmx + 9)
				s.wr = s.xmx + 9
		}

		/* leave room for dots and set their offset */
		if (s.dots) {
		  if (s.wl == undefined)	// don't recompute if new music line
			switch (s.head) {
			case C.SQUARE:
				s.xmx += 4
				break
			case C.OVALBARS:
			case C.OVAL:
				s.xmx += 2
				s.xmx += 3
				break
			case C.EMPTY:
			case C.OVAL:
				s.xmx += 1
				break
			}
			if (s.wr < s.xmx + 8)
				s.wr = s.xmx + 8
			if (s.dots >= 2)
				s.wr += 3.5 * (s.dots - 1)
︙
660
661
662
663
664
665
666
667

668
669
670
671
672
673
674
745
746
747
748
749
750
751

752
753
754
755
756
757
758
759







-
+







				if ((s.y > 27 && s2.y > 27)
				 || (s.y < -3 && s2.y < -3)) {
					if (wlw < 6)
						wlw = 6
				}

				/* have ties wide enough */
				if (s2.ti1) {
				if (s2.tie) {
					if (wlw < 14)
						wlw = 14
				}
				break
			case C.CLEF:		/* extra space at start of line */
				if (s2.second
				 || s2.clef_small)
︙
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
788
789
790
791
792
793
794


795
796
797
798
799
800
801







-
-







		if (s.a_gch)
			xx = gchord_width(s, xx, xx)
		if (s.a_dd)
			xx += deco_width(s);
		s.wl = xx
		return
	case C.BAR:
		if (s.norepbra)
			break
		bar_type = s.bar_type
			switch (bar_type) {
			case "|":
				w = 5		// 3 + 2
				break
			default:
				w = 2 + 2.8 * bar_type.length
︙
762
763
764
765
766
767
768






769
770
771
772
773
774
775
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864







+
+
+
+
+
+







			s.wl = s.wr = 1		// (!! not 0 !!)
			return
		}
		s.wl = s.clef_small ? 9 : 12
		s.wr = s.clef_small ? 7 : 12
		return
	case C.KEY:
		if ((!s.k_a_acc && !s.k_sf && !s.k_old_sf) // if no accidental
		 || s.k_none				// or no key
		 || s.k_play) {				// or key for play
			s.wl = s.wr = 0			// no width
			return
		}
		s.wl = 3;
		esp = 4
		if (!s.k_a_acc) {
			n1 = s.k_sf			/* new key sig */
			if (s.k_old_sf && (cfmt.cancelkey || n1 == 0))
				n2 = s.k_old_sf	/* old key */
			else
︙
848
849
850
851
852
853
854


855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872

873
874
875
876
877
878

879
880
881
882
883
884
885
886
887
888
889
890
891
892
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962

963






964







965
966
967
968
969
970
971







+
+

















-
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-







		s.wr = wlw + 6
		return
	case C.MREST:
		s.wl = 6;
		s.wr = 66
		return
	case C.GRACE:
		if (s.invis)
			break
		s.wl = set_graceoffs(s);
		s.wr = 0
		if (s.a_ly)
			ly_width(s, wlw)
		return
	case C.STBRK:
		s.wl = s.xmx
		if (s.next && s.next.type == C.CLEF) {
			s.wr = 2
			delete s.next.clef_small	/* big clef */
		} else {
			s.wr = 8
		}
		return
	case C.CUSTOS:
		s.wl = s.wr = 4
		return
	case C.TEMPO:		// no width, but set here the [w,h] of the strings
	case C.TEMPO:		// no width, but build the tempo string
		set_font("tempo")
		if (s.tempo_str1)
			s.tempo_wh1 = strwh(s.tempo_str1)
		if (s.tempo_notes ) {
			s.tempo_str0 = '= '
			if (s.tempo_ca)
		tempo_build(s)
				s.tempo_str0 += s.tempo_ca
			if (s.tempo)
				s.tempo_str0 += s.tempo;
			s.tempo_wh0 = strwh(s.tempo_str0)
		}
		if (s.tempo_str2)
			s.tempo_wh2 = strwh(s.tempo_str2)
		break
	case C.BLOCK:				// no width
	case C.PART:
	case C.REMARK:
	case C.STAVES:
		break
	default:
︙
929
930
931
932
933
934
935
936

937
938
939
940
941
942


943
944
945
946
947
948

949


950
951
952







953
954

955
956
957
958
959
960
961
1008
1009
1010
1011
1012
1013
1014

1015
1016
1017
1018
1019
1020

1021
1022
1023
1024
1025
1026
1027

1028
1029
1030
1031



1032
1033
1034
1035
1036
1037
1038
1039

1040
1041
1042
1043
1044
1045
1046
1047







-
+





-
+
+





-
+

+
+
-
-
-
+
+
+
+
+
+
+

-
+







		else if (len >= C.BLEN / 64)
			i = 1
		else
			i = 0
	}
	l = len - ((C.BLEN / 16 / 8) << i)
	space = space_tb[i]
	if (l != 0) {
	if (l) {
		if (l < 0) {
			space = space_tb[0] * len / (C.BLEN / 16 / 8)
		} else {
			if (i >= 9)
				i = 8
			space += (space_tb[i + 1] - space_tb[i]) * l / len
			space += (space_tb[i + 1] - space_tb[i]) *
					l / ((C.BLEN / 16 / 8) << i)
		}
	}
	return space
}

/* -- set the natural space -- */
// set the natural space
function set_space(s) {
    var	space, prev_time, len,
	s2 = s.ts_prev
	var	s2, space,
		prev_time = s.ts_prev.time,
		len = s.time - prev_time		/* time skip */

	if (s.play)
		return 0
	while (s2.play)				// if play symbol
		s2 = s2.ts_prev
	prev_time = s2.time
	len = s.time - prev_time		// time skip

	if (len == 0) {
	if (!len) {
		switch (s.type) {
		case C.MREST:
			return s.wl
///*fixme:do same thing at start of line*/
//		case C.NOTE:
//		case C.REST:
//			if (s.ts_prev.type == C.BAR) {
︙
991
992
993
994
995
996
997



998
999









1000
1001
1002
1003
1004
1005
1006
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086


1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102







+
+
+
-
-
+
+
+
+
+
+
+
+
+







				return space
			continue
		}
		break
	}

	/* reduce spacing within a beam */
	if (s.dur && len <= C.BLEN / 4) {
		s2 = s
		while (s2) {
	if (!s.beam_st)
		space *= .9			// ex fnnp
			if (!s2.beam_st) {
				space *= .9		// ex fnnp
				break
			}
			s2 = s2.ts_next
			if (!s2 || s2.seqst)
				break
		}
	}

	/* decrease spacing when stem down followed by stem up */
/*fixme:to be done later, after x computed in sym_glue*/
	if (s.type == C.NOTE && s.nflags >= -1
	 && s.stem > 0) {
		var stemdir = true

︙
1032
1033
1034
1035
1036
1037
1038
1039

1040
1041
1042
1043
1044
1045
1046
1128
1129
1130
1131
1132
1133
1134

1135
1136
1137
1138
1139
1140
1141
1142







-
+








// set a fixed spacing inside tuplets
function set_sp_tup(s, s_et) {
    var	s2,
	tim = s.time,
	endtime = s_et.time + s_et.dur,
	ttim = endtime - tim,
	space = time2space(s, ttim / s.tq0) * s.tq0 / ttim
	space = time2space(s, ttim / s.tp[0].q) * s.tp[0].q / ttim

	// start on the second note/rest
	do {
		s = s.ts_next
	} while (!s.seqst)
//fixme: problem when BAR inside the tuplet sequence (in case user error)
//	while (!s.dur)
︙
1108
1109
1110
1111
1112
1113
1114
1115

1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128


1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149

1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170








1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186


1187
1188
1189
1190
1191
1192








1193
1194
1195
1196
1197
1198

1199
1200
1201
1202
1203









1204
1205
1206
1207
1208
1209
1210
1211




1212
1213

1214
1215
1216
1217
1218
1219
1220
1221
1222





1223
1224
1225
1226
1227
1228
1229
1204
1205
1206
1207
1208
1209
1210

1211

1212
1213
1214
1215
1216
1217
1218
1219
1220
1221


1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243

1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259






1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281


1282
1283






1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296

1297
1298
1299
1300


1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313




1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324





1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336







-
+
-










-
-
+
+




















-
+















-
-
-
-
-
-
+
+
+
+
+
+
+
+














-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+





-
+



-
-
+
+
+
+
+
+
+
+
+




-
-
-
-
+
+
+
+


+




-
-
-
-
-
+
+
+
+
+







		shrink: s.wr + 3
	}
	s.next = s.ts_next = bar
	return bar
}

/* -- set the width and space of all symbols -- */
/* this function is called once for the whole tune
// this function is called once for the whole tune
 * then, once per music line up to the first sequence */
function set_allsymwidth() {
    var	maxx, new_val, s_tupc, s_tupn, st, s_chs,
	s = tsfirst,
	s2 = s,
	xa = 0,
	xl = [],
	wr = [],
	ntup = 0

	/* loop on all symbols */
	while (1) {
		maxx = xa
	maxx = xa
	while (1) {
		do {
			if (s.a_gch && !s_chs)
				s_chs = s;
			self.set_width(s);
			st = s.st
			if (xl[st] == undefined)
				xl[st] = 0
			if (wr[st] == undefined)
				wr[st] = 0;
			new_val = xl[st] + wr[st] + s.wl
			if (new_val > maxx)
				maxx = new_val;
			s = s.ts_next
		} while (s && !s.seqst);

		// set the spaces of the time sequence
		s2.shrink = maxx - xa
		if (!ntup)			// if not inside a tuplet sequence
			s2.space = s2.ts_prev ? set_space(s2) : 0

		if (s2.shrink == 0 && s2.space == 0 && s2.type == C.CLEF) {
		if (!s2.shrink && !s2.space && s2.type == C.CLEF) {
			delete s2.seqst;		/* no space */
			s2.time = s2.ts_prev.time
		}
		if (!s)
			break

		// update the min left space per staff
		for (st = 0; st < wr.length; st++)
			wr[st] = 0;
		xa = maxx
		do {
			st = s2.st;
			xl[st] = xa
			if (s2.wr > wr[st])
				wr[st] = s2.wr
			if (s2.tp0		// start of tuplet
			 && ++ntup == 1
			 && !s_tupc)
				s_tupc = s2;	// keep the first tuplet address
			if (s2.te0)		// end of tuplet
				ntup--;
			if (s2.tp) {		// start of tuplet
				if (!ntup
				 && !s_tupc)
					s_tupc = s2 // keep the first tuplet address
				ntup += s2.tp.length
			}
			if (s2.tpe)		// end of tuplet
				ntup -= s2.tpe;
			s2 = s2.ts_next
		} while (!s2.seqst)
	}

	// let the chord symbols at the same offset
	if (s_chs)
		set_w_chs(s_chs)

	// adjust the spacing inside the tuplets
	s = s_tupc
	if (!s)
		return
	do {
		s2 = s;			// start of tuplet
		ntup = 1
		do {			// search the end of the tuplet sequence
		ntup = 0
		while (1) {		// search the end of the tuplet sequence
			s = s.ts_next
			if (s.tp0)
				ntup++
			if (s.te0)
				ntup--
		} while (ntup != 0);
			if (s.tp)
				ntup += s.tp.length
			if (s.tpe)
				ntup -= s.tpe
			if (!ntup)
				break
			s = s.ts_next
		}

		set_sp_tup(s2, s)

		do {			// search next tuplet
			s = s.ts_next
		} while (s && !s.tp0)
		} while (s && !s.tp)
	} while (s)
}

/* change a symbol into a rest */
function to_rest(s) {
// insert a rest, this one replacing a sequence or a measure
function to_rest(so) {
    var	s = clone(so)

	s.prev.next = so.ts_prev = so.prev = s.ts_prev.ts_next = s
	s.next = s.ts_next = so
	so.seqst = false
	so.invis = so.play = true

	s.type = C.REST
// just keep nl and seqst
	delete s.in_tuplet
	delete s.sl1
	delete s.sl2
	delete s.a_dd
	delete s.a_gch
	s.slur_start = s.slur_end = 0
	delete s.a_dd
	delete s.a_gch
	delete s.sls
//fixme: what if chord / slur in notes / ... ?
/*fixme: should set many parameters for set_width*/
//	set_width(s)
	return s
}

/* -- set the repeat sequences / measures -- */
function set_repeat(s) {	// first note
	var	s2, s3,  i, j, dur,
		n = s.repeat_n,
		k = s.repeat_k,
		st = s.st,
		v = s.v
    var	s2, s3, i, j, dur,
	n = s.repeat_n,
	k = s.repeat_k,
	st = s.st,
	v = s.v

	s.repeat_n = 0				// treated

	/* treat the sequence repeat */
	if (n < 0) {				/* number of notes / measures */
		n = -n;
		i = n				/* number of notes to repeat */
︙
1270
1271
1272
1273
1274
1275
1276


1277

1278
1279
1280
1281
1282
1283
1284

1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1377
1378
1379
1380
1381
1382
1383
1384
1385

1386
1387
1388
1389
1390
1391
1392

1393
1394
1395
1396
1397


1398
1399
1400
1401
1402
1403
1404







+
+
-
+






-
+




-
-







		for (j = k; --j >= 0; ) {
			i = n			/* number of notes/rests */
			if (s.dur)
				i--;
			s2 = s.ts_next
			while (i > 0) {
				if (s2.st == st) {
					s2.invis = s2.play = true
					if (s2.seqst && s2.ts_next.seqst)
					unlksym(s2)
						s2.seqst = false
					if (s2.v == v
					 && s2.dur)
						i--
				}
				s2 = s2.ts_next
			}
			to_rest(s);
			s = to_rest(s)
			s.dur = s.notes[0].dur = dur;
			s.rep_nb = -1;		// single repeat
			s.beam_st = true;
			self.set_width(s)
			if (s.seqst)
				s.space = set_space(s);
			s.head = C.SQUARE;
			for (s = s2; s; s = s.ts_next) {
				if (s.st == st
				 && s.v == v
				 && s.dur)
					break
			}
︙
1346
1347
1348
1349
1350
1351
1352

1353

1354
1355
1356






1357
1358

1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369

1370

1371
1372
1373






1374
1375

1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391

1392

1393
1394
1395






1396
1397


1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1453
1454
1455
1456
1457
1458
1459
1460

1461



1462
1463
1464
1465
1466
1467
1468

1469
1470
1471


1472


1473
1474
1475
1476
1477

1478



1479
1480
1481
1482
1483
1484
1485

1486
1487
1488
1489




1490
1491
1492
1493
1494
1495
1496
1497
1498
1499

1500



1501
1502
1503
1504
1505
1506
1507

1508
1509
1510
1511




1512
1513
1514
1515
1516
1517
1518







+
-
+
-
-
-
+
+
+
+
+
+

-
+


-
-

-
-




+
-
+
-
-
-
+
+
+
+
+
+

-
+



-
-
-
-









+
-
+
-
-
-
+
+
+
+
+
+

-
+
+


-
-
-
-







	/* replace */
	dur /= n
	if (n == 2) {			/* repeat 2 measures (once) */
		s3 = s
		for (s2 = s.ts_next; ; s2 = s2.ts_next) {
			if (s2.st != st)
				continue
			if (s2.type == C.BAR) {
			if (s2.v == v
				if (s2.v == v)
			 && s2.type == C.BAR)
				break
			unlksym(s2)
					break
				continue
			}
			s2.invis = s2.play = true
			if (s2.seqst && s2.ts_next.seqst)
				s2.seqst = false
		}
		to_rest(s3);
		s3 = to_rest(s3)
		s3.dur = s3.notes[0].dur = dur;
		s3.invis = true
		if (s3.seqst)
			s3.space = set_space(s3);
		s2.bar_mrep = 2
		if (s2.seqst)
			s2.space = set_space(s2);
		s3 = s2.next;
		for (s2 = s3.ts_next; ; s2 = s2.ts_next) {
			if (s2.st != st)
				continue
			if (s2.type == C.BAR) {
			if (s2.v == v
				if (s2.v == v)
			 && s2.type == C.BAR)
				break
			unlksym(s2)
					break
				continue
			}
			s2.invis = s2.play = true
			if (s2.seqst && s2.ts_next.seqst)
				s2.seqst = false
		}
		to_rest(s3);
		s3 = to_rest(s3)
		s3.dur = s3.notes[0].dur = dur;
		s3.invis = true;
		self.set_width(s3)
		if (s3.seqst)
			s3.space = set_space(s3)
		if (s2.seqst)
			s2.space = set_space(s2)
		return
	}

	/* repeat 1 measure */
	s3 = s
	for (j = k; --j >= 0; ) {
		for (s2 = s3.ts_next; ; s2 = s2.ts_next) {
			if (s2.st != st)
				continue
			if (s2.type == C.BAR) {
			if (s2.v == v
				if (s2.v == v)
			 && s2.type == C.BAR)
				break
			unlksym(s2)
					break
				continue
			}
			s2.invis = s2.play = true
			if (s2.seqst && s2.ts_next.seqst)
				s2.seqst = false
		}
		to_rest(s3);
		s3 = to_rest(s3)

		s3.dur = s3.notes[0].dur = dur;
		s3.beam_st = true
		if (s3.seqst)
			s3.space = set_space(s3)
		if (s2.seqst)
			s2.space = set_space(s2)
		if (k == 1) {
			s3.rep_nb = 1
			break
		}
		s3.rep_nb = k - j + 1;	// number to print above the repeat rest
		s3 = s2.next
	}
︙
1450
1451
1452
1453
1454
1455
1456
1457

1458
1459
1460

1461
1462
1463
1464
1465
1466
1467
1468

1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1558
1559
1560
1561
1562
1563
1564

1565
1566
1567

1568
1569
1570
1571





1572



1573
1574
1575
1576
1577
1578
1579







-
+


-
+



-
-
-
-
-
+
-
-
-







			dur: C.BLEN / 4
		}
	}
	new_s.stemless = true
}

/* -- define the beginning of a new music line -- */
function set_nl(s, eoln) {
function set_nl(s) {
    var	s2, s3, p_voice, done

	// set the end of line marker and ...
	// set the end of line marker
	function set_eol(s) {
		if (cfmt.custos && voice_tb.length == 1)
			custos_add(s)

		// ... set the nl flag if more music
		for (var s2 = s.ts_next; s2; s2 = s2.ts_next) {
			if (s2.seqst) {
				s.nl = true
		s.nl = true
				break
			}
		}
	} // set_eol()

	// set the eol on the next symbol
	function set_eol_next(s) {
		if (!s.next) {		// special case: the voice stops here
			set_eol(s)
			return s
︙
1500
1501
1502
1503
1504
1505
1506
1507




1508

1509
1510
1511
1512
1513
1514
1515
1601
1602
1603
1604
1605
1606
1607

1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620







-
+
+
+
+

+







	if (cfmt.keywarn || cfmt.timewarn) {
		for (s2 = s.ts_next; s2; s2 = s2.ts_next) {
			switch (s2.type) {
			case C.BAR:
			case C.CLEF:
				continue
			case C.KEY:
				if (!cfmt.keywarn)
				if (!cfmt.keywarn
				 || (!s2.k_a_acc && !s2.k_sf && !s2.k_old_sf)
				 || s2.k_none
				 || s2.k_play)		// play only
					continue
				// fall thru
			case C.METER:
				if (s2.type == C.METER && !cfmt.timewarn)
					continue
				s3 = s2.ts_prev;
				if (s3 == s) {		// if next symbol
					s = s2		// advance the eol
					continue
︙
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1649
1650
1651
1652
1653
1654
1655




1656
1657
1658
1659
1660
1661
1662







-
-
-
-







				continue
			}
			if (w_tb[s2.type])
				break		// symbol with a width
		}
	}

	/* if explicit EOLN, cut on the next symbol */
	if (eoln)
		return set_eol_next(s)

	/* if normal symbol, cut here */
	switch (s.type) {
	case C.CLEF:
	case C.BAR:
	case C.STAVES:
		break
	case C.GRACE:			/* don't cut on a grace note */
︙
1633
1634
1635
1636
1637
1638
1639


1640
1641
1642
1643




1644
1645
1646
1647

1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658


1659

1660
1661
1662
1663
1664
1665
1666
1667
1668


1669



1670
1671


1672
1673


1674
1675
1676
1677







1678

1679
1680
1681
1682
1683
1684
1685
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743



1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765

1766
1767
1768
1769
1770
1771
1772
1773


1774
1775
1776
1777
1778
1779


1780
1781


1782
1783
1784



1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800







+
+

-
-
-
+
+
+
+




+











+
+
-
+







-
-
+
+

+
+
+
-
-
+
+
-
-
+
+

-
-
-
+
+
+
+
+
+
+

+







	self.set_width(p_voice.meter)
	return [p_voice.clef.wl + p_voice.clef.wr +
			p_voice.key.wl + p_voice.key.wr,
		p_voice.meter.wl + p_voice.meter.wr]
}

// get the width of the symbols up to the next eoln or eof
// also, set a x (nice spacing) to all symbols
// two returned values: width of nice spacing, width with max shrinking
function get_width(s, last) {
	var	shrink, space,
		w = 0,
		sp_fac = (1 - cfmt.maxshrink)
    var	shrink, space,
	w = 0,
	wmx = 0,
	sp_fac = (1 - cfmt.maxshrink)

	do {
		if (s.seqst) {
			shrink = s.shrink
			wmx += shrink
			if ((space = s.space) < shrink)
				w += shrink
			else
				w += shrink * cfmt.maxshrink
					+ space * sp_fac
			s.x = w
		}
		if (s == last)
			break
		s = s.ts_next
	} while (s)
	if (last)
		wmx += last.wr		// big key signatures may be wide enough
	return w;
	return [w, wmx]
}

/* -- search where to cut the lines according to the staff width -- */
function set_lines(	s,		/* first symbol */
			last,		/* last symbol / null */
			lwidth,		/* w - (clef & key sig) */
			indent) {	/* for start of tune */
	var	first, s2, s3, x, xmin, xmid, xmax, wwidth, shrink, space,
		nlines, cut_here;
    var	first, s2, s3, x, xmin, xmid, xmax, wwidth, shrink, space, ws,
	nlines, cut_here

	// take care of big key signatures at end of line
	if (cfmt.keywarn && last && last.type == C.BAR
	 && last.next && last.next.time == last.next.time
	for ( ; last; last = last.ts_next) {
		if (last.eoln)
	 && last.next.type == C.KEY)
		last = last.next
			break
	}

	ws = get_width(s, last)		// 2 widths: nice and shrinked

	/* calculate the whole size of the piece of tune */
	wwidth = get_width(s, last) + indent

	// check if the symbols can enter in one line
	if (ws[1] + indent < lwidth) {
		if (last)
			last = set_nl(last)
		return last
	}

	/* loop on cutting the tune into music lines */
	wwidth = ws[0] + indent
	while (1) {
		nlines = Math.ceil(wwidth / lwidth)
		if (nlines <= 1) {
			if (last)
				last = set_nl(last)
			return last
		}
︙
1814
1815
1816
1817
1818
1819
1820
1821

1822
1823
1824
1825
1826
1827
1828
1829

1830
1831
1832

1833
1834
1835
1836
1837
1838
1839
1929
1930
1931
1932
1933
1934
1935

1936
1937
1938
1939
1940
1941
1942
1943

1944
1945
1946

1947
1948
1949
1950
1951
1952
1953
1954







-
+







-
+


-
+







		indent = 0
	}
	return s
}

/* -- cut the tune into music lines -- */
function cut_tune(lwidth, indent) {
	var	s, s2, s3, i, xmin, lwx,
    var	s2, i,
//fixme: not usable yet
//		pg_sav = {
//			leftmargin: cfmt.leftmargin,
//			rightmargin: cfmt.rightmargin,
//			pagewidth: cfmt.pagewidth,
//			scale: cfmt.scale
//		},
		s = tsfirst
	s = tsfirst

	// take care of the voice subnames
	if (indent != 0) {
	if (indent) {
		i = set_indent()
		lwidth -= i;
		indent -= i;
	}

	/* adjust the line width according to the starting clef
	 * and key signature */
︙
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881


1882
1883
1884
1885
1886
1887


1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900

1901
1902
1903


1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1970
1971
1972
1973
1974
1975
1976


1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992


1993
1994






1995
1996













1997



1998
1999


2000
2001
2002
2003
2004
2005
2006
2007
2008

2009
2010
2011
2012
2013
2014
2015







-
-
















-
-
+
+
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
+
-
-









-







				continue
			s2.eoln = true;
			i = cfmt.barsperstaff
		}
	}

	/* cut at explicit end of line, checking the line width */
	xmin = indent;
	lwx = lwidth * cfmt.maxshrink;
	s2 = s
	for ( ; s; s = s.ts_next) {
//fixme: not usable yet
//		if (s.type == C.BLOCK) {
//			switch (s.subtype) {
//			case "leftmargin":
//			case "rightmargin":
//			case "pagescale":
//			case "pagewidth":
//			case "scale":
//			case "staffwidth":
//				set_format(s.subtype, s.param)
//				break
//			}
//			continue
//		}
		if (!s.seqst && !s.eoln)
			continue
		if (!s.ts_next)
			s = null
		xmin += s.shrink
		if (xmin > lwx) {		// overflow
			s2 = set_lines(s2, s, lwidth, indent)
		} else {
			if (!s.eoln)
				continue
		else if (!s.eoln)
			continue
			delete s.eoln

			// if eoln on a note or a rest,
			// check for a smaller duration in an other voice
			if (s.dur) {
				for (s3 = s.ts_next; s3; s3 = s3.ts_next) {
					if (s3.seqst
					 || s3.dur < s.dur)
						break
				}
				if (s3 && !s3.seqst)
					s2 = set_lines(s2, s, lwidth, indent)
				else
		else
					s2 = set_nl(s, true)
			} else {
				s2 = set_nl(s, true)
			s.eoln = false
		s2 = set_lines(s2, s, lwidth, indent)
			}
		}
		if (!s2)
			break

		// (s2 may be tsfirst - no ts_prev - when only one
		//  embedded info in the first line after the first K:)
		if (!s2.ts_prev) {
			delete s2.nl
			continue
		}
		xmin = s2.shrink;
		s = s2.ts_prev;		// don't miss an eoln
		indent = 0
	}

//fixme: not usable yet
//	// restore the page parameters at start of line
//	cfmt.leftmargin = pg_sav.leftmargin;
︙
1985
1986
1987
1988
1989
1990
1991






































































1992
1993
1994
1995
1996
1997
1998
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	default:
//		s.ymx = top;
		s.ymx = 24;
		s.ymn = 0
		break
	}
}

// set the ottava decorations and the associated pitch shift
function set_ottava() {
    var	s, st, delta, note, g, o,
	m = nstaff + 1,
	staff_d = new Int16Array(new Array(m * 2)),	// (-ottava)
	staff_noo = new Int8Array(new Array(m))		// number of ottava values

	// add an ottava decoration
	function ottava_add(s, ottava, start) {
	    var	dc_st = ["15mb(", "8vb(", null, "8va(", "15ma("],
		dc_en = ["15mb)", "8vb)", null, "8va)", "15ma)"];

		deco_cnv([(start ? dc_st : dc_en)[2 + ottava]], s)
	}

	for (st = 0; st <= nstaff; st++) {
		staff_d[st] = 0;
		staff_noo[st] = 0
	}
	for (s = tsfirst; s; s = s.ts_next) {
		st = s.st
		if (s.ottava != undefined)
		    while (s.ottava.length) {
			o = s.ottava.shift()
			if (o) {
				if (staff_noo[st]++ == 0) {	// if first ottava
					ottava_add(s, o, true)
					staff_d[st] = -o * 7
				}
			} else {
				if (--staff_noo[st] == 0) {	// if last ottava
					ottava_add(s, -staff_d[st] / 7);
					staff_d[st] = 0
				}
			}
		}
		switch (s.type) {
		case C.REST:
			if (voice_tb.length == 1)
				break
		case C.NOTE:
			delta = staff_d[st]
			if (delta
			 && !s.p_v.key.k_drum) {
				for (m = s.nhd; m >= 0; m--) {
					note = s.notes[m];
					if (!note.opit)
						note.opit = note.pit;
					note.pit += delta
				}
			}
			break
		case C.GRACE:
			for (g = s.extra; g; g = g.next) {
				delta = staff_d[st]
				if (delta
				 && !s.p_v.key.k_drum) {
					for (m = 0; m <= g.nhd; m++) {
						note = g.notes[m]
						if (!note.opit)
							note.opit = note.pit
						note.pit += delta
					}
				}
			}
			break
		}
	}
}

// set the clefs (treble or bass) in a 'auto clef' sequence
// return the starting clef type
function set_auto_clef(st, s_start, clef_type_start) {
	var s, min, max, time, s2, s3;

	/* get the max and min pitches in the sequence */
︙
2136
2137
2138
2139
2140
2141
2142
2143

2144
2145
2146
2147
2148

2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162

2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174

2175
2176
2177



2178
2179
2180
2181

2182
2183
2184
2185
2186
2187
2188
2299
2300
2301
2302
2303
2304
2305

2306
2307
2308
2309
2310

2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324

2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336

2337



2338
2339
2340
2341
2342
2343

2344
2345
2346
2347
2348
2349
2350
2351







-
+




-
+













-
+











-
+
-
-
-
+
+
+



-
+







 *	- staff_tb[st].clef = clefs at start of line (here, start of tune)
 *				(created here, updated on clef draw)
 *	- voice_tb[v].clef = clefs at end of generation
 *				(created on voice creation, updated here)
 */
function set_clefs() {
	var	s, s2, st, v, p_voice, g, new_type, new_line, p_staff, pit,
		staff_clef = new Array(nstaff),	// st -> { clef, autoclef }
		staff_clef = new Array(nstaff + 1),	// st -> { clef, autoclef }
		sy = cur_sy,
		mid = []

	// create the staff table
	staff_tb = new Array(nstaff)
	staff_tb = new Array(nstaff + 1)
	for (st = 0; st <= nstaff; st++) {
		staff_clef[st] = {
			autoclef: true
		}
		staff_tb[st] = {
			output: "",
			sc_out: ""
		}
	}

	// set the starting clefs of the staves
	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (sy.voices[v].range < 0)
		if (!sy.voices[v])
			continue
		st = sy.voices[v].st
		if (!sy.voices[v].second) {		// main voices
			if (p_voice.staffnonote != undefined)
				sy.staves[st].staffnonote = p_voice.staffnonote
			if (p_voice.staffscale)
				sy.staves[st].staffscale = p_voice.staffscale
			if (sy.voices[v].sep)
				sy.staves[st].sep = sy.voices[v].sep
			if (sy.voices[v].maxsep)
				sy.staves[st].maxsep = sy.voices[v].maxsep;
		}

		if (!sy.voices[v].second
		 && !p_voice.clef.clef_auto)
			staff_clef[st].autoclef = false
			if (!p_voice.clef.clef_auto)
				staff_clef[st].autoclef = false
		}
	}
	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (sy.voices[v].range < 0
		if (!sy.voices[v]
		 || sy.voices[v].second)		// main voices
			continue
		st = sy.voices[v].st;
		s = p_voice.clef
		if (staff_clef[st].autoclef) {
			s.clef_type = set_auto_clef(st,
						tsfirst,
︙
2200
2201
2202
2203
2204
2205
2206
2207

2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228

2229
2230
2231
2232
2233
2234
2235
2363
2364
2365
2366
2367
2368
2369

2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390

2391
2392
2393
2394
2395
2396
2397
2398







-
+




















-
+








		switch (s.type) {
		case C.STAVES:
			sy = s.sy
			for (st = 0; st <= nstaff; st++)
				staff_clef[st].autoclef = true
			for (v = 0; v < voice_tb.length; v++) {
				if (sy.voices[v].range < 0)
				if (!sy.voices[v])
					continue
				p_voice = voice_tb[v];
				st = sy.voices[v].st
				if (!sy.voices[v].second) {
					if (p_voice.staffnonote != undefined)
						sy.staves[st].staffnonote = p_voice.staffnonote
					if (p_voice.staffscale)
						sy.staves[st].staffscale = p_voice.staffscale
					if (sy.voices[v].sep)
						sy.staves[st].sep = sy.voices[v].sep
					if (sy.voices[v].maxsep)
						sy.staves[st].maxsep = sy.voices[v].maxsep
				}
				s2 = p_voice.clef
				if (!s2.clef_auto)
					staff_clef[st].autoclef = false
			}
			for (st = 0; st <= sy.nstaff; st++)
				mid[st] = (sy.staves[st].stafflines.length - 1) * 3
			for (v = 0; v < voice_tb.length; v++) {
				if (sy.voices[v].range < 0
				if (!sy.voices[v]
				 || sy.voices[v].second)	// main voices
					continue
				p_voice = voice_tb[v];
				st = sy.voices[v].st;
				s2 = p_voice.clef
				if (s2.clef_auto) {
//fixme: the staff may have other voices with explicit clefs...
︙
2323
2324
2325
2326
2327
2328
2329
2330

2331
2332
2333
2334
2335
2336
2337
2486
2487
2488
2489
2490
2491
2492

2493
2494
2495
2496
2497
2498
2499
2500







-
+







		}
		staff_clef[st].clef = s
	}

	/* set a pitch to the symbols of voices with no note */
	sy = cur_sy
	for (v = 0; v < voice_tb.length; v++) {
		if (sy.voices[v].range < 0)
		if (!sy.voices[v])
			continue
		s2 = voice_tb[v].sym
		if (!s2 || s2.notes[0].pit != 127)
			continue
		st = sy.voices[v].st
		switch (staff_tb[st].clef.clef_type) {
		default:
︙
2372
2373
2374
2375
2376
2377
2378
2379

2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454


2455
2456
2457

2458
2459
2460
2461
2462
2463
2464
2535
2536
2537
2538
2539
2540
2541

2542
2543
2544
2545

2546


2547
2548








2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561



2562
2563
2564
2565


















2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578

2579
2580
2581
2582


2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595







-
+



-

-
-


-
-
-
-
-
-
-
-













-
-
-




-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-













-




-
-
+
+



+







	[6, 4],
	[10, 0],
	[10, 4],
	[10, 10]
]

// (possible hook)
function set_pitch(last_s) {
Abc.prototype.set_pitch = function(last_s) {
	var	s, s2, g, st, delta, pitch, note,
		dur = C.BLEN,
		m = nstaff + 1,
		staff_d = new Int16Array(new Array(m * 2)),	// (delta - ottava)
		staff_delta = new Int16Array(new Array(m * 2)),	// delta clef
		staff_ottava = new Int16Array(new Array(m * 2)), // ottava value
		staff_noo = new Int8Array(new Array(m)),	// number of ottava values
		sy = cur_sy

	// add an ottava decoration
	function ottava_add(s, ottava, start) {
	    var	dc_st = ["15mb(", "8vb(", null, "8va(", "15ma("],
		dc_en = ["15mb)", "8vb)", null, "8va)", "15ma)"];

		deco_cnv([(start ? dc_st : dc_en)[2 + ottava]], s)
	}

	// set the starting clefs of the staves
	for (st = 0; st <= nstaff; st++) {
		s = staff_tb[st].clef;
		staff_delta[st] = delta_tb[s.clef_type] + s.clef_line * 2
		if (s.clefpit)
			staff_delta[st] += s.clefpit
		if (cfmt.sound) {
			if (s.clef_octave && !s.clef_oct_transp)
				staff_delta[st] += s.clef_octave
		} else {
			if (s.clef_oct_transp)
				staff_delta[st] -= s.clef_octave
		}
		staff_d[st] = staff_delta[st];
		staff_ottava[st] = 0;
		staff_noo[st] = 0
	}

	for (s = tsfirst; s != last_s; s = s.ts_next) {
		st = s.st

		// set the ottava decorations and the associated pitch shift
		if (s.ottava != undefined && !cfmt.sound) {
			if (s.ottava) {
				if (staff_noo[st]++ == 0) {	// if first ottava
					ottava_add(s, s.ottava, true);
					staff_ottava[st] = s.ottava;
					staff_d[st] = staff_delta[st] - s.ottava * 7
				}
			} else {
				if (--staff_noo[st] == 0) {	// if last ottava
					ottava_add(s, staff_ottava[st]);
					staff_ottava[st] = s.ottava;
					staff_d[st] = staff_delta[st]
				}
			}
		}

		switch (s.type) {
		case C.CLEF:
			staff_delta[st] = delta_tb[s.clef_type] +
						s.clef_line * 2
			if (s.clefpit)
				staff_delta[st] += s.clefpit
			if (cfmt.sound) {
				if (s.clef_octave && !s.clef_oct_transp)
					staff_delta[st] += s.clef_octave
			} else {
				if (s.clef_oct_transp)
					staff_delta[st] -= s.clef_octave
			}
			staff_d[st] = staff_delta[st] - staff_ottava[st] * 7;
			set_yval(s)
			break
		case C.GRACE:
			for (g = s.extra; g; g = g.next) {
				delta = staff_d[g.st]
				if (delta != 0
				delta = staff_delta[g.st]
				if (delta
				 && !s.p_v.key.k_drum) {
					for (m = 0; m <= g.nhd; m++) {
						note = g.notes[m];
						note.opit = note.pit
						note.pit += delta
					}
				}
				g.ymn = 3 * (g.notes[0].pit - 18) - 2;
				g.ymx = 3 * (g.notes[g.nhd].pit - 18) + 2
			}
			set_yval(s)
︙
2483
2484
2485
2486
2487
2488
2489
2490
2491


2492
2493
2494
2495



2496
2497
2498
2499
2500
2501
2502
2614
2615
2616
2617
2618
2619
2620


2621
2622
2623
2624


2625
2626
2627
2628
2629
2630
2631
2632
2633
2634







-
-
+
+


-
-
+
+
+







//				s.ymn = 12 - 8
				s.ymx = 24;
				s.ymn = 0
				break
			}
			// fall thru
		case C.NOTE:
			delta = staff_d[st]
			if (delta != 0
			delta = staff_delta[st]
			if (delta
			 && !s.p_v.key.k_drum) {
				for (m = s.nhd; m >= 0; m--) {
					s.notes[m].opit = s.notes[m].pit;
					s.notes[m].pit += delta
					note = s.notes[m]
					note.opit = note.pit
					note.pit += delta
				}
			}
			if (s.type == C.NOTE) {
				s.ymx = 3 * (s.notes[s.nhd].pit - 18) + 4;
				s.ymn = 3 * (s.notes[0].pit - 18) - 4;
			} else {
				s.y = (((s.notes[0].pit - 18) / 2) | 0) * 6;
︙
2511
2512
2513
2514
2515
2516
2517
2518

2519
2520
2521
2522
2523
2524
2525
2643
2644
2645
2646
2647
2648
2649

2650
2651
2652
2653
2654
2655
2656
2657







-
+







	if (!last_s)
		smallest_duration = dur
}

/* -- set the stem direction when multi-voices -- */
/* this function is called only once per tune */
// (possible hook)
function set_stem_dir() {
Abc.prototype.set_stem_dir = function() {
	var	t, u, i, st, rvoice, v,
		v_st,			// voice -> staff 1 & 2
		st_v, vobj,		// staff -> (v, ymx, ymn)*
		v_st_tb,		// array of v_st
		st_v_tb = [],		// array of st_v
		s = tsfirst,
		sy = cur_sy,
︙
2629
2630
2631
2632
2633
2634
2635

2636
2637
2638


2639
2640
2641
2642
2643
2644
2645
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780







+



+
+







			case C.REST:
				// handle %%voicecombine 0
				if ((s.combine != undefined && s.combine < 0)
				 || !s.ts_next || s.ts_next.type != C.REST
				 || s.ts_next.st != s.st
				 || s.time != s.ts_next.time
				 || s.dur != s.ts_next.dur
				 || (s.a_dd && s.ts_next.a_dd)
				 || (s.a_gch && s.ts_next.a_gch)
				 || s.invis)
					break
				if (s.ts_next.a_dd)
					s.a_dd = s.ts_next.a_dd
				if (s.ts_next.a_gch)
					s.a_gch = s.ts_next.a_gch
				unlksym(s.ts_next)
				break
			case C.NOTE:
			case C.GRACE:
				break
︙
2850
2851
2852
2853
2854
2855
2856

2857

2858
2859

2860
2861
2862
2863
2864
2865


2866
2867

2868
2869
2870
2871
2872
2873
2874









2875
2876
2877

2878
2879

2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893

2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910

2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922

2923
2924
2925
2926
2927
2928
2929
2985
2986
2987
2988
2989
2990
2991
2992

2993


2994






2995
2996


2997







2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008

3009
3010

3011
3012
3013
3014

3015
3016
3017
3018
3019
3020
3021
3022
3023

3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040

3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052

3053
3054
3055
3056
3057
3058
3059
3060







+
-
+
-
-
+
-
-
-
-
-
-
+
+
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+


-
+

-
+



-









-
+
















-
+











-
+







			s.xmx = dx
			continue
		}
	}
}

/* -- create a starting symbol -- */
// last_s = symbol at same time
function new_sym(type, p_voice,
function new_sym(s, p_v, last_s) {
			last_s) {	/* symbol at same time */
	var s = {
	s.p_v = p_v
		type: type,
		fname: last_s.fname,
//		istart: last_s.istart,
//		iend: last_s.iend,
		v: p_voice.v,
		p_v: p_voice,
	s.v = p_v.v
	s.st = p_v.st
		st: p_voice.st,
		time: last_s.time,
	s.time = last_s.time
		next: p_voice.last_sym.next
	}
	if (s.next)
		s.next.prev = s;
	p_voice.last_sym.next = s;
	s.prev = p_voice.last_sym;
	p_voice.last_sym = s;

	if (p_v.last_sym) {
		s.next = p_v.last_sym.next
		if (s.next)
			s.next.prev = s;
		p_v.last_sym.next = s;
		s.prev = p_v.last_sym
	}
	p_v.last_sym = s;

	lktsym(s, last_s)
	if (s.ts_prev.type != type)
	if (s.ts_prev.type != s.type)
		s.seqst = true
	if (last_s.type == type && s.v != last_s.v) {
	if (last_s.type == s.type && s.v != last_s.v) {
		delete last_s.seqst;
		last_s.shrink = 0
	}
	return s
}

/* -- init the symbols at start of a music line -- */
function init_music_line() {
	var	p_voice, s, s2, s3, last_s, v, st, shr, shrmx,
		nv = voice_tb.length

	/* initialize the voices */
	for (v = 0; v < nv; v++) {
		if (cur_sy.voices[v].range < 0)
		if (!cur_sy.voices[v])
			continue
		p_voice = voice_tb[v];
		p_voice.second = cur_sy.voices[v].second;
		p_voice.last_sym = p_voice.sym;

		/* move the voice to a printed staff */
		st = cur_sy.voices[v].st
		while (st < nstaff && !cur_sy.st_print[st])
			st++;
		p_voice.st = st
	}

	/* add a clef at start of the main voices */
	last_s = tsfirst
	while (last_s && last_s.type == C.CLEF) {	// move the starting clefs
		v = last_s.v
		if (cur_sy.voices[v].range >= 0
		if (cur_sy.voices[v]
		 && !cur_sy.voices[v].second) {
			delete last_s.clef_small;	/* normal clef */
			p_voice = last_s.p_v;
			p_voice.last_sym = p_voice.sym = last_s
		}
		last_s = last_s.ts_next
	}
	for (v = 0; v < nv; v++) {
		p_voice = voice_tb[v]
		if (p_voice.sym && p_voice.sym.type == C.CLEF)
			continue
		if (cur_sy.voices[v].range < 0
		if (!cur_sy.voices[v]
		 || (cur_sy.voices[v].second
		  && !p_voice.bar_start))	// needed for correct linkage
			continue
		st = cur_sy.voices[v].st
		if (!staff_tb[st]
		 || !staff_tb[st].clef)
			continue
︙
2960
2961
2962
2963
2964
2965
2966
2967

2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979

2980

2981
2982

2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000

3001
3002
3003

3004
3005
3006
3007
3008
3009

3010

3011
3012
3013
3014
3015
3016
3017


















3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034

3035
3036
3037
3038
3039
3040
3041
3042
3043
3044

3045
3046
3047
3048
3049
3050
3051
3052


3053
3054

3055
3056

3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084

3085
3086

3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098













3099
3100
3101

3102
3103
3104
3105
3106
3107
3108
3091
3092
3093
3094
3095
3096
3097

3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111

3112


3113









3114
3115
3116
3117
3118
3119
3120
3121

3122
3123
3124

3125
3126
3127
3128
3129
3130
3131
3132

3133




3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170

3171
3172
3173
3174







3175








3176
3177


3178


3179



















3180
3181
3182
3183
3184
3185
3186
3187

3188


3189



3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222







-
+












+
-
+
-
-
+
-
-
-
-
-
-
-
-
-








-
+


-
+






+
-
+
-
-
-
-



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
















-
+



-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
+
+
-
-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-








-
+
-
-
+
-
-
-









+
+
+
+
+
+
+
+
+
+
+
+
+



+







// (fixme: needed for sample5 X:3 Fugue & staffnonote.xhtml)
		if (!cur_sy.st_print[st])
			s.invis = true
	}

	/* add keysig */
	for (v = 0; v < nv; v++) {
		if (cur_sy.voices[v].range < 0
		if (!cur_sy.voices[v]
		 || cur_sy.voices[v].second
		 || !cur_sy.st_print[cur_sy.voices[v].st])
			continue
		p_voice = voice_tb[v]
		if (last_s && last_s.v == v && last_s.type == C.KEY) {
			p_voice.last_sym = last_s;
			last_s.k_old_sf = last_s.k_sf;	// no key cancel
			last_s = last_s.ts_next
			continue
		}
		s2 = p_voice.key
		if (s2.k_sf || s2.k_a_acc) {
			s = clone(s2)
			s = new_sym(C.KEY, p_voice, last_s);
			new_sym(s, p_voice, last_s)
			s.k_sf = s2.k_sf;
			s.k_old_sf = s2.k_sf;	// no key cancel
			s.k_old_sf = s2.k_sf	// no key cancel
			s.k_none = s2.k_none;
			s.k_a_acc = s2.k_a_acc;
			s.istart = s2.istart;
			s.iend = s2.iend
			if (s2.k_bagpipe) {
				s.k_bagpipe = s2.k_bagpipe
				if (s.k_bagpipe == 'p')
					s.k_old_sf = 3	/* "A" -> "D" => G natural */
			}
		}
	}

	/* add time signature (meter) if needed */
	if (insert_meter & 1) {
		for (v = 0; v < nv; v++) {
			p_voice = voice_tb[v];
			s2 = p_voice.meter
			if (cur_sy.voices[v].range < 0
			if (!cur_sy.voices[v]
			 || cur_sy.voices[v].second
			 || !cur_sy.st_print[cur_sy.voices[v].st]
			 || s2.a_meter.length == 0)
			 || !s2.a_meter.length)
				continue
			if (last_s && last_s.v == v && last_s.type == C.METER) {
				p_voice.last_sym = last_s;
				last_s = last_s.ts_next
				continue
			}
			s = clone(s2)
			s = new_sym(C.METER, p_voice, last_s);
			new_sym(s, p_voice, last_s)
			s.istart = s2.istart;
			s.iend = s2.iend;
			s.wmeasure = s2.wmeasure;
			s.a_meter = s2.a_meter
		}
		insert_meter &= ~1		// no meter any more
	}

	// add an invisible bar for the various continued elements
	for (v = 0; v < nv; v++) {
		p_voice = voice_tb[v]
		if (p_voice.sls.length) {
			s = {
				type: C.BAR,
				fname: last_s.fname,
				bar_type: "|",
				dur: 0,
				multi: 0,
				invis: true,
				sls: p_voice.sls
			}
			new_sym(s, p_voice, last_s)
			p_voice.sls = []
		}
	}

	/* add bar if needed (for repeat bracket) */
	for (v = 0; v < nv; v++) {
		p_voice = voice_tb[v];
		s2 = p_voice.bar_start;
		p_voice.bar_start = null

		// if bar already, keep it in sequence
		if (last_s && last_s.v == v && last_s.type == C.BAR) {
			p_voice.last_sym = last_s;
			last_s = last_s.ts_next
			continue
		}

		if (!s2)
			continue
		if (cur_sy.voices[v].range < 0
		if (!cur_sy.voices[v]
		 || !cur_sy.st_print[cur_sy.voices[v].st])
			continue

		s2.next = p_voice.last_sym.next
		if (s2.next)
			s2.next.prev = s2;
		p_voice.last_sym.next = s2;
		s2.prev = p_voice.last_sym;
		p_voice.last_sym = s2;
		lktsym(s2, last_s);
		new_sym(s2, p_voice, last_s)
		s2.time = tsfirst.time
		if (s2.ts_prev.type != s2.type)
			s2.seqst = true;
		if (last_s && last_s.type == s2.type && s2.v != last_s.v) {
			delete last_s.seqst;
//			last_s.shrink = 0
		}
	}
	}


	/* if initialization of a new music line, compute the spacing,
	// compute the spacing of the added symbols
	 * including the first (old) sequence */
	self.set_pitch(last_s)
	self.set_pitch(last_s);
	for (s = last_s; s; s = s.ts_next) {
		if (s.seqst) {
			for (s = s.ts_next; s; s = s.ts_next)
				if (s.seqst)
					break
			break
		}
	}

	// set the spacing of the added symbols
	while (last_s) {
		if (last_s.seqst) {
			do {
				last_s = last_s.ts_next
			} while (last_s && !last_s.seqst)
			break
		}
		last_s = last_s.ts_next
	}

	s = tsfirst
	while (1) {
		s2 = s;
		shrmx = 0
		do {
			self.set_width(s);
			shr = s.wl
			for (s3 = s.prev; s3; s3 = s3.prev) {
			if (s.prev)
				if (w_tb[s3.type] != 0) {
					shr += s3.wr
				shr += s.prev.wr
					break
				}
			}
			if (shr > shrmx)
				shrmx = shr;
			s = s.ts_next
		} while (s != last_s && !s.seqst);
		s2.shrink = shrmx;
		s2.space = 0
		if (s == last_s)
			break
	}

	// update the spacing before the first old time sequence
	if (!s)
		return
	shr = 0
	do {
		self.set_width(s)
		if (shr < s.wl)
			shr = s.wl;
		s = s.ts_next
	} while (s && !s.seqst);
	last_s.shrink = s2.wr + shr
	last_s.space = 0
}

/* -- set a pitch in all symbols and the start/stop of the beams -- */
// and sort the pitches in the chords
function set_words(p_voice) {
	var	s, s2, nflags, lastnote, res,
		start_flag = true,
		pitch = 127			/* no note */

	for (s = p_voice.sym; s; s = s.next) {
		if (s.type == C.NOTE) {
︙
3121
3122
3123
3124
3125
3126
3127




3128
3129
3130
3131
3132
3133
3134
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252







+
+
+
+







				start_flag = true
			if (!s.next && s.prev
//			 && s.prev.type == C.NOTE
//			 && s.prev.dur >= C.BLEN * 2)
			 && s.prev.head == C.OVALBARS)
				s.prev.head = C.SQUARE
			break
		case C.GRACE:
			for (s2 = s.extra; s2; s2 = s2.next)
				s2.notes.sort(abc2svg.pitcmp)
			break
		case C.NOTE:
		case C.REST:
			res = identify_note(s, s.dur_orig);
			s.head = res[0];
			s.dots = res[1];
			s.nflags = res[2]
			if (s.nflags <= -2)
︙
3186
3187
3188
3189
3190
3191
3192
3193
3194


3195
3196
3197
3198
3199
3200
3201
3304
3305
3306
3307
3308
3309
3310


3311
3312
3313
3314
3315
3316
3317
3318
3319







-
-
+
+







			if (s.beam_end)
				start_flag = true
			if (s.type == C.NOTE)
				lastnote = s
			break
		}
		if (s.type == C.NOTE) {
			if (s.nhd != 0)
				sort_pitch(s);
			if (s.nhd)
				s.notes.sort(abc2svg.pitcmp)
			pitch = s.notes[0].pit
//			if (s.prev
//			 && s.prev.type != C.NOTE) {
//				s.prev.notes[0].pit = (s.prev.notes[0].pit
//						    + pitch) / 2
			for (s2 = s.prev; s2; s2 = s2.prev) {
				if (s2.type != C.REST)
︙
3213
3214
3215
3216
3217
3218
3219
3220
3221


3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241










3242
3243
3244
3245
3246
3247




3248
3249
3250
3251

3252
3253
3254
3255
3256
3257
3258
3259

3260
3261
3262

3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276


3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3331
3332
3333
3334
3335
3336
3337


3338
3339
3340
3341
3342
3343
3344
3345














3346
3347
3348
3349
3350
3351
3352
3353
3354
3355






3356
3357
3358
3359




3360








3361



3362














3363
3364




3365
3366
3367
3368
3369
3370
3371







-
-
+
+






-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
-
-
-
-
+
-
-
-
-
-
-
-
-
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-







	}
	if (lastnote)
		lastnote.beam_end = true
}

/* -- set the end of the repeat sequences -- */
function set_rb(p_voice) {
	var	s2, mx, n,
		s = p_voice.sym
    var	s2, n,
	s = p_voice.sym

	while (s) {
		if (s.type != C.BAR || !s.rbstart || s.norepbra) {
			s = s.next
			continue
		}

		mx = cfmt.rbmax

		/* if 1st repeat sequence, compute the bracket length */
		if (s.text && s.text[0] == '1') {
			n = 0;
			s2 = null
			for (s = s.next; s; s = s.next) {
				if (s.type != C.BAR)
					continue
				n++
				if (s.rbstop) {
					if (n <= cfmt.rbmax) {
						mx = n;
		n = 0;
		s2 = null
		for (s = s.next; s; s = s.next) {
			if (s.type != C.BAR)
				continue
			n++
			if (s.rbstop)
				break
			if (!s.next) {
				s.rbstop = 2	// right repeat with end
						s2 = null
					}
					break
				}
				if (n == cfmt.rbmin)
					s2 = s
				break
			}
			if (n == cfmt.rbmin)
				s2 = s
			}
			if (s2) {
				s2.rbstop = 1;
				mx = cfmt.rbmin
			if (n == cfmt.rbmax) {
			}
		}
		while (s) {

			/* check repbra shifts (:| | |2 in 2nd staves) */
			if (s.rbstart != 2) {
				s = s.next
				if (!s)
				if (s2)
					break
				if (s.rbstart != 2) {
					s = s.next
					s = s2;
					if (!s)
						break
					if (s.rbstart != 2)
						break
				}
			}
			n = 0;
			s2 = null
			for (s = s.next; s; s = s.next) {
				if (s.type != C.BAR)
					continue
				n++
				if (s.rbstop)
					break
				s.rbstop = 1	// right repeat without end
				break
				if (!s.next)
					s.rbstop = 2	// right repeat with end
				else if (n == mx)
					s.rbstop = 1	// right repeat without end
			}
		}
	}
}

/* -- initialize the generator -- */
/* this function is called only once per tune  */
︙
3313
3314
3315
3316
3317
3318
3319



3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334

3335
3336
3337
3338
3339
3340
3341
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420

3421
3422
3423
3424
3425
3426
3427
3428







+
+
+














-
+







// (test removed because v.second may change after %%staves)
//		if (!p_voice.second && !p_voice.norepbra)
			set_rb(p_voice)
	}

	/* set the staff of the floating voices */
	set_float();

	if (glovar.ottava && cfmt.sound != "play")
		set_ottava();

	// set the clefs and adjust the pitches of all symbol
	set_clefs();
	self.set_pitch(null)
}

/* -- return the left indentation of the staves -- */
function set_indent(first) {
	var	st, v, w, p_voice, p, i, j, font,
		nv = voice_tb.length,
		maxw = 0

	for (v = 0; v < nv; v++) {
		p_voice = voice_tb[v]
		if (cur_sy.voices[v].range < 0)
		if (!cur_sy.voices[v])
			continue
		st = cur_sy.voices[v].st
//		if (!cur_sy.st_print[st])
//			continue
		p = ((first || p_voice.new_name) && p_voice.nm) ?
			p_voice.nm : p_voice.snm
		if (!p)
︙
3378
3379
3380
3381
3382
3383
3384
3385
3386


3387
3388
3389
3390
3391
3392
3393
3465
3466
3467
3468
3469
3470
3471


3472
3473
3474
3475
3476
3477
3478
3479
3480







-
-
+
+







		maxw += cfmt.indent
	return maxw
}

/* -- decide on beams and on stem directions -- */
/* this routine is called only once per tune */
function set_beams(sym) {
	var	s, t, g, beam, s_opp, dy, avg, n, m, mid_p, pu, pd,
		laststem = -1
    var	s, t, g, beam, s_opp, n, m, mid_p, pu, pd,
	laststem = -1

	for (s = sym; s; s = s.next) {
		if (s.type != C.NOTE) {
			if (s.type != C.GRACE)
				continue
			g = s.extra
			if (g.stem == 2) {	/* opposite gstem direction */
︙
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419







3420

3421

3422
3423
3424



























3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437













3438
3439
3440




3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451





3452
3453
3454
3455
3456
3457
3458
3459
3460
3496
3497
3498
3499
3500
3501
3502




3503
3504
3505
3506
3507
3508
3509
3510
3511

3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542













3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556


3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567




3568
3569
3570
3571
3572
3573

3574
3575
3576
3577
3578
3579
3580







-
-
-
-
+
+
+
+
+
+
+

+
-
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+







-
-
-
-
+
+
+
+
+

-







			mid_p = s.mid / 3 + 18

			/* notes in a beam have the same stem direction */
			if (beam) {
				s.stem = laststem
			} else if (s.beam_st && !s.beam_end) {	// beam start
				beam = true;
				pu = s.notes[s.nhd].pit;
				pd = s.notes[0].pit
				for (g = s.next; g; g = g.next) {
					if (g.type != C.NOTE)

				// count the number of notes above the mid-line
				n = pu = 0
				for (g = s; g; g = g.next) {
					if (g.type != C.NOTE) {
//						if (g.beam_end)
//							break
						continue
					}
					if (g.stem || g.multi) {
					if (g.stem || g.multi) { // if forced direction
						s.stem = g.stem || g.multi
						break
					}
					n += g.nhd + 1
					for (m = 0; m <= g.nhd; m++) {
						if (g.notes[m].pit >= mid_p)
							pu++
					}
					if (g.beam_end)
						break
				}

				if (!s.stem) {
					pu *= 2
					if (pu > n) {
						s.stem = -1
					} else if (pu < n) {
						s.stem = 1

					// same number of note above and below the mid-line
					// get the highest and lowest pitches
					} else {
						pu = s.notes[s.nhd].pit;
						pd = s.notes[0].pit
						for (g = s.next; g; g = g.next) {
							if (g.type != C.NOTE) {
//								if (g.beam_end)
//									break
								continue
							}
					if (g.notes[g.nhd].pit > pu)
						pu = g.notes[g.nhd].pit
					if (g.notes[0].pit < pd)
						pd = g.notes[0].pit
					if (g.beam_end)
						break
				}
				if (g.beam_end) {
					if ((pu + pd) / 2 < mid_p) {
						s.stem = 1
					} else if ((pu + pd) / 2 > mid_p) {
						s.stem = -1
					} else {
							if (g.notes[g.nhd].pit > pu)
								pu = g.notes[g.nhd].pit
							if (g.notes[0].pit < pd)
								pd = g.notes[0].pit
							if (g.beam_end)
								break
						}
						if (g.beam_end) {
							if (pu + pd < mid_p * 2) {
								s.stem = 1
							} else if (pu + pd > mid_p * 2) {
								s.stem = -1
							} else {
//--fixme: equal: check all notes of the beam
						if (cfmt.bstemdown)
							s.stem = -1
								if (cfmt.bstemdown)
									s.stem = -1
							}
						}
					}
				}
				if (!s.stem)
					s.stem = laststem
			} else {				// no beam
				n = (s.notes[s.nhd].pit + s.notes[0].pit) / 2
				if (n == mid_p) {
					n = 0
					for (m = 0; m <= s.nhd; m++)
						n += s.notes[m].pit;
					n /= (s.nhd + 1)
					for (m = 0; m <= s.nhd; m++) {
						if (s.notes[m].pit >= mid_p)
							break
					}
					n = m * 2 < s.nhd ? mid_p - 1 : mid_p + 1
				}
//				s.stem = n < mid_p ? 1 : -1
				if (n < mid_p)
					s.stem = 1
				else if (n > mid_p)
					s.stem = -1
				else if (cfmt.bstemdown)
					s.stem = -1
				else
︙
3575
3576
3577
3578
3579
3580
3581
3582

3583
3584
3585
3586
3587
3588
3589
3695
3696
3697
3698
3699
3700
3701

3702
3703
3704
3705
3706
3707
3708
3709







-
+







			if (s1.shiftunison && (s1.shiftunison & 2))
				return false
			head = s2.dur >= C.BLEN / 2 ? 2 : 1
		} else {
			return false
		}
	}
	if (head == 0)
	if (!head)
		head = s1.p_v.scale < s2.p_v.scale ? 2 : 1
	if (head == 1) {
		for (i2 = i21; i2 < i22; i2++) {
			s2.notes[i2].invis = true
			delete s2.notes[i2].acc
		}
		for (i2 = 0; i2 <= s2.nhd; i2++)
︙
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760




3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774



3775

3776
3777
3778
3779
3780
3781
3782
3871
3872
3873
3874
3875
3876
3877



3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894

3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906







-
-
-
+
+
+
+













-
+
+
+

+








	return right
}

/* -- shift the notes horizontally when voices overlap -- */
/* this routine is called only once per tune */
function set_overlap() {
	var	s, s1, s2, s3, i, i1, i2, m, sd, t, dp,
		d, d2, dr, dr2, dx,
		left1, right1, left2, right2, right3, pl, pr
    var	s, s1, s2, s3, i, i1, i2, m, sd, t, dp,
	d, d2, dr, dr2, dx,
	left1, right1, left2, right2, right3, pl, pr,
	sy = cur_sy

	// invert the voices
	function v_invert() {
		s1 = s2;
		s2 = s;
		d = d2;
		pl = left1;
		pr = right1;
		dr2 = dr
	}

	for (s = tsfirst; s; s = s.ts_next) {
		if (s.type != C.NOTE
		 || s.invis)
		 || s.invis) {
			if (s.type == C.STAVES)
				sy = s.sy
			continue
		}

		/* treat the stem on two staves with different directions */
		if (s.xstem
		 && s.ts_prev.stem < 0) {
			for (m = 0; m <= s.nhd; m++) {
				s.notes[m].shhd -= 7;		// stem_xoff
				s.notes[m].shac += 16
︙
3799
3800
3801
3802
3803
3804
3805
3806

3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817


















3818
3819
3820
3821
3822
3823
3824
3923
3924
3925
3926
3927
3928
3929

3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966







-
+











+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







				break
		}
		if (!s2)
			continue
		s1 = s

		/* set the dot vertical offset */
		if (cur_sy.voices[s1.v].range < cur_sy.voices[s2.v].range)
		if (sy.voices[s1.v].range < sy.voices[s2.v].range)
			s2.dot_low = true
		else
			s1.dot_low = true

		/* no shift if no overlap */
		if (s1.ymn > s2.ymx
		 || s1.ymx < s2.ymn)
			continue

		if (same_head(s1, s2))
			continue

		// special case when only a second and no dots
	    if (!s1.dots && !s2.dots)
		if ((s1.stem > 0 && s2.stem < 0
		  && s1.notes[0].pit == s2.notes[s2.nhd].pit + 1)
		 || (s1.stem < 0 && s2.stem > 0
		  && s1.notes[s1.nhd].pit + 1 == s2.notes[0].pit)) {
			if (s1.stem < 0) {
				s1 = s2;
				s2 = s
			}
			d = s1.notes[0].shhd + 7
			for (m = 0; m <= s2.nhd; m++)	// shift the lower note(s)
				s2.notes[m].shhd += d
			s2.xmx += d
			s1.xmx = s2.xmx		// align the dots
			continue
		}

		/* compute the minimum space for 's1 s2' and 's2 s1' */
		right1 = set_right(s1);
		left2 = set_left(s2);

		s3 = s1.ts_prev
		if (s3 && s3.time == s1.time
︙
3869
3870
3871
3872
3873
3874
3875
3876


3877
3878
3879
3880



3881

3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
4011
4012
4013
4014
4015
4016
4017

4018
4019
4020
4021
4022

4023
4024
4025
4026
4027
4028
4029
4030
4031
4032




4033
4034
4035
4036
4037
4038
4039
4040
4041

4042
4043
4044
4045



4046
4047

4048
4049
4050
4051
4052
4053
4054







-
+
+



-
+
+
+

+





-
-
-
-









-




-
-
-


-







		t = 0;
		i1 = s1.nhd;
		i2 = s2.nhd
		while (1) {
			dp = s1.notes[i1].pit - s2.notes[i2].pit
			switch (dp) {
			case 0:
				if (s1.notes[i1].acc != s2.notes[i2].acc) {
				if (s1.notes[i1].acc != s2.notes[i2].acc
				 && !s1.notes[i1].acc && !s2.notes[i2].acc) {
					t = -1
					break
				}
				if (s2.notes[i2].acc)
				if (s2.notes[i2].acc) {
					if (!s1.notes[i1].acc)
						s1.notes[i1].acc = s2.notes[i2].acc
					s2.notes[i2].acc = 0
				}
				if (s1.dots && s2.dots
				 && (s1.notes[i1].pit & 1))
					t = 1
				break
			case -1:
//fixme:dots++
//				if (s1.dots && s2.dots)
//					t = 1
//++--
				if (s1.dots && s2.dots) {
					if (s1.notes[i1].pit & 1) {
						s1.dot_low = false;
						s2.dot_low = false
					} else {
						s1.dot_low = true;
						s2.dot_low = true
					}
				}
//fixme:dots--
				break
			case -2:
				if (s1.dots && s2.dots
				 && !(s1.notes[i1].pit & 1)) {
//fixme:dots++
//					t = 1
//++--
					s1.dot_low = false;
					s2.dot_low = false
//fixme:dots--
					break
				}
				break
			}
			if (t < 0)
				break
			if (dp >= 0) {
︙
3930
3931
3932
3933
3934
3935
3936
3937


3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
4067
4068
4069
4070
4071
4072
4073

4074
4075
4076
4077
4078
4079

4080
4081
4082
4083
4084
4085
4086







-
+
+




-







		}

		sd = 0;
		if (s1.dots) {
			if (s2.dots) {
				if (!t)			/* if no dot clash */
					sd = 1		/* align the dots */
//fixme:dots
			} else {
				v_invert()		// shift the first voice
			}
		} else if (s2.dots) {
			if (d2 + dr < d + dr2)
				sd = 1		/* align the dots */
//fixme:dots
		}
		pl = left2;
		pr = right2
		if (!s3 && d2 + dr < d + dr2)
			v_invert()
		d += 3
		if (d < 0)
︙
4029
4030
4031
4032
4033
4034
4035
4036

4037
4038
4039
4040
4041
4042
4043
4166
4167
4168
4169
4170
4171
4172

4173
4174
4175
4176
4177
4178
4179
4180







-
+







			s1.xmx = s2.xmx		// align the dots
	}
}

/* -- set the stem height -- */
/* this routine is called only once per tune */
// (possible hook)
function set_stems() {
Abc.prototype.set_stems = function() {
	var s, s2, g, slen, scale,ymn, ymx, nflags, ymin, ymax, res

	for (s = tsfirst; s; s = s.ts_next) {
		if (s.type != C.NOTE) {
			if (s.type != C.GRACE)
				continue
			ymin = ymax = s.mid
︙
4142
4143
4144
4145
4146
4147
4148
4149

4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170

4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181


4182
4183
4184
4185
4186
4187
4188
4279
4280
4281
4282
4283
4284
4285

4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306

4307
4308
4309
4310
4311
4312
4313
4314
4315
4316


4317
4318
4319
4320
4321
4322
4323
4324
4325







-
+




















-
+









-
-
+
+







			  || !s.beam_st
			  || !s.beam_end)) {
				slen -= 2
				if (s.notes[s.nhd].pit > 28)
					slen -= 2
			}
			s.y = ymn
			if (s.notes[0].ti1)
			if (s.notes[0].tie)
				ymn -= 3;
			s.ymn = ymn - 4;
			s.ys = ymx + slen
			if (s.ys < s.mid)
				s.ys = s.mid;
			s.ymx = (s.ys + 2.5) | 0
		} else {			/* stem down */
			if (s.notes[0].pit < 18
			 && (nflags <= 0
			  || !s.beam_st || !s.beam_end)) {
				slen -= 2
				if (s.notes[0].pit < 16)
					slen -= 2
			}
			s.ys = ymn - slen
			if (s.ys > s.mid)
				s.ys = s.mid;
			s.ymn = (s.ys - 2.5) | 0;
			s.y = ymx
/*fixme:the tie may be lower*/
			if (s.notes[s.nhd].ti1)
			if (s.notes[s.nhd].tie)
				ymx += 3;
			s.ymx = ymx + 4
		}
	}
}

/* -- split up unsuitable bars at end of staff -- */
// return true if the bar type has changed
function check_bar(s) {
	var	bar_type, i, b1, b2,
		p_voice = s.p_v
    var	bar_type, i, b1, b2, s_bs, s2,
	p_voice = s.p_v

	/* search the last bar */
	while (s.type == C.CLEF || s.type == C.KEY || s.type == C.METER) {
		if (s.type == C.METER
		 && s.time > p_voice.sym.time)	/* if not empty voice */
			insert_meter |= 1;	/* meter in the next line */
		s = s.prev
︙
4201
4202
4203
4204
4205
4206
4207

4208
4209
















































4210
4211
4212

4213
4214
4215
4216

4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
4229
4230
4231
4232
4233

4234
4235
4236
4237

4238
4239
4240
4241
4242
4243
4244
4338
4339
4340
4341
4342
4343
4344
4345


4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363
4364
4365
4366
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395

4396
4397
4398
4399

4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416

4417
4418
4419
4420

4421
4422
4423
4424
4425
4426
4427
4428







+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
+



-
+
















-
+



-
+







	}
	bar_type = s.bar_type
	if (bar_type == ":")
		return
	if (bar_type.slice(-1) != ':')		// if not left repeat bar
		return

	// add a left repeat bar in the next music line
	if (!p_voice.bar_start)
		p_voice.bar_start = clone(s)
	s2 = p_voice.s_next
	while (1) {
		switch (s2.type) {
		case C.BAR:
		case C.GRACE:
		case C.NOTE:
		case C.REST:
			break
		default:
			s2 = s2.next
			if (!s2)
				return
		}
		break
	}
	if (s2.type == C.BAR) {
		s_bs = s2		// bar start
	} else {
		s_bs = clone(s)
		s_bs.next = s2
		s_bs.prev = s2.prev
		if (s2 == p_voice.s_next)
			p_voice.s_next = s_bs
		else
			s2.prev.next = s_bs
		s2.prev = s_bs

		while (!s2.seqst)
			s2 = s2.ts_prev
		s_bs.ts_next = s2
		s_bs.ts_prev = s2.ts_prev
		if (s2 == tsnext)
			tsnext = s_bs
		else
			s2.ts_prev.ts_next = s_bs
		s2.ts_prev = s_bs
		delete s_bs.seqst
		if (s_bs == tsnext || s2.ts_prev.type != C.BAR) {
			if (s2.seqst) {
				s_bs.seqst = true
			} else {
				s2.seqst = true
				s2.shrink = s_bs.wr + s2.wl
				s2.space = 0
			}
		}
	}

	if (bar_type[0] != ':') {		// 'xx:' (not ':xx:')
		if (bar_type == "||:") {
			p_voice.bar_start.bar_type = "[|:";
			s_bs.bar_type = "[|:"
			s.bar_type = "||"
			return true
		}
		p_voice.bar_start.bar_type = bar_type
		s_bs.bar_type = bar_type
		if (s.prev && s.prev.type == C.BAR)
			unlksym(s)
		else
			s.bar_type = "|"
		return
	}

	// ':xx:' -> ':x|]' and '[|x:'
	i = 0
	while (bar_type[i] == ':')
		i++
	if (i < bar_type.length) {
		s.bar_type = bar_type.slice(0, i) + '|]';
		i = bar_type.length - 1
		while (bar_type[i] == ':')
			i--;
		p_voice.bar_start.bar_type = '[|' + bar_type.slice(i + 1)
		s_bs.bar_type = '[|' + bar_type.slice(i + 1)
	} else {
		i = (bar_type.length / 2) |0;			// '::::' !
		s.bar_type = bar_type.slice(0, i) + '|]';
		p_voice.bar_start.bar_type = '[|' + bar_type.slice(i)
		s_bs.bar_type = '[|' + bar_type.slice(i)
	}
	return true
}

/* -- move the symbols of an empty staff to the next one -- */
function sym_staff_move(st) {
	for (var s = tsfirst; s; s = s.ts_next) {
︙
4275
4276
4277
4278
4279
4280
4281
4282

4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4459
4460
4461
4462
4463
4464
4465

4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479



4480
4481
4482
4483
4484
4485
4486







-
+













-
-
-







		block.newpage = true;
		blk_out()
		break
	case "sep":
		set_page();
		vskip(s.sk1);
		output += '<path class="stroke"\n\td="M';
		out_sxsy(s.x, ' ', 0);
		out_sxsy((img.width -s.l) / 2 - img.lm, ' ', 0)
		output += 'h' + s.l.toFixed(1) + '"/>\n';
		vskip(s.sk2);
		break
	case "text":
		write_text(s.text, s.opt)
		break
	case "title":
		write_title(s.text, true)
		break
	case "vskip":
		vskip(s.sk);
//		blk_out()
		break
	default:
		error(2, s, 'Block $1 not treated', s.subtype)
		break
	}
}

/* -- define the start and end of a piece of tune -- */
/* tsnext becomes the beginning of the next line */
function set_piece() {
	var	s, last, p_voice, st, v, nst, nv, tmp,
︙
4343
4344
4345
4346
4347
4348
4349
4350

4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
4362
4363








4364
4365
4366
4367
4368
4369
4370
4371

















4372
4373
4374
4375
4376
4377
4378
4524
4525
4526
4527
4528
4529
4530

4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541



4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581







-
+










-
-
-
+
+
+
+
+
+
+
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







				}
			}
		}
	} // set_brace()

	// set the top and bottom of the staves
	function set_top_bot() {
		var st, p_staff, i, l, hole
	    var	st, p_staff, i, j, l

		for (st = 0; st <= nstaff; st++) {
			p_staff = staff_tb[st]
			if (!non_empty_gl[st]) {
				p_staff.botbar = p_staff.topbar = 0
				continue
			}
			l = p_staff.stafflines.length;
			p_staff.topbar = 6 * (l - 1)

			for (i = 0; i < l - 1; i++)
				if (p_staff.stafflines[i] != '.')
					break
			for (i = 0; i < l - 1; i++) {
				switch (p_staff.stafflines[i]) {
				case '.':
				case '-':
					continue
				}
				break
			}
			p_staff.botline = p_staff.botbar = i * 6
			if (i >= l - 2) {		// 0, 1 or 2 lines
				if (p_staff.stafflines[i] != '.') {
					p_staff.botbar -= 6;
					p_staff.topbar += 6
				} else {		// no line: big bar
					p_staff.botbar -= 12;
					p_staff.topbar += 12
					continue	// no helper line
				}
			}

			// define the helper lines
			p_staff.hll = 17 + i;		// pitch of lowest note
							// without helper line
							// ('D' when standard staff)
			p_staff.hlmap = new Int8Array(new Array((l - i + 1) * 2))
			for (j = 1; i < l; i++, j += 2) {
				switch (p_staff.stafflines[i]) {
				case '|':
				case '[':
					p_staff.hlmap[j - 1] = 1; // no helper line
					p_staff.hlmap[j] = 1;
					p_staff.hlmap[j + 1] = 1
					break
				}
			}
		}
	} // set_top_bot()

	/* reset the staves */
	nstaff = nst = sy.nstaff
︙
4391
4392
4393
4394
4395
4396
4397
4398

4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414

4415

4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4594
4595
4596
4597
4598
4599
4600

4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618

4619



4620
4621
4622
4623
4624
4625
4626







-
+
















+
-
+
-
-
-







//				blocks.push(s);
//				unlksym(s);
//				s = s.ts_next
//			}
			break
		}
		if (!s.ts_next)
			last = s		// keep the last symbol
			last = s	// keep the last symbol of the tune
		switch (s.type) {
		case C.STAVES:
			set_brace();
			sy.st_print = new Uint8Array(non_empty);
			sy = s.sy;
			nst = sy.nstaff
			if (nstaff < nst) {
				for (st = nstaff + 1; st <= nst; st++)
					reset_staff(st);
				nstaff = nst
			}
			non_empty = []
			continue

		// the block symbols will be treated after music line generation
		case C.BLOCK:
			if (!s.play)
			blocks.push(s);
				blocks.push(s)
			unlksym(s)
			if (last)
				last = s.ts_prev
			continue
		}
		st = s.st
		if (non_empty[st])
			continue
		switch (s.type) {
		default:
︙
4521
4522
4523
4524
4525
4526
4527
4528

4529
4530
4531
4532
4533
4534
4535
4722
4723
4724
4725
4726
4727
4728

4729
4730
4731
4732
4733
4734
4735
4736







-
+







		 && last.type != C.KEY)
			s.space = s.shrink
	}
}

/* -- position the symbols along the staff -- */
// (possible hook)
function set_sym_glue(width) {
Abc.prototype.set_sym_glue = function(width) {
    var	s, g, ll,
	some_grace,
	spf,			// spacing factor
	xmin = 0,		// sigma shrink = minimum spacing
	xx = 0,			// sigma natural spacing
	x = 0,			// sigma expandable elements
	xs = 0,			// sigma unexpandable elements with no space
︙
4551
4552
4553
4554
4555
4556
4557
4558

4559
4560
4561
4562
4563
4564
4565


4566
4567
4568
4569
4570
4571
4572
4752
4753
4754
4755
4756
4757
4758

4759
4760
4761
4762
4763
4764
4765

4766
4767
4768
4769
4770
4771
4772
4773
4774







-
+






-
+
+







			} else {
				xs += s.shrink
			}
		}
	}

	// can occur when bar alone in a staff system
	if (xx == 0) {
	if (!xx) {
		realwidth = 0
		return
	}

	// last line?
	ll = !tsnext ||			// yes
		tsnext.type == C.BLOCK	// no, but followed by %%command
		(tsnext.type == C.BLOCK	// no, but followed by %%command
		 && !tsnext.play)
		|| blocks.length	//	(abcm2ps compatibility)

	// strong shrink
	if (xmin >= width) {
		if (xmin > width)
			error(1, s, "Line too much shrunk $1 $2 $3",
				xmin.toFixed(1),
︙
4634
4635
4636
4637
4638
4639
4640
4641
4642


4643
4644
4645
4646
4647
4648
4649










4650



4651
4652
4653
4654
4655
4656
4657
4836
4837
4838
4839
4840
4841
4842


4843
4844
4845
4846





4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867







-
-
+
+


-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

+
+
+







		for (g = s.extra; g; g = g.next)
			g.x += x
	}
}

// set the starting symbols of the voices for the new music line
function set_sym_line() {
	var	p_voice, s, v,
		nv = voice_tb.length
    var	p_v, s,
	v = voice_tb.length

	// set the first symbol of each voice
	for (v = 0; v < nv; v++) {
		p_voice = voice_tb[v];
		s = p_voice.s_next;		// (set in set_piece)
		p_voice.sym = s
		if (s)
	while (--v >= 0) {
		p_v = voice_tb[v]
		if (p_v.sym && p_v.s_prev)
			p_v.sym.prev = p_v.s_prev
		s = p_v.s_next			// (set in set_piece)
		p_v.sym = s
		if (s) {
			if (s.prev)
				s.prev.next = s
			p_v.s_prev = s.prev	// (save for play)
			s.prev = null
		} else {
			p_v.s_prev = null
		}
	}
}

// set the left offset the images
function set_posx() {
	posx = img.lm / cfmt.scale
}
︙
4675
4676
4677
4678
4679
4680
4681


4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695


4696
4697
4698
4699

4700
4701
4702
4703
4704
4705
4706
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905


4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919







+
+












-
-
+
+




+







			return
		default:
			continue
		case C.STAVES:
			cur_sy = s.sy
			break
		case C.BLOCK:
			if (s.play)
				continue	// keep for play
			block_gen(s)
			break
		}
		unlksym(s)
		if (s.p_v.s_next == s)
			s.p_v.s_next = s.next
	}
	tsfirst = null			/* no more notes */
}

/* -- generate the music -- */
// (possible hook)
function output_music() {
	var v, lwidth, indent, line_height
Abc.prototype.output_music = function() {
    var v, lwidth, indent, line_height, ts1st, tslast, p_v

	gen_init()
	if (!tsfirst)
		return
	tunes.push([tsfirst, voice_tb])	// keep tune data for playing
	set_global()
	if (voice_tb.length > 1)	/* if many voices */
		self.set_stem_dir()	// set the stems direction in 'multi'

	for (v = 0; v < voice_tb.length; v++)
		set_beams(voice_tb[v].sym);	/* decide on beams */

︙
4715
4716
4717
4718
4719
4720
4721
4722

4723
4724
4725
4726
4727
4728
4729
4730






4731
4732
4733
4734
4735
4736


4737
4738
4739
4740
4741
4742
4743
4744

4745
4746
4747
4748

4749
4750
4751
4752
4753
4754
4755
4756


4757
4758


4759
4760
4761
4762
4763

4764
4765
4766
4767
4768










4769
4770
4771
4772
4773
4774
4775
4776
4928
4929
4930
4931
4932
4933
4934

4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953


4954
4955
4956
4957
4958
4959
4960
4961
4962

4963
4964
4965
4966

4967
4968
4969
4970

4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
5005
5006
5007
5008
5009







-
+








+
+
+
+
+
+




-
-
+
+







-
+



-
+



-




+
+


+
+





+





+
+
+
+
+
+
+
+
+
+








	set_allsymwidth();		/* set the width of all symbols */

	indent = set_indent(true)

	/* if single line, adjust the page width */
	if (cfmt.singleline) {
		v = get_ck_width();
		lwidth = indent + v[0] + v[1] + get_width(tsfirst, null);
		lwidth = indent + v[0] + v[1] + get_width(tsfirst, null)[0];
		img.width = lwidth * cfmt.scale + img.lm + img.rm + 2
	} else {

	/* else, split the tune into music lines */
		lwidth = get_lwidth();
		cut_tune(lwidth, indent)
	}

	// save symbol pointers for play
	ts1st = tsfirst
	v = voice_tb.length
	while (--v >= 0)
		voice_tb[v].osym = voice_tb[v].sym

	spf_last = 1.2				// last spacing factor
	while (1) {				/* loop per music line */
		set_piece();
		self.set_sym_glue(lwidth - indent)
		if (realwidth != 0) {
			if (indent != 0)
		if (realwidth) {
			if (indent)
				posx += indent;
			draw_sym_near();		// delayed output
			line_height = set_staff();
			draw_systems(indent);
			draw_all_sym();
			delayed_update();
			vskip(line_height)
			if (indent != 0) {
			if (indent) {
				posx -= indent;
				insert_meter &= ~2	// no more indentation
			}
			while (blocks.length != 0)
			while (blocks.length)
				block_gen(blocks.shift())
		}

		tsfirst = tsnext
		if (cfmt.splittune)
			blk_flush()
		else
			svg_flush()
		if (tslast)
			tslast.ts_next.ts_prev = tslast
		if (!tsnext)
			break
		tsnext.ts_prev.ts_next =		// (restore for play)
			tsfirst = tsnext

		// next line
		gen_init()
		if (!tsfirst)
			break
		tslast = tsfirst.ts_prev
		tsfirst.ts_prev = null;
		set_sym_line();
		lwidth = get_lwidth()	// the image size may have changed
		indent = set_indent()
	}

	// restore for play
	tsfirst = ts1st
	v = voice_tb.length
	while (--v >= 0) {
		p_v = voice_tb[v]
		if (p_v.sym && p_v.s_prev)
			p_v.sym.prev = p_v.s_prev
		p_v.sym = p_v.osym
	}
}

/* -- reset the generator -- */
function reset_gen() {
	insert_meter = cfmt.writefields.indexOf('M') >= 0 ?
				3 :	/* insert meter and indent */
				2	/* indent only */
}
Changes to core/parse.js.
︙
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33
34
35
36
37
16
17
18
19
20
21
22

23




24
25

26
27
28
29
30
31
32







-
+
-
-
-
-


-







//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

var	a_gch,		// array of parsed guitar chords
	a_dcn,		// array of parsed decoration names
	multicol,	// multi column object
	maps = {}	// maps object - hashcode = map name
	maps = {}	// maps object - see set_map()
			//	-> object - hashcode = note
			//	[0] array of heads
			//	[1] print
			//	[2] color
var	qplet_tb = new Int8Array([ 0, 1, 3, 2, 3, 0, 2, 0, 3, 0 ]),
	ntb = "CDEFGABcdefgab"


// set the source references of a symbol
function set_ref(s) {
	s.fname = parse.fname;
	s.istart = parse.istart;
	s.iend = parse.iend
}
︙
93
94
95
96
97
98
99



100
101
102
103
104
105
106
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104







+
+
+







		break
	case 'p':
		i = 4
	case 'P':				// perc
		s.clef_type = "p";
		s.clef_line = 3;
		curvoice.key.k_sf = 0;		// no accidental
		curvoice.ckey.k_sf = 0
		curvoice.ckey.k_map = abc2svg.keys[7]
		curvoice.ckey.k_b40 = 2
		curvoice.ckey.k_drum = true	// no transpose
		break
	default:
		syntax(1, "Unknown clef '$1'", clef_def)
		return //undefined
	}
	if (clef_def[i] >= '1' && clef_def[i] <= '9') {
︙
120
121
122
123
124
125
126
127
128

129
130

131
132

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174

175

176
177
178

179
180
181



182
183
184
185
186
187
188



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205

206
207
208
209
210
211
212
213
118
119
120
121
122
123
124


125


126


127

128








































129
130
131
132
133
134
135



136
137
138







139
140
141













142
143


144

145
146
147
148
149
150
151







-
-
+
-
-
+
-
-
+
-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+

+



+
-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-


-
-
+
-







	case '-':
		s.clef_octave = -7
		break
	}
	return s
}

var note_pit = new Int8Array([0, 2, 4, 5, 7, 9, 11])

// convert an interval to a base-40 interval
// get a transposition value
function get_transp(param,
function get_interval(param, score) {
			type) {		// undefined or "instr"
	var	i, val, tmp, note,
    var	i, val, tmp, note, pit
		pit = []

	if (param[0] == '0')
		return 0
	if ("123456789-+".indexOf(param[0]) >= 0) {	// by semi-tone
		val = parseInt(param) * 3
		if (isNaN(val) || val < -108 || val > 108) {
//fixme: no source reference...
			syntax(1, "Bad transpose value")
			return
		}
		switch (param.slice(-1)) {
		default:
			return val
		case '#':
			val++
			break
		case 'b':
			val += 2
			break
		}
		if (val > 0)
			return val
		return val - 3
	}

	// by music interval
	if (type == "instr") {	// convert instrument= into score= or sound=
		tmp = param.indexOf('/')
		if (!cfmt.sound) {
			if (tmp < 0)
				return 0	// written pitch
			param = param.replace('/', '')
		} else {
			if (tmp < 0)
				param = 'c' + param
			else
				param = param.replace(/.*\//, 'c')
		}
	}

	tmp = new scanBuf();
	tmp = new scanBuf;
	tmp.buffer = param
	pit = []
	for (i = 0; i < 2; i++) {
		note = tmp.buffer[tmp.index] ? parse_acc_pit(tmp) : null
		if (!note) {
			if (i != 1 || !score) {
			syntax(1, "Bad transpose value")
			return
		}
				syntax(1, "Bad transpose value")
				return
			}
		note.pit += 124;	// 126 - 2 for value > 0 and 'C' % 7 == 0
		val = ((note.pit / 7) | 0) * 12 + note_pit[note.pit % 7]
		if (note.acc && note.acc != 3)		// if not natural
			val += note.acc;
		pit[i] = val
	}

			pit[i] = 23			// 'c' (C5)
		} else {
			pit[i] = abc2svg.pab40(note.pit, note.acc)
	val = (pit[1] - pit[0]) * 3
	if (note) {
		switch (note.acc) {
		default:
			return val
		case 2:
		case 1:
			val++
			break
		case -1:
		case -2:
			val += 2
			break
		}
	}
	if (val > 0)
		return val
	return pit[1] - pit[0]
	return val - 3
}

// set the linebreak character
function set_linebreak(param) {
	var i, item

	for (i = 0; i < 128; i++) {
︙
238
239
240
241
242
243
244
245

246
247
248

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276

277
278
279
280
281
282
283
176
177
178
179
180
181
182

183
184
185

186
187
188
189
190




191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218







-
+


-
+




-
-
-
-




















+







		char_tb[item.charCodeAt(0)] = '\n'
	}
}

// set a new user character (U: or %%user)
function set_user(parm) {
    var	k, c, v,
	a = parm.match(/(.*?)[= ]*([!"].*[!"])/)
	a = parm.match(/(.)[= ]*(\[I:.+\]|".+"|!.+!)$/)

	if (!a) {
		syntax(1, 'Lack of starting ! or " in U: / %%user')
		syntax(1, 'Lack of starting [, ! or " in U: / %%user')
		return
	}
	c = a[1];
	v = a[2]
	if (v.slice(-1) != v[0]) {
		syntax(1, "Lack of ending $1 in U:/%%user", v[0])
		return
	}
	if (c[0] == '\\') {
		if (c[1] == 't')
			c = '\t'
		else if (!c[1])
			c = ' '
	}

	k = c.charCodeAt(0)
	if (k >= 128) {
		syntax(1, errs.not_ascii)
		return
	}
	switch (char_tb[k][0]) {
	case '0':			// nil
	case 'd':
	case 'i':
	case ' ':
		break
	case '"':
	case '!':
	case '[':
		if (char_tb[k].length > 1)
			break
		// fall thru
	default:
		syntax(1, "Bad user character '$1'", c)
		return
	}
︙
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308

309
310
311
312
313
314
315
316
317
318
319
320

321
322
323
324
325
326
327
328
329
330
331
332
333
334
335

336
337
338
339
340
341
342

343
344
345
346
347
348
349
350


351
352
353
354
355
356
357
229
230
231
232
233
234
235


236
237
238
239
240

241
242
243
244
245
246
247
248
249




250
251
252
253
254
255
256
257
258
259
260
261
262



263

264
265
266
267
268

269
270
271
272
273
274
275


276
277
278
279
280
281
282
283
284







-
-





-
+








-
-
-
-
+












-
-
-
+
-





-
+






-
-
+
+







		break
	}
	char_tb[k] = v
}

// get a stafflines value
function get_st_lines(param) {
	var n, val

	if (!param)
		return
	if (/^[\]\[|.-]+$/.test(param))
		return param.replace(/\]/g, '[')

	n = parseInt(param)
    var	n = parseInt(param)
	switch (n) {
	case 0: return "..."
	case 1: return "..|"
	case 2: return ".||"
	case 3: return ".|||"
	}
	if (isNaN(n) || n < 0 || n > 16)
		return //undefined
	val = '|'
	while (--n > 0)
		val += '|'
	return val
	return "||||||||||||||||".slice(0, n)
}

// create a block symbol in the tune body
function new_block(subtype) {
	var	s = {
			type: C.BLOCK,
			subtype: subtype,
			dur: 0
		}

	if (parse.state == 2)
		goto_tune()
	var voice_s = curvoice;
	curvoice = voice_tb[par_sy.top_voice]
	sym_link(s);
	sym_link(s)
	curvoice = voice_s
	return s
}

// set the voice parameters
// (possible hook)
function set_vp(a) {
Abc.prototype.set_vp = function(a) {
    var	s, item, pos, val, clefpit

	while (1) {
		item = a.shift()
		if (!item)
			break
		if (item[item.length - 1] == '='
		 && a.length == 0) {
		if (item.slice(-1) == '='
		 && !a.length) {
			syntax(1, errs.bad_val, item)
			break
		}
		switch (item) {
		case "clef=":
			s = a.shift()		// keep last clef
			break
︙
384
385
386
387
388
389
390













391

392
393
394
395
396
397
398
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330

331
332
333
334
335
336
337
338







+
+
+
+
+
+
+
+
+
+
+
+
+
-
+







			else
				curvoice[item.slice(0, -1)] = val
			break
		case "cue=":
			curvoice.scale = a.shift() == 'on' ? .7 : 1
			break
		case "instrument=":

			// instrument=M/N => score=MN and sound=cN
			// (instrument=M == instrument=M/M)
			item = a.shift()
			val = item.indexOf('/')
			if (val < 0) {
				val = 0
				curvoice.sndtran = get_interval('c' + item)
			} else {
				curvoice.sndtran = get_interval('c' +
							item.slice(val + 1))
				val = get_interval(item.replace('/', ''))
			}
			curvoice.transp = get_transp(a.shift(), 'instr')
			curvoice.transp = cfmt.sound ? curvoice.sndtran : val
			break
		case "map=":			// %%voicemap
			curvoice.map = a.shift()
			break
		case "name=":
		case "nm=":
			curvoice.nm = a.shift()
︙
419
420
421
422
423
424
425



426
427
428
429
430
431
432
433
434

435
436
437




438

439
440

441
442
443
444
445
446
447
359
360
361
362
363
364
365
366
367
368
369
370



371
372
373

374
375
376
377
378
379
380
381

382


383
384
385
386
387
388
389
390







+
+
+


-
-
-



-
+



+
+
+
+
-
+
-
-
+







			val = parseFloat(a.shift())
			if (isNaN(val) || val < .6 || val > 1.5)
				syntax(1, errs.bad_val, "%%voicescale")
			else
				curvoice.scale = val
			break
		case "score=":
			// score=MN
			// (score=M == score=Mc)
			item = a.shift()
			if (cfmt.sound)
				break
			item = a.shift()
			if (item.indexOf('/') < 0)
				item += '/c';
			curvoice.transp = get_transp(item)
			break
		case "shift=":
			curvoice.shift = get_transp(a.shift())
			curvoice.shift = curvoice.sndsh = get_interval(a.shift())
			break
		case "sound=":
		case "transpose=":		// (abcMIDI compatibility)
// concert-score display: apply sound=
// sounding-score display: apply sound= only if M != c/C
// sound: apply sound=
			curvoice.sndtran = get_interval(a.shift())
			if (!cfmt.sound)
			if (cfmt.sound)
				break
			curvoice.transp = get_transp(a.shift())
				curvoice.transp = curvoice.sndtran
			break
		case "subname=":
		case "sname=":
		case "snm=":
			curvoice.snm = a.shift()
			if (curvoice.snm[0] == '"')
				curvoice.snm = curvoice.snm.slice(1, -1);
︙
512
513
514
515
516
517
518
519

520
521
522
523
524
525
526

527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542





543
544
545


546



























































547
548
549
550

551
552
553
554
555
556
557
558
559
560
561
562
563
564

565
566
567
568

569
570
571
572
573

574
575
576
577
578
579

580
581

582
583
584
585
586

587

588
589
590
591
592


593
594
595
596
597
598
599
600
601
602
603
604
605
606

607
608
609

610
611
612
613
614

615
616
617

618
619
620

621
622
623

624
625
626
627
628
629
630
631
632
633
634
635

636
637
638
639
640

641
642
643
644
645

646
647
648
649
650


651
652
653


654
655
656
657

658












659
660



661
662
663
664
665
666
667
455
456
457
458
459
460
461

462
463
464
465
466
467
468

469
470
471
472
473
474
475
476
477
478
479
480
481




482
483
484
485
486



487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554







555
556
557
558
559

560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576

577
578
579
580
581
582
583


584
585
586
587
588
589


590
591
592
593
594
595
596
597
598
599
600
601
602
603
604

605
606
607

608
609
610
611
612

613
614
615

616
617
618

619
620
621

622
623
624
625
626
627
628
629
630
631
632
633

634
635
636
637
638

639
640
641
642
643

644
645
646
647
648
649
650
651
652


653
654
655
656
657
658
659

660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683







-
+






-
+












-
-
-
-
+
+
+
+
+
-
-
-
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+

-
-
-
-
-
-
-





-
+




+





+





-
+


+



-
-
+

+



-
-
+
+













-
+


-
+




-
+


-
+


-
+


-
+











-
+




-
+




-
+





+
+

-
-
+
+




+
-
+
+
+
+
+
+
+
+
+
+
+
+


+
+
+







		if (info.V) {
			if (info.V['*'])
				a = info.V['*'].concat(a)
			if (info.V[curvoice.id])
				a = info.V[curvoice.id].concat(a)
		}
	}
	if (a.length != 0)
	if (a.length)
		self.set_vp(a)
} // set_kv_parm()

// memorize the K:/V: parameters
function memo_kv_parm(vid,	// voice ID (V:) / '*' (K:/V:*)
			a) {	// array of items
	if (a.length == 0)
	if (!a.length)
		return
	if (!info.V)
		info.V = {}
	if (info.V[vid])
		Array.prototype.push.apply(info.V[vid], a)
	else
		info.V[vid] = a
}

// K: key signature
// return the key and the voice/clef parameters
function new_key(param) {
	var	i, clef, key_end, c, tmp,
		mode = 0,
		s = {
			type: C.KEY,
    var	i, clef, key_end, c, tmp, exp,
	sf = "FCGDAEB".indexOf(param[0]) - 1,
	mode = 0,
	s = {
		type: C.KEY,
			k_delta: 0,
			dur:0
		}
		dur:0
	}

	// set the accidentals when K: with modified accidentals
	function set_k_acc(s, sf) {
	    var i, j, n, nacc, p_acc,
		accs = [],
		pits = [],
		m_n = [],
		m_d = []

		if (sf > 0) {
			for (nacc = 0; nacc < sf; nacc++) {
				accs[nacc] = 1;			// sharp
				pits[nacc] = [26, 23, 27, 24, 21, 25, 22][nacc]
			}
		} else {
			for (nacc = 0; nacc < -sf; nacc++) {
				accs[nacc] = -1;		// flat
				pits[nacc] = [22, 25, 21, 24, 20, 23, 26][nacc]
			}
		}
		n = s.k_a_acc.length
		for (i = 0; i < n; i++) {
			p_acc = s.k_a_acc[i]
			for (j = 0; j < nacc; j++) {
				if (pits[j] == p_acc.pit) {
					accs[j] = p_acc.acc
					if (p_acc.micro_n) {
						m_n[j] = p_acc.micro_n;
						m_d[j] = p_acc.micro_d
					}
					break
				}
			}
			if (j == nacc) {
				accs[j] = p_acc.acc;
				pits[j] = p_acc.pit
				if (p_acc.micro_n) {
					m_n[j] = p_acc.micro_n;
					m_d[j] = p_acc.micro_d
				}
				nacc++
			}
		}
		for (i = 0; i < nacc; i++) {
			p_acc = s.k_a_acc[i]
			if (!p_acc)
				p_acc = s.k_a_acc[i] = {}
			p_acc.acc = accs[i];
			p_acc.pit = pits[i]
			if (m_n[i]) {
				p_acc.micro_n = m_n[i];
				p_acc.micro_d = m_d[i]
			} else {
				delete p_acc.micro_n
				delete p_acc.micro_d
				}
		}
	} // set_k_acc()

	// code of new_key()
	set_ref(s);

	// tonic
	i = 1
    if (sf < -1) {
	switch (param[0]) {
	case 'A': s.k_sf = 3; break
	case 'B': s.k_sf = 5; break
	case 'C': s.k_sf = 0; break
	case 'D': s.k_sf = 2; break
	case 'E': s.k_sf = 4; break
	case 'F': s.k_sf = -1; break
	case 'G': s.k_sf = 1; break
	case 'H':				// bagpipe
		switch (param[1]) {
		case 'P':
		case 'p':
			s.k_bagpipe = param[1];
			s.k_sf = param[1] == 'P' ? 0 : 2;
			sf = param[1] == 'P' ? 0 : 2;
			i++
			break
		default:
			syntax(1, "Unknown bagpipe-like key")
			key_end = true
			break
		}
		break
	case 'P':
		syntax(1, "K:P is deprecated");
		sf = 0;
		s.k_drum = true;
		key_end = true
		break
	case 'n':				// none
		if (param.indexOf("none") == 0) {
			s.k_sf = 0;
			sf = 0;
			s.k_none = true;
			i = 4
			break
		}
		// fall thru
	default:
		key_end = true
		break
		return [s, info_split(param)]
	}
    }

	if (!key_end) {
		switch (param[i]) {
		case '#': s.k_sf += 7; i++; break
		case 'b': s.k_sf -= 7; i++; break
		case '#': sf += 7; i++; break
		case 'b': sf -= 7; i++; break
		}
		param = param.slice(i).trim()
		switch (param.slice(0, 3).toLowerCase()) {
		default:
			if (param[0] != 'm'
			 || (param[1] != ' ' && param[1] != '\t'
			  && param[1] != '\n')) {
				key_end = true
				break
			}
			// fall thru ('m')
		case "aeo":
		case "m":
		case "min": s.k_sf -= 3;
		case "min": sf -= 3;
			mode = 5
			break
		case "dor": s.k_sf -= 2;
		case "dor": sf -= 2;
			mode = 1
			break
		case "ion":
		case "maj": break
		case "loc": s.k_sf -= 5;
		case "loc": sf -= 5;
			mode = 6
			break
		case "lyd": s.k_sf += 1;
		case "lyd": sf += 1;
			mode = 3
			break
		case "mix": s.k_sf -= 1;
		case "mix": sf -= 1;
			mode = 4
			break
		case "phr": s.k_sf -= 4;
		case "phr": sf -= 4;
			mode = 2
			break
		}
		if (!key_end)
			param = param.replace(/\w+\s*/, '')

		// [exp] accidentals
		if (param.indexOf("exp ") == 0) {
			param = param.replace(/\w+\s*/, '')
			if (!param)
				syntax(1, "No accidental after 'exp'");
			s.k_exp = true
			exp = true
		}
		c = param[0]
		if (c == '^' || c == '_' || c == '=') {
			s.k_a_acc = [];
			tmp = new scanBuf();
			tmp = new scanBuf;
			tmp.buffer = param
			do {
				var note = parse_acc_pit(tmp)
				if (!note)
					return [s, null]
					break
				s.k_a_acc.push(note);
				c = param[tmp.index]
				while (c == ' ')
					c = param[++tmp.index]
			} while (c == '^' || c == '_' || c == '=');
			if (!exp)
				set_k_acc(s, sf)
			param = param.slice(tmp.index)
		} else if (s.k_exp && param.indexOf("none") == 0) {
			s.k_sf = 0;
		} else if (exp && param.indexOf("none") == 0) {
			sf = 0
			param = param.replace(/\w+\s*/, '')
		}
	}

	s.k_sf = sf;
	s.k_delta = cgd2cde[(s.k_sf + 7) % 7];

	// set the map of the notes with accidentals
	if (s.k_a_acc) {
		s.k_map = []
		i = s.k_a_acc.length
		while (--i >= 0) {
			note = s.k_a_acc[i]
			s.k_map[(note.pit + 19) % 7] = note.acc
		}
	} else {
		s.k_map = abc2svg.keys[sf + 7]
	}
	s.k_mode = mode

	// key note as base-40
	s.k_b40 = [1,24,7,30,13,36,19, 2 ,25,8,31,14,37,20,3][sf + 7]

	return [s, info_split(param)]
}

// M: meter
function new_meter(p) {
	var	s = {
			type: C.METER,
︙
693
694
695
696
697
698
699
700

701
702

703
704
705

706
707
708
709

710
711
712
713

714
715
716
717
718
719

720
721
722
723
724
725
726
709
710
711
712
713
714
715

716
717

718
719
720

721
722
723
724

725
726
727
728

729
730
731
732
733
734
735
736
737
738
739
740
741
742
743







-
+

-
+


-
+



-
+



-
+






+







					m2 = 4
				}
				break
			case 'c':
			case 'o':
				meter.top = p[i++]
				if (!m1) {
					if (p[-1] == 'c') {
					if (p[i - 1] == 'c') {
						m1 = 2;
						m2 = 4
						m2 = 4	// c = 2/4
					} else {
						m1 = 3;
						m2 = 4
						m2 = 4	// o = 3/4
					}
					switch (p[i]) {
					case '|':
						m2 /= 2
						m2 /= 2	// c| = 2/2, o| = 3/2
						break
					case '.':
						m1 *= 3;
						m2 *= 2
						m2 *= 2	// c. = 6/8, o. = 9/8
						break
					}
				}
				break
			case '.':
			case '|':
				m1 = 0;
				meter.top = p[i++]
				break
			case '(':
				if (p[i + 1] == '(') {	/* "M:5/4 ((2+3)/4)" */
					in_parenth = true;
					meter.top = p[i++];
					s.a_meter.push(meter);
︙
835
836
837
838
839
840
841
842
843
844
845
846





















847
848
849
850
851
852
853
854
855
856


857
858
859
860
861
862






863



864
865
866
867
868
869
870
871






872
873
874
875
876
877
878








879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896




897
898

899
900
901
902
903
904
905







906
907
908
909
910

911
912
913
914
915
916
917
918




919

920
921
922
923
924
925
926
852
853
854
855
856
857
858





859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887


888
889
890
891
892



893
894
895
896
897
898

899
900
901
902
903
904





905
906
907
908
909
910







911
912
913
914
915
916
917
918






919











920
921
922
923


924
925






926
927
928
929
930
931
932





933








934
935
936
937

938
939
940
941
942
943
944
945







-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+








-
-
+
+



-
-
-
+
+
+
+
+
+
-
+
+
+



-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
+

-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
+
-
-
-
-
-
-
-
-
+
+
+
+
-
+







			sym_link(s)
		}
	}
}

/* Q: tempo */
function new_tempo(text) {
	var	i = 0, j, c, nd, tmp,
		s = {
			type: C.TEMPO,
			dur: 0
		}
    var	i, c, d, nd,
	s = {
		type: C.TEMPO,
		dur: 0
	}

	// get a note duration
	function get_nd(p) {
	    var	n, d,
		nd = p.match(/(\d+)\/(\d+)/)

		if (nd) {
			d = Number(nd[2])
			if (d && !isNaN(d) && !(d & (d - 1))) {
				n = Number(nd[1])
				if (!isNaN(n))
					return C.BLEN * n / d
			}
		}
		syntax(1, "Invalid note duration $1", c)
	} // get_nd()

	set_ref(s)

	if (cfmt.writefields.indexOf('Q') < 0)
		s.del = true			// don't display

	/* string before */
	if (text[0] == '"') {
		i = text.indexOf('"', 1)
		if (i < 0) {
		c = text.match(/"([^"]*)"/)		// "
		if (!c) {
			syntax(1, "Unterminated string in Q:")
			return
		}
		s.tempo_str1 = text.slice(1, i);
		i++
		while (text[i] == ' ')
		s.tempo_str1 = c[1]
		text = text.slice(c[0].length).replace(/^\s+/,'')
	}

	// string after
	if (text.slice(-1) == '"') {
			i++
		i = text.indexOf('"')
		s.tempo_str2 = text.slice(i + 1, -1)
		text = text.slice(0, i).replace(/\s+$/,'')
	}

	/* beat */
	tmp = new scanBuf();
	tmp.buffer = text;
	tmp.index = i
	while (1) {
//		c = tmp.char()
	i = text.indexOf('=')
	if (i > 0) {
		d = text.slice(0, i).split(/\s+/)
		text = text.slice(i + 1).replace(/^\s+/,'')
		while (1) {
			c = d.shift()
		c = text[tmp.index]
		if (c == undefined || c <= '0' || c > '9')
			break
		nd = parse_dur(tmp)
		if (!s.tempo_notes)
			s.tempo_notes = []
		s.tempo_notes.push(C.BLEN * nd[0] / nd[1])
			if (!c)
				break
			nd = get_nd(c)
			if (!nd)
				return
			if (!s.tempo_notes)
				s.tempo_notes = []
			s.tempo_notes.push(nd)
		while (1) {
//			c = tmp.char()
			c = text[tmp.index]
			if (c != ' ')
				break
			tmp.index++
		}
	}

	/* tempo value */
	if (c == '=') {
		c = text[++tmp.index]
		while (c == ' ')
			c = text[++tmp.index];
		i = tmp.index
		if (c == 'c' && text[i + 1] == 'a'
		 && text[i + 2] == '.' && text[i + 3] == ' ') {
			s.tempo_ca = 'ca. ';

		// tempo value
		if (text.slice(0, 4) == "ca. ") {
			s.tempo_ca = 'ca. '
			tmp.index += 4;
//			c = text[tmp.index]
			text = text.slice(4)
		}
		if (text[tmp.index + 1] != '/') {
			s.tempo = tmp.get_int()
		} else {
			nd = parse_dur(tmp);
			s.new_beat = C.BLEN * nd[0] / nd[1]
		}
		i = text.indexOf('/')
		if (i > 0) {
			nd = get_nd(text)
			if (!nd)
				return
			s.new_beat = nd
		} else {
		c = text[tmp.index]
		while (c == ' ')
			c = text[++tmp.index]
	}

			s.tempo = Number(text)
	/* string after */
	if (c == '"') {
		tmp.index++;
		i = text.indexOf('"', tmp.index + 1)
		if (i < 0) {
			syntax(1, "Unterminated string in Q:")
			return
		}
			if (!s.tempo || isNaN(s.tempo)) {
				syntax(1, "Bad tempo value")
				return
			}
		s.tempo_str2 = text.slice(tmp.index, i)
		}
	}

	if (parse.state != 3) {
		if (parse.state == 1) {			// tune header
			info.Q = text;
			glovar.tempo = s
			return
︙
1027
1028
1029
1030
1031
1032
1033
1034

1035
1036

1037
1038
1039
1040
1041
1042
1043
1046
1047
1048
1049
1050
1051
1052

1053
1054

1055
1056
1057
1058
1059
1060
1061
1062







-
+

-
+







			break
		new_tempo(text)
		break
	case 'V':
		get_voice(text)
		break

	// key signature at end of tune header on in tune body
	// key signature at end of tune header or in tune body
	case 'K':
		if (parse.state == 0)
		if (!parse.state)	// ignore if in file header
			break
		get_key(text)
		break

	// info in any state
	case 'N':
	case 'R':
︙
1063
1064
1065
1066
1067
1068
1069
1070

1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084

1085
1086
1087
1088
1089
1090
1091
1092

1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110


1111
1112
1113
1114
1115

1116
1117
1118
1119
1120
1121
1122
1123
1082
1083
1084
1085
1086
1087
1088

1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111

1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128


1129
1130
1131
1132
1133
1134

1135

1136
1137
1138
1139
1140
1141
1142







-
+














+







-
+
















-
-
+
+




-
+
-







	}
}

// music line parsing functions

/* -- adjust the duration and time of symbols in a measure when L:auto -- */
function adjust_dur(s) {
	var s2, time, auto_time, i, res;
    var	s2, time, auto_time, i, fac;

	/* search the start of the measure */
	s2 = curvoice.last_sym
	if (!s2)
		return;

	/* the bar time is correct if there are multi-rests */
	if (s2.type == C.MREST
	 || s2.type == C.BAR)			/* in second voice */
		return
	while (s2.type != C.BAR && s2.prev)
		s2 = s2.prev;
	time = s2.time;
	auto_time = curvoice.time - time
	fac = curvoice.wmeasure / auto_time

	/* remove the invisible rest at start of tune */
	if (time == 0) {
		while (s2 && !s2.dur)
			s2 = s2.next
		if (s2 && s2.type == C.REST
		 && s2.invis) {
			time += s2.dur * curvoice.wmeasure / auto_time
			time += s2.dur * fac
			if (s2.prev)
				s2.prev.next = s2.next
			else
				curvoice.sym = s2.next
			if (s2.next)
				s2.next.prev = s2.prev;
			s2 = s2.next
		}
	}
	if (curvoice.wmeasure == auto_time)
		return				/* already good duration */

	for ( ; s2; s2 = s2.next) {
		s2.time = time
		if (!s2.dur || s2.grace)
			continue
		s2.dur = s2.dur * curvoice.wmeasure / auto_time;
		s2.dur_orig = s2.dur_orig * curvoice.wmeasure / auto_time;
		s2.dur *= fac;
		s2.dur_orig *= fac;
		time += s2.dur
		if (s2.type != C.NOTE && s2.type != C.REST)
			continue
		for (i = 0; i <= s2.nhd; i++)
			s2.notes[i].dur = s2.notes[i].dur
			s2.notes[i].dur *= fac
					 * curvoice.wmeasure / auto_time;
	}
	curvoice.time = s.time = time
}

/* -- parse a bar -- */
function new_bar() {
	var	s2, c, bar_type,
︙
1151
1152
1153
1154
1155
1156
1157


1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170

1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185

1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255




























1256
1257
1258
1259
1260
1261
1262
1263

1264
1265
1266
1267
1268
1269
1270
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190

1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205

1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225


1226
1227
1228
1229
1230
1231
1232
1233
1234
1235

1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246




1247
1248

1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301



1302
1303
1304
1305
1306
1307
1308
1309







+
+












-
+














-
+



















-
-










-











-
-
-
-


-




















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
-
+







	}
	if (bar_type[0] == ':') {
		if (bar_type.length == 1) {	// ":" alone
			bar_type = '|';
			s.bar_dotted = true
		} else {
			s.rbstop = 2		// right repeat with end
			if (curvoice.tie_s)
				curvoice.tie_s.tie_s = s
		}
	}

	// set the guitar chord and the decorations
	if (a_gch)
		self.gch_build(s)
	if (a_dcn) {
		deco_cnv(a_dcn, s);
		a_dcn = null
	}

	// set the start/stop of ottava
	if (parse.ottava != undefined) {
	if (parse.ottava.length) {
		s2 = s
		if (curvoice.cst != curvoice.st) {	// if staff change
			s2 = {
				type: C.SPACE,		// put the decoration on a ...
				fname: parse.fname,
				istart: parse.bol + line.index,
				dur: 0,
				multi: 0,
				invis: true,
				width: 1		// .. small space
			}
			sym_link(s2)
		}
		s2.ottava = parse.ottava
		delete parse.ottava
		parse.ottava = []
	}

	/* if the last element is '[', it may start
	 * a chord or an embedded header */
	switch (bar_type.slice(-1)) {
	case '[':
		if (/[0-9" ]/.test(c))		// "
			break
		bar_type = bar_type.slice(0, -1);
		line.index--;
		c = '['
		break
	case ':':				// left repeat
		s.rbstop = 2			// with bracket end
		break
	}

	// check if repeat bar
	if (c > '0' && c <= '9') {
		if (bar_type.slice(-1) == '[')
			bar_type = bar_type.slice(0, -1);
		s.text = c
		while (1) {
			c = line.next_char()
			if ("0123456789,.-".indexOf(c) < 0)
				break
			s.text += c
		}
		s.rbstop = 2;
		s.rbstart = 2
	} else if (c == '"' && bar_type.slice(-1) == '[') {
		bar_type = bar_type.slice(0, -1);
		s.text = ""
		while (1) {
			c = line.next_char()
			if (!c) {
				syntax(1, "No end of repeat string")
				return
			}
			if (c == '"') {
				line.index++
				break
			}
			if (c == '\\') {
				s.text += c;
				c = line.next_char()
			}
			s.text += c
		}
		s.text = cnv_escape(s.text);
		s.rbstop = 2;
		s.rbstart = 2
	}

	// ']' as the first character indicates a repeat bar stop
	if (bar_type[0] == ']') {
		s.rbstop = 2			// with end
		if (bar_type.length != 1)
			bar_type = bar_type.slice(1)
		else
			s.invis = true
	}

	s.iend = parse.bol + line.index

	if (s.rbstart
	 && curvoice.norepbra
	 && !curvoice.second)
		s.norepbra = true

	// handle the accidentals (ties and repeat)
	if (s.text) {
		if (bar_type.slice(-1) == '[')
			bar_type = bar_type.slice(0, -1)
		if (s.text[0] == '1') {
			if (curvoice.tie_s)
				curvoice.tie_s_rep = curvoice.tie_s
			if (curvoice.acc_tie)
				curvoice.acc_tie_rep = curvoice.acc_tie.slice()
			else if (curvoice.acc_tie_rep)
				curvoice.acc_tie_rep = null
		} else {
			if (curvoice.tie_s_rep) {
				curvoice.tie_s = clone(curvoice.tie_s_rep)
				curvoice.tie_s.notes = clone(curvoice.tie_s.notes)
				for (var m = 0; m <= curvoice.tie_s.nhd; m++) {
					curvoice.tie_s.notes[m] =
						clone(curvoice.tie_s.notes[m])
					curvoice.tie_s.notes[m].s =
						curvoice.tie_s
				}
			}
			if (curvoice.acc_tie_rep)
				curvoice.acc_tie = curvoice.acc_tie_rep.slice()
		}
	}
	curvoice.acc = []			// no accidental anymore

	if (curvoice.ulen < 0)			// L:auto
		adjust_dur(s);

	s2 = curvoice.last_sym
	if (s2 && s2.time == curvoice.time) {
		if (s2.type == C.SPACE) {
			s2.time--		// keep the space at the right place
		} else if (bar_type == "["
		if (bar_type == "["
			|| bar_type == "|:") {

			// search if a previous bar at this time
			do {
				if (s2.type == C.BAR)
					break
				if (w_tb[s2.type]) // symbol with a width
︙
1376
1377
1378
1379
1380
1381
1382
1383

1384
1385
1386

1387
1388
1389
1390
1391
1392
1393
1415
1416
1417
1418
1419
1420
1421

1422
1423
1424

1425
1426
1427
1428
1429
1430
1431
1432







-
+


-
+







		err = false,
		flags = 0,
		brace = 0,
		bracket = 0,
		parenth = 0,
		flags_st = 0,
	e,
	a = p.match(/\w+|[^\s]/g)
	a = p.match(/[^[\]|{}()*+\s]+|[^\s]/g)

	if (!a) {
		syntax(1, errs.bad_val, "%%staves")
		syntax(1, errs.bad_val, "%%score")
		return // null
	}
	while (1) {
		e = a.shift()
		if (!e)
			break
		switch (e) {
︙
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438






1439
1440
1441

1442
1443
1444
1445
1446
1447
1448
1466
1467
1468
1469
1470
1471
1472





1473
1474
1475
1476
1477
1478



1479
1480
1481
1482
1483
1484
1485
1486







-
-
-
-
-
+
+
+
+
+
+
-
-
-
+







		case '*':
			if (brace && !parenth && !(flags & (OPEN_BRACE | OPEN_BRACE2)))
				flags |= FL_VOICE
			break
		case '+':
			flags |= MASTER_VOICE
			break
		default:
			if (!/\w/.test(e)) {
				syntax(1, "Bad voice ID in %%staves");
				err = true
				break
		case ']':
		case '}':
		case ')':
			syntax(1, "Bad voice ID in %%score");
			err = true
			break
			}

			/* get / create the voice in the voice table */
		default:	// get / create the voice in the voice table
			vid = e
			while (1) {
				e = a.shift()
				if (!e)
					break
				switch (e) {
				case ']':
︙
1491
1492
1493
1494
1495
1496
1497
1498

1499
1500
1501

1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1529
1530
1531
1532
1533
1534
1535

1536
1537
1538

1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556



















































1557
1558
1559
1560
1561
1562
1563







-
+


-
+

















-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







			if (!e)
				break
			a.unshift(e)
			break
		}
	}
	if (flags_st != 0) {
		syntax(1, "'}', ')' or ']' missing in %%staves");
		syntax(1, "'}', ')' or ']' missing in %%score");
		err = true
	}
	if (err || a_vf.length == 0)
	if (err || !a_vf.length)
		return //null
	return a_vf
}

// split an info string
function info_split(text) {
	if (!text)
		return []
    var	a = text.match(/=?[^\s"=]+=?|".+?"/g)	// "
	if (!a) {
//fixme: bad error text
		syntax(1, "Unterminated string")
		return []
	}
	return a
}

/* -- get head type, dots, flags of note/rest for a duration -- */
function identify_note(s, dur_o) {
    var	head, dots, flags,
	dots = 0,
	dur = dur_o

	if (dur % 12 != 0)
		error(1, s, "Invalid note duration $1", dur);
	dur /= 12			/* see C.BLEN for values */
	if (dur == 0)
		error(1, s, "Note too short")
	for (flags = 5; dur != 0; dur >>= 1, flags--) {
		if (dur & 1)
			break
	}
	dur >>= 1
	switch (dur) {
	case 0: break
	case 1: dots = 1; break
	case 3: dots = 2; break
	case 7: dots = 3; break
	default:
		error(1, s, "Invalid note duration $1", dur_o);
		flags += ((11 - dur) / 4) | 0;
		dots = 4
		break
	}
	flags -= dots
	if (flags >= 0) {
		head = C.FULL
	} else switch (flags) {
	default:
		error(1, s, "Note too long");
		flags = -4
		/* fall thru */
	case -4:
		head = C.SQUARE
		break
	case -3:
		head = cfmt.squarebreve ? C.SQUARE : C.OVALBARS
		break
	case -2:
		head = C.OVAL
		break
	case -1:
		head = C.EMPTY
		break
	}
	return [head, dots, flags]
}

// parse a duration and return [numerator, denominator]
// 'line' is not always 'parse.line'
var reg_dur = /(\d*)(\/*)(\d*)/g		/* (stop comment) */

function parse_dur(line) {
	var res, num, den;

︙
1646
1647
1648
1649
1650
1651
1652
1653

1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665

1666
1667




1668
1669
1670
1671
1672






1673
1674
1675
1676
1677

1678
1679
1680

1681
1682
1683
1684


1685
1686
1687

1688
1689
1690
1691


1692
1693
1694
1695
1696
1697
1698
1699
1700
1701


1702
1703





1704
1705
1706
1707
1708
1709
1710
1633
1634
1635
1636
1637
1638
1639

1640

1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652


1653
1654
1655
1656





1657
1658
1659
1660
1661
1662





1663



1664

1665
1666

1667
1668
1669
1670

1671
1672



1673
1674
1675
1676
1677
1678
1679
1680
1681
1682


1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698







-
+
-











+
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
+
-
-
-
+
-


-
+
+


-
+

-
-
-
+
+








-
-
+
+


+
+
+
+
+







	while (c == ',') {
		pit -= 7;
		c = line.next_char()
	}
	note = {
		pit: pit,
		shhd: 0,
		shac: 0,
		shac: 0
		ti1: 0
	}
	if (acc) {
		note.acc = acc
		if (micro_n) {
			note.micro_n = micro_n;
			note.micro_d = micro_d
		}
	}
	return note
}

/* set the mapping of a note */
// convert a note pitch to ABC text
function note2abc(note) {
//
// The global 'maps' object is indexed by the map name.
// Its content is an object ('map') indexed from the map type:
// - normal = b40 pitch
    var	i,
	abc = 'abcdefg'[(note.pit + 77) % 7]

//fixme: treat microtone
	if (note.acc)
// - octave = 'o' + b40 pitch in C..B interval
// - key    = 'k' + scale index
// - all    = 'all'
// The 'map' is stored in the note. It is an array of
//	[0] array of heads (glyph names)
//	[1] print (note)
		abc = ['__', '_', '', '^', '^^', '='][note.acc + 2] + abc
	for (i = note.pit; i >= 30; i -= 7)	// down to 'c'
		abc += "'"
	for (i = note.pit; i < 23; i += 7)	// up to 'C'
		abc += ","
//	[2] color
	return abc
}

//	[3] play (b40)
/* set the mapping of a note */
function set_map(note) {
    var	map = maps[curvoice.map],	// never null
	nn = note2abc(note)
	n = note.b40,
	nn = n.toString()

	if (!map[nn]) {
		nn = 'octave,' + nn.replace(/[',]/g, '')	// octave '
		nn = 'o' + (n % 40).toString()		// octave
		if (!map[nn]) {
			nn = 'key,' +			// 'key,'
				'abcdefg'[(note.pit + 77 -
						curvoice.ckey.k_delta) % 7]
			n += 2 - curvoice.ckey.k_b40	// key
			nn = 'k' + (abc2svg.b40_p[n % 40]).toString()
			if (!map[nn]) {
				nn = 'all'		// 'all'
				if (!map[nn])
					return
			}
		}
	}
	note.map = map[nn]
	if (note.map[1]) {
		note.pit = note.map[1].pit;		// print/play
	if (note.map[1]) {				// if print map
		note.pit = note.map[1].pit
		note.acc = note.map[1].acc
	}
	if (note.map[2])				// if color
		note.color = note.map[2]
	nn = note.map[3]
	if (nn)						// if play map
		note.b40 = nn
}

/* -- parse note or rest with pitch and length -- */
// 'line' is not always 'parse.line'
function parse_basic_note(line, ulen) {
	var	nd,
		note = parse_acc_pit(line)
︙
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728


1729
1730
1731

1732
1733
1734
1735

1736
1737
1738

1739
1740

1741
1742
1743
1744
1745



1746
1747

1748
1749
1750
1751
1752
1753
1754





1755

1756
1757
1758



1759
1760






1761
1762
1763
1764







1765
1766

1767
1768

1769
1770
1771
1772
1773

1774
1775
1776
1777
1778




1779
1780
1781

1782
1783
1784
1785
1786
1787





















1788
1789
1790
1791


1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806

1807
1808
1809
1810
1811








1812
1813
1814
1815
1816
1817
1818
1819
1820
1821

1822
1823
1824


1825
1826
1827
1828
1829




1830
1831


1832
1833
1834
1835
1836
1837



1838
1839
1840
1841
1842
1843
1844






1845
1846
1847
1848
1849
1850
1851
1852
1853
1854





















1855
1856
1857
1858
1859
1860
1861



1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1707
1708
1709
1710
1711
1712
1713



1714
1715
1716
1717

1718
1719
1720
1721

1722
1723
1724

1725
1726

1727
1728
1729



1730
1731
1732
1733

1734







1735
1736
1737
1738
1739
1740
1741



1742
1743
1744


1745
1746
1747
1748
1749
1750
1751



1752
1753
1754
1755
1756
1757
1758
1759

1760


1761





1762





1763
1764
1765
1766



1767






1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788




1789
1790















1791





1792
1793
1794
1795
1796
1797
1798
1799










1800



1801
1802





1803
1804
1805
1806


1807
1808






1809
1810
1811







1812
1813
1814
1815
1816
1817










1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838







1839
1840
1841






1842
1843
1844
1845
1846
1847
1848







-
-
-
+
+


-
+



-
+


-
+

-
+


-
-
-
+
+
+

-
+
-
-
-
-
-
-
-
+
+
+
+
+

+
-
-
-
+
+
+
-
-
+
+
+
+
+
+

-
-
-
+
+
+
+
+
+
+

-
+
-
-
+
-
-
-
-
-
+
-
-
-
-
-
+
+
+
+
-
-
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
+
-
-
-
-
-
+
+
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-







	}
	nd = parse_dur(line);
	note.dur = ulen * nd[0] / nd[1]
	return note
}

function parse_vpos() {
	var	c,
		line = parse.line,
		ti1 = 0
	var	line = parse.line,
		ty = 0

	if (line.buffer[line.index - 1] == '.' && !a_dcn)
		ti1 = C.SL_DOTTED
		ty = C.SL_DOTTED
	switch (line.next_char()) {
	case "'":
		line.index++
		return ti1 + C.SL_ABOVE
		return ty + C.SL_ABOVE
	case ",":
		line.index++
		return ti1 + C.SL_BELOW
		return ty + C.SL_BELOW
	}
	return ti1 + C.SL_AUTO
	return ty + C.SL_AUTO
}

var	cde2fcg = new Int8Array([0, 2, 4, -1, 1, 3, 5]),
	cgd2cde = new Int8Array([0, 4, 1, 5, 2, 6, 3]),
	acc2 = new Int8Array([-2, -1, 3, 1, 2])
// on end of slur, create the slur
function slur_add(enote, e_is_note) {
    var	i, s, sl, snote, s_is_note

/* transpose a note / chord */
	// go back and find the last start of slur
function note_transp(s) {
	var	i, j, n, d, a, acc, i1, i3, i4, note,
		m = s.nhd,
		sf_old = curvoice.okey.k_sf,
		i2 = curvoice.ckey.k_sf - sf_old,
		dp = cgd2cde[(i2 + 4 * 7) % 7],
		t = curvoice.vtransp
	for (i = curvoice.sls.length; --i >= 0; ) {
		sl = curvoice.sls[i]
		snote = sl.note
		s_is_note = sl.is_note
		delete sl.is_note

		// the slur must not start and stop on a same symbol
	if (t < 0 && dp != 0)
		dp -= 7;
	dp += ((t / 3 / 12) | 0) * 7
		if (snote.s != enote.s) {
			sl.note = enote
			if (e_is_note)
	for (i = 0; i <= m; i++) {
		note = s.notes[i];
				sl.is_note = e_is_note
			s = s_is_note ? snote : snote.s
			if (!s.sls)
				s.sls = [];
			s.sls.push(sl)
			curvoice.sls.splice(i, 1)

		// pitch
		n = note.pit;
		note.pit += dp;
			// set a flag on the start symbol if slur from a note
			if (s_is_note)
				snote.s.sl1 = true

			// set a flag if the slur starts on a grace note
			if (sl.grace)
				sl.grace.sl1 = true

		// accidental
			// set a flag if the slur ends on a grace note
		i1 = cde2fcg[(n + 5 + 16 * 7) % 7];	/* fcgdaeb */
		a = note.acc
			if (enote.s.grace)
		if (!a) {
			if (!curvoice.okey.a_acc) {
				if (sf_old > 0) {
					if (i1 < sf_old - 1)
						a = 1	// sharp
				enote.s.sl2 = true
				} else if (sf_old < 0) {
					if (i1 >= sf_old + 6)
						a = -1	// flat
				}
			} else {
			return
		}
	}

				for (j = 0; j < curvoice.okey.a_acc.length; j++) {
					acc = curvoice.okey.a_acc[j]
					if ((n + 16 * 7 - acc.pit) % 7 == 0) {
	if (enote.grace) {
						a = acc.acc
						break
					}
				}
			}
		}
		error(1, enote.s, errs.bad_slur_end)
		return
	}

	// the lack of a starting slur may be due to a repeat
	for (s = enote.s.prev; s; s = s.prev) {
		if (s.type == C.BAR
		 && s.bar_type[0] == ':'
		 && s.text) {
			if (!s.sls)
				s.sls = [];
			s.sls.push({
				note: enote,
//fixme: should go back to the bar "|1" and find the slur type...
				ty: C.SL_AUTO
			})
			if (e_is_note)
				s.sls[s.sls.length - 1].is_note = e_is_note
			return
		}
	}
		i3 = i1 + i2
		if (a && a != 3)				// ! natural
			i3 += a * 7;

	syntax(1, "Lack of starting slur")
}
		i1 = ((((i3 + 1 + 21) / 7) | 0) + 2 - 3 + 32 * 5) % 5;
		a = acc2[i1]
		if (note.acc) {
			;
		} else if (curvoice.ckey.k_none) {
			if (a == 3		// natural
			 || acc_same_pitch(note.pit))
				continue
		} else if (curvoice.ckey.a_acc) {	/* acc list */
			i4 = cgd2cde[(i3 + 16 * 7) % 7]
			for (j = 0; j < curvoice.ckey.a_acc.length; j++) {
				if ((i4 + 16 * 7 - curvoice.ckey.a_acc[j].pits) % 7
							== 0)
					break
			}

			if (j < curvoice.ckey.a_acc.length)
				continue
		} else {
			continue
		}
// (possible hook)
Abc.prototype.new_note = function(grace, sls) {
    var	note, s, in_chord, c, dcn, type, tie_s, acc_tie,
	i, n, s2, nd, res, num, dur, apit,
	sl1 = [],
	line = parse.line,
	a_dcn_sav = a_dcn		// save parsed decoration names

		i1 = note.acc;
		d = note.micro_d
		if (d				/* microtone */
		 && i1 != a) {			/* different accidental type */
			n = note.micro_n
//fixme: double sharps/flats ?*/
//fixme: does not work in all cases (tied notes, previous accidental)
			switch (a) {
			case 3:			// natural
				if (n > d / 2) {
	if (!grace
					n -= d / 2;
					note.micro_n = n;
					a = i1
	 && curvoice.tie_s) {		// if tie from previous note / grace note
		tie_s = curvoice.tie_s
				} else {
					a = -i1
				}
				break
			case 2:			// double sharp
		curvoice.tie_s = null
	}

	// handle the ties
				if (n > d / 2) {
					note.pit += 1;
	function do_ties(s, tie_s) {
	    var	m, note
					n -= d / 2
				} else {
					n += d / 2
				}
				a = i1;
				note.micro_n = n

		for (m = 0; m <= s.nhd; m++) {
			note = s.notes[m]
				break
			case -2:		// double flat
				if (n >= d / 2) {
					note.pit -= 1;
					n -= d / 2
				} else {
					n += d / 2
			if (tie_s.type != C.GRACE) {
				for (i = 0; i <= tie_s.nhd; i++) {
					if (!tie_s.notes[i].tie_ty)
						continue
					// (tie_s.notes[i].tie_n may exist
					//  on repeat restart)
				}
				a = i1;
				note.micro_n = n
				break
			}
		}
		note.acc = a
	}
}

					if (tie_s.notes[i].b40 == note.b40) {
						tie_s.notes[i].tie_n = note
						note.s = s
						tie_s.tie_s = s
						break
					}
				}
			} else {
				for (s2 = tie_s.extra; s2; s2 = s2.next) {
					if (!s2.notes[0].tie_ty)
						continue
					if (s2.notes[0].b40 == note.b40) {
						s2.tie_s = s
						s2.notes[0].tie_n = note
						note.s = s
						s2.notes[0].s = s2
						tie_s.tie_s = s
						break
					}
				}
			}
/* sort the notes of the chord by pitch (lowest first) */
function sort_pitch(s) {
	s.notes = s.notes.sort(function(n1, n2) {
			return n1.pit - n2.pit
		})
}
// (possible hook)
			s.ti2 = tie_s	// pointer to the tie start
		}
	} // do_ties()
function new_note(grace, tp_fact) {
	var	note, s, in_chord, c, dcn, type,
		i, n, s2, nd, res, num, dur,
		sl1 = 0,
		line = parse.line,
		a_dcn_sav = a_dcn;	// save parsed decoration names

	a_dcn = null;
	parse.stemless = false;
	s = {
		type: C.NOTE,
		fname: parse.fname,
		stem: 0,
︙
1923
1924
1925
1926
1927
1928
1929


1930
1931
1932
1933
1934
1935
1936
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912







+
+







		s.invis = true;
		s.dur = 0;
		c = line.next_char()
		if (c >= '0' && c <= '9')
			s.width = line.get_int()
		else
			s.width = 10
		if (tie_s)
			curvoice.tie_s = tie_s
		break
	case 'x':
		s.invis = true
	case 'z':
		s.type = C.REST;
		line.index++;
		nd = parse_dur(line);
︙
1944
1945
1946
1947
1948
1949
1950




1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972

1973
1974
1975
1976
1977
1978
1979
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950


1951
1952
1953
1954
1955
1956
1957
1958







+
+
+
+




















-
-
+







		}]
		break
	case '[':			// chord
		in_chord = true;
		c = line.next_char()
		// fall thru
	default:			// accidental, chord, note
		if (curvoice.acc_tie) {
			acc_tie = curvoice.acc_tie
			curvoice.acc_tie = null
		}
		if (curvoice.uscale)
			s.uscale = curvoice.uscale;
		s.notes = []

		// loop on the chord
		while (1) {

			// when in chord, get the slurs and decorations
			if (in_chord) {
				while (1) {
					if (!c)
						break
					i = c.charCodeAt(0);
					if (i >= 128) {
						syntax(1, errs.not_ascii)
						return //null
					}
					type = char_tb[i]
					switch (type[0]) {
					case '(':
						sl1 <<= 4;
						sl1 += parse_vpos();
						sl1.push(parse_vpos());
						c = line.char()
						continue
					case '!':
						if (!a_dcn)
							a_dcn = []
						if (type.length > 1) {
							a_dcn.push(type.slice(1, -1))
︙
2004
2005
2006
2007
2008
2009
2010

2011
2012
2013





2014
2015
2016
2017



























2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033

2034
2035

2036
2037
2038
2039
2040
2041
2042
2043
2044












2045
2046
2047
2048

2049
2050
2051






2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069

















2070
2071
2072
2073
2074
2075
2076

2077
2078
2079
2080
2081

2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
1983
1984
1985
1986
1987
1988
1989
1990



1991
1992
1993
1994
1995




1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036


2037


2038




2039
2040
2041


2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058



2059
2060
2061
2062
2063
2064


2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103

2104
2105
2106
2107
2108

2109
2110
2111
2112
2113
2114
2115
2116

2117
2118
2119
2120
2121
2122
2123







+
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+














-
-
+
-
-
+
-
-
-
-



-
-
+
+
+
+
+
+
+
+
+
+
+
+




+
-
-
-
+
+
+
+
+
+
-
-
















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






-
+




-
+







-







						curvoice.ulen)
			if (!note)
				return //null

			// transpose
			if (curvoice.octave)
				note.pit += curvoice.octave * 7
			apit = note.pit + 19		// pitch from C-1
			if (sl1) {
				note.sl1 = sl1
				if (s.sl1)

			// get the explicit or implicit accidental
			// and keep the absolute pitch in base-40
			i = note.acc
			if (i) {
					s.sl1++
				else
					s.sl1 = 1;
				sl1 = 0
				curvoice.acc[apit] = i
			} else {
				i = curvoice.acc[apit]
				if (!i && acc_tie)
					i = acc_tie[apit]
				if (!i)
				    i = curvoice.ckey.k_map[apit % 7] || 0
			}
			note.b40 = abc2svg.pab40(note.pit, i)

			if (curvoice.map
			 && maps[curvoice.map])
				set_map(note)

			// starting slurs
			if (sl1.length) {
				while (1) {
					i = sl1.shift()
					if (!i)
						break
					curvoice.sls.push({
						is_note: true,
						note: note,
						ty: i
					})
				}
				note.s = s;		// link the note to the chord
			}
			if (a_dcn) {
				note.a_dcn = a_dcn;
				a_dcn = null
			}
			s.notes.push(note)
			if (!in_chord)
				break

			// in chord: get the ending slurs and the ties
			c = line.char()
			while (1) {
				switch (c) {
				case ')':
					if (note.sl2)
						note.sl2++
					note.s = s
					else
						note.sl2 = 1
					slur_add(note, true)
					if (s.sl2)
						s.sl2++
					else
						s.sl2 = 1;
					c = line.next_char()
					continue
				case '-':
					note.ti1 = parse_vpos();
					s.ti1 = true;
					note.tie_ty = parse_vpos()
					note.s = s
					curvoice.tie_s = s
					if (curvoice.acc[apit]
					 || (acc_tie
					  && acc_tie[apit])) {
						if (!curvoice.acc_tie)
							curvoice.acc_tie = []
						curvoice.acc_tie[apit] =
							curvoice.acc[apit] ||
								acc_tie[apit]
					}
					c = line.char()
					continue
				case '.':
					c = line.next_char()
					switch (c) {
					if (c != '-') {
						syntax(1, "Misplaced dot")
						break
					case '-':
					case '(':
						continue
					}
					syntax(1, "Misplaced dot")
					break
					}
					continue
				}
				break
			}
			if (c == ']') {
				line.index++;

				// adjust the chord duration
				nd = parse_dur(line);
				s.nhd = s.notes.length - 1
				for (i = 0; i <= s.nhd ; i++) {
					note = s.notes[i];
					note.dur = note.dur * nd[0] / nd[1]
				}
				break
			}
		}

		// handle the starting slurs
		if (sls.length) {
			while (1) {
				i = sls.shift()
				if (!i)
					break
				s.notes[0].s = s
				curvoice.sls.push({
					note: s.notes[0],
					ty: i
				})
				if (grace)
					curvoice.sls[curvoice.sls.length - 1].grace =
										grace
			}
		}

		// the duration of the chord is the duration of the 1st note
		s.dur_orig = s.notes[0].dur;
		s.dur = s.notes[0].dur * curvoice.dur_fact
	}
	if (s.grace && s.type != C.NOTE) {
		syntax(1, "Not a note in grace note sequence")
		syntax(1, errs.bad_grace)
		return //null
	}

	if (s.notes) {				// if note or rest
		if (!s.grace) {
		if (!grace) {
			switch (curvoice.pos.stm) {
			case C.SL_ABOVE: s.stem = 1; break
			case C.SL_BELOW: s.stem = -1; break
			case C.SL_HIDDEN: s.stemless = true; break
			}

			// adjust the symbol duration
			s.dur *= tp_fact;
			num = curvoice.brk_rhythm
			if (num) {
				curvoice.brk_rhythm = 0;
				s2 = curvoice.last_note
				if (num > 0) {
					n = num * 2 - 1;
					s.dur = s.dur * n / num;
︙
2128
2129
2130
2131
2132
2133
2134

2135
2136
2137
2138























2139
2140




2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152




















2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178

2179
2180

2181
2182
2183
2184
2185
2186
2187



























2188
2189

2190
2191
2192
2193
2194
2195
2196
2155
2156
2157
2158
2159
2160
2161
2162




2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185


2186
2187
2188
2189












2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213










2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224

2225
2226

2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262

2263
2264
2265
2266
2267
2268
2269
2270







+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




-
-
-
-
-
-
-
-
-
-











-
+

-
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+







				s.notes[i].dur /= div;
			s.dur /= div;
			s.dur_orig /= div
			if (grace.stem)
				s.stem = grace.stem
		}

		curvoice.last_note = s
		// adjust the duration
		dur = s.dur_orig
		if (curvoice.ulen < 0) {
			dur = C.BLEN

		// get the possible ties and end of slurs
		c = line.char()
		while (1) {
			switch (c) {
			case '-':
			    var	ty = parse_vpos()
				for (i = 0; i <= s.nhd; i++) {
					s.notes[i].tie_ty = ty
					s.notes[i].s = s
				}
				if (grace)
					grace.tie_s = curvoice.tie_s = grace
				else
					curvoice.tie_s = s
				for (i = 0; i <= s.nhd; i++) {
					note = s.notes[i]
					apit = note.pit + 19	// pitch from C-1
					if (curvoice.acc[apit]
					 || (acc_tie
					  && acc_tie[apit])) {
						if (!curvoice.acc_tie)
							curvoice.acc_tie = []
		} else if (s.type == C.REST

						curvoice.acc_tie[apit] =
							curvoice.acc[apit] ||
								acc_tie[apit]
					}
			/* change the figure of whole measure rests */
//--fixme: does not work in abcm2ps sample.abc because broken rhythm on measure bar
			&& dur == curvoice.wmeasure) {
			if (dur < C.BLEN * 2)
				dur = C.BLEN
			else if (dur < C.BLEN * 4)
				dur = C.BLEN * 2
			else
				dur = C.BLEN * 4
		}

		curvoice.last_note = s
				}
				c = line.char()
				continue
			case ')':
				s.notes[0].s = s
				slur_add(s.notes[0])
				c = line.next_char()
				continue
			case '.':
				if (line.buffer[line.index + 1] != '-')
					break
				c = line.next_char()
				continue
			}
			break
		}

		// handle the ties ending on this chord/note
		if (tie_s)		// if tie from previous note / grace note
			do_ties(s, tie_s)
	}

	sym_link(s)

	if (s.type == C.NOTE) {
		if (curvoice.vtransp)
			note_transp(s)
		if (curvoice.map
		 && maps[curvoice.map]) {
			for (i = 0; i <= s.nhd; i++)
				set_map(s.notes[i])
		}
	}

	if (cfmt.shiftunison)
		s.shiftunison = cfmt.shiftunison
	if (!grace) {
		if (!curvoice.lyric_restart)
			curvoice.lyric_restart = s
		if (!curvoice.sym_restart)
			curvoice.sym_restart = s
	}

	if (a_dcn_sav)
		deco_cnv(a_dcn_sav, s, s.prev)
	if (parse.ottava != undefined) {
	if (parse.ottava.length) {
		s.ottava = parse.ottava
		delete parse.ottava
		parse.ottava = []
	}
	if (parse.stemless)
		s.stemless = true
	s.iend = parse.bol + line.index
	return s
}

// adjust the duration of the elements in a tuplet
function tp_adj(s, fact) {
    var	tim = s.time

	curvoice.time = s.time + (curvoice.time - s.time) * fact
	while (1) {
//fixme: tuplets in grace notes?
		s.in_tuplet = true
		if (!s.grace) {
			s.time = tim
			if (s.dur) {
				s.dur = Math.round(s.dur * fact)
				tim += s.dur
			}
		}
		if (!s.next) {
			s.dur = curvoice.time - s.time
			if (s.tpe)
				s.tpe++
			else
				s.tpe = 1
			break
		}
		s = s.next
	}
} // tp_adj()

// characters in the music line (ASCII only)
var nil = ["0"],
var nil = "0",
    char_tb = [
	nil, nil, nil, nil,		/* 00 - .. */
	nil, nil, nil, nil,
	nil, " ", "\n", nil,		/* . \t \n . */
	nil, nil, nil, nil,
	nil, nil, nil, nil,
	nil, nil, nil, nil,
︙
2224
2225
2226
2227
2228
2229
2230
2231
2232


2233
2234

2235
2236
2237
2238
2239
2240
2241
2242
2298
2299
2300
2301
2302
2303
2304


2305
2306
2307

2308

2309
2310
2311
2312
2313
2314
2315







-
-
+
+

-
+
-







	"n", "n", "n", "{",		/* x y z { */
	"|", "}", "!gmark!", nil,	/* | } ~ (del) */
],
    ottava = {"8va(":1, "8va)":0, "15ma(":2, "15ma)":0,
	"8vb(":-1, "8vb)":0, "15mb(":-2, "15mb)":0}

function parse_music_line() {
	var	grace, last_note_sav, a_dcn_sav, no_eol, s,
		tp_a = [], tp,
	var	grace, last_note_sav, a_dcn_sav, no_eol, s, tps,
		tp = [],
		tpn = -1,
		tp_fact = 1,
		sls = [],
		slur_start = 0,
		line = parse.line

	// check if a transposing macro matches a source sequence
	// if yes return the base note
	function check_mac(m) {
	    var	i, j, b

︙
2255
2256
2257
2258
2259
2260
2261

2262















2263
2264
2265
2266

2267
2268
2269
2270
2271
2272
2273

2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293


2294
2295
2296
2297

2298








































2299
2300
2301





2302
2303

2304
2305
2306
2307

2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333

2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349

2350
2351
2352
2353
2354
2355







2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370


2371
2372
2373
2374
2375
2376

2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388




2389
2390
2391
2392
2393
2394
2395
2328
2329
2330
2331
2332
2333
2334
2335

2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353

2354
2355
2356
2357
2358
2359
2360

2361










2362
2363
2364
2365
2366
2367
2368
2369


2370
2371
2372
2373
2374

2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416



2417
2418
2419
2420
2421
2422

2423
2424
2425
2426

2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444







2445

2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461

2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489

2490
2491
2492
2493
2494
2495
2496

2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520







+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+






-
+
-
-
-
-
-
-
-
-
-
-








-
-
+
+



-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+

-
+



-
+

















-
-
-
-
-
-
-

-
+















-
+






+
+
+
+
+
+
+














-
+
+





-
+












+
+
+
+







			while (line.buffer[j + 1] == ',') {
				b -= 7;
				j++
			}
		}
		line.index = j
		return b
	} // check_mac()
	}

	// convert a note as a number into a note as a ABC string
	function n2n(n) {
	    var	c = ntb[n]

		while (n < 0) {
			n += 7;
			c += ','
		}
		while (n > 14) {
			n -= 7;
			c += "'"
		}
		return c
	} // n2n()

	// expand a transposing macro
	function expand(m, b) {
	    var	c, d,
	    var	c, i,
		r = "",				// result
		n = m.length

		for (i = 0; i < n; i++) {
			c = m[i]
			if (c >= 'h' && c <= 'z') {
				d = b + c.charCodeAt(0) - 'n'.charCodeAt(0)
				r += n2n(b + c.charCodeAt(0) - 'n'.charCodeAt(0))
				c = ""
				while (d < 0) {
					d += 7;
					c += ','
				}
				while (d > 14) {
					d -= 7;
					c += "'"
				}
				r += ntb[d] + c
			} else {
				r += c
			}
		}
		return r
	} // expand()

	// parse a macro
	function parse_mac(m, b) {
	    var	seq,
	function parse_mac(k, m, b) {
	    var	te, ti, curv, s,
		line_sav = line,
		istart_sav = parse.istart;

		parse.line = line = new scanBuf();
		parse.line = line = new scanBuf;
		parse.istart += line_sav.index;

		// if the macro is not displayed
		if (cfmt.writefields.indexOf('m') < 0) {

			// build the display sequence from the original sequence
			line.buffer = k.replace('n', n2n(b))
			s = curvoice.last_sym
			ti = curvoice.time		// start time
			parse_seq(true)
			if (!s)
				s = curvoice.sym
			for (s = s.next ; s; s = s.next)
				s.noplay = true
			te = curvoice.time		// end time
			curv = curvoice

			// and put the macro sequence in a play specific voice
			curvoice = clone_voice(curv.id + '-p')
			if (!par_sy.voices[curvoice.v]) {
				curvoice.second = true
				par_sy.voices[curvoice.v] = {
					st: curv.st,
					second: true,
					range: curvoice.v
				}
			}
			curvoice.time = ti
			s = curvoice.last_sym
			parse.line = line = new scanBuf
			parse.istart += line_sav.index
			line.buffer = b ? expand(m, b) : m
			parse_seq(true)
			if (curvoice.time != te)
				syntax(1, "Bad length of the macro sequence")
			if (!s)
				s = curvoice.sym
			for ( ; s; s = s.next)
				s.invis = s.play = true
			curvoice = curv
		} else {
		line.buffer = b ? expand(m, b) : m;
		parse_seq(true);
		parse.line = line = line_sav;
			line.buffer = b ? expand(m, b) : m;
			parse_seq(true)
		}

		parse.line = line = line_sav
		parse.istart = istart_sav
	}
	} // parse_mac()

	// parse a music sequence
	function parse_seq(in_mac) {
	    var	c, idx, type, k, s, dcn, i, n, text
	    var	c, idx, type, k, s, dcn, i, n, text, note

		while (1) {
			c = line.char()
			if (!c)
				break

			// special case for '.' (dot)
			if (c == '.') {
				switch (line.buffer[line.index + 1]) {
				case '(':
				case '-':
				case '|':
					c = line.next_char()
					break
				}
			}

			idx = c.charCodeAt(0);
			if (idx >= 128) {
				syntax(1, errs.not_ascii);
				line.index++
				break
			}

			// check if start of a macro
			if (!in_mac && maci[idx]) {
			if (!in_mac && maci[c]) {
				n = 0
				for (k in mac) {
					if (!mac.hasOwnProperty(k)
					 || k[0] != c)
						continue
					if (k.indexOf('n') < 0) {
						if (line.buffer.indexOf(k, line.index)
								!= line.index)
							continue
						line.index += k.length
					} else {
						n = check_mac(k)
						if (!n)
							continue
					}
					parse_mac(mac[k], n);
					parse_mac(k, mac[k], n)
					n = 1
					break
				}
				if (n)
					continue
			}

			idx = c.charCodeAt(0)
			if (idx >= 128) {
				syntax(1, errs.not_ascii)
				line.index++
				break
			}

			type = char_tb[idx]
			switch (type[0]) {
			case ' ':			// beam break
				s = curvoice.last_note
				if (s) {
					s.beam_end = true
					if (grace)
						grace.gr_shift = true
				}
				break
			case '\n':			// line break
				if (cfmt.barsperstaff)
					break
				if (par_sy.voices[curvoice.v].range == 0
				if (par_sy.voices[curvoice.v]
				 && par_sy.voices[curvoice.v].range == 0
				 && curvoice.last_sym)
					curvoice.last_sym.eoln = true
				break
			case '&':			// voice overlay
				if (grace) {
					syntax(1, errs.bad_char, c)
					syntax(1, errs.bad_grace)
					break
				}
				c = line.next_char()
				if (c == ')') {
					get_vover(')')
					break
				}
				get_vover('&')
				continue
			case '(':			// slur start - tuplet - vover
				c = line.next_char()
				if (c > '0' && c <= '9') {	// tuplet
					if (grace) {
						syntax(1, errs.bad_grace)
						break
					}
				    var	pplet = line.get_int(),
					qplet = qplet_tb[pplet],
					rplet = pplet,
					c = line.char()

					if (c == ':') {
						c = line.next_char()
︙
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420








2421
2422

2423
2424
2425
2426
2427

2428
2429
2430
2431
2432
2433
2434
2435

2436
2437
2438
2439
2440
2441
2442





2443
2444

2445





2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457


2458
2459
2460
2461
2462
2463
2464
2465
2466
2532
2533
2534
2535
2536
2537
2538







2539
2540
2541
2542
2543
2544
2545
2546


2547
2548
2549
2550
2551

2552
2553
2554
2555
2556
2557

2558

2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574

2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589


2590
2591


2592
2593
2594
2595
2596
2597
2598







-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
+




-
+





-

-
+







+
+
+
+
+


+
-
+
+
+
+
+










-
-
+
+
-
-







								continue
							}
						}
					}
					if (qplet == 0 || qplet == undefined)
						qplet = (curvoice.wmeasure % 9) == 0 ?
									3 : 2;
					tp = tp_a[++tpn]
					if (!tp)
						tp_a[tpn] = tp = {}
					tp.p = pplet;
					tp.q = qplet;
					tp.r = rplet;
					tp.f = cfmt.tuplets;
					if (tpn < 0)
						tpn = tp.length	// new tuplet
					tp.push({
						p: pplet,
						q: qplet,
						r: rplet,
						ro: rplet,
						f: cfmt.tuplets
					tp.fact	= tp_fact * qplet / pplet;
					tp_fact = tp.fact
					})
					continue
				}
				if (c == '&') {		// voice overlay start
					if (grace) {
						syntax(1, errs.bad_char, c)
						syntax(1, errs.bad_grace)
						break
					}
					get_vover('(')
					break
				}
				slur_start <<= 4;
				line.index--;
				slur_start += parse_vpos()
				sls.push(parse_vpos())
				continue
			case ')':			// slur end
				if (curvoice.ignore)
					break
				s = curvoice.last_sym
				if (s) {
					switch (s.type) {
					case C.SPACE:
						if (!s.notes) {
							s.notes = []
							s.notes[0] = {}
						}
					case C.NOTE:
					case C.REST:
						break
					case C.SPACE:
					case C.GRACE:

						// stop the slur on the last grace note
						for (s = s.extra; s.next; s = s.next)
							;
						break
					default:
						s = null
						break
					}
				}
				if (!s) {
					syntax(1, errs.bad_char, c)
					break
				}
				if (s.slur_end)
					s.slur_end++
				s.notes[0].s = s
				slur_add(s.notes[0])
				else
					s.slur_end = 1
				break
			case '!':			// start of decoration
				if (!a_dcn)
					a_dcn = []
				if (type.length > 1) {	// decoration letter
					dcn = type.slice(1, -1)
				} else {
︙
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485




2486

2487
2488




2489
2490
2491

2492
2493
2494


2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523




2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540

2541
2542
2543
2544
2545
2546
2547

2548
2549


2550
2551
2552
2553
2554

2555
2556
2557
2558


2559
2560
2561
2562
2563




2564
2565
2566



2567
2568
2569
2570
2571
2572
2573
2574




2575
2576
2577
2578
2579

2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590

2591
2592
2593
2594
2595
2596



2597
2598
2599

2600
2601
2602
2603
2604
2605






2606




2607
2608
2609
2610
2611
2612
2613
2608
2609
2610
2611
2612
2613
2614



2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628

2629



2630
2631


2632
2633













2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665

2666
2667
2668





2669


2670
2671



2672

2673




2674
2675





2676
2677
2678
2679



2680
2681
2682








2683
2684
2685
2686





2687











2688






2689
2690
2691



2692






2693
2694
2695
2696
2697
2698

2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709







-
-
-
+
+
+
+

+


+
+
+
+


-
+
-
-
-
+
+
-
-


-
-
-
-
-
-
-
-
-
-
-
-
-












+
+
+
+
















-
+


-
-
-
-
-
+
-
-
+
+
-
-
-

-
+
-
-
-
-
+
+
-
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
+
+
+
-
-
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
+
+
+







					}
					if (!c) {
						line.index = i;
						syntax(1, "No end of decoration")
						break
					}
				}
				if (ottava[dcn] != undefined)
					parse.ottava = ottava[dcn]
				else
				if (ottava[dcn] != undefined) {
					glovar.ottava = true;
					parse.ottava.push(ottava[dcn])
				} else {
					a_dcn.push(dcn)
				}
				break
			case '"':
				if (grace) {
					syntax(1, errs.bad_grace)
					break
				}
				parse_gchord(type)
				break
			case '-':
			case '[':
			    var tie_pos = 0

				if (!curvoice.last_note
				if (type.length > 1) {	// U: [I:xxx]
					self.do_pscom(type.slice(3, -1))
				 || curvoice.last_note.type != C.NOTE) {
					syntax(1, "No note before '-'")
					break
				}
				tie_pos = parse_vpos();
				s = curvoice.last_note
				for (i = 0; i <= s.nhd; i++) {
					if (!s.notes[i].ti1)
						s.notes[i].ti1 = tie_pos
					else if (s.nhd == 0)
						syntax(1, "Too many ties")
				}
				s.ti1 = true
				if (grace)
					grace.ti1 = true
				continue
			case '[':
			    var c_next = line.buffer[line.index + 1]

				if ('|[]: "'.indexOf(c_next) >= 0
				 || (c_next >= '1' && c_next <= '9')) {
					if (grace) {
						syntax(1, errs.bar_grace)
						break
					}
					new_bar()
					continue
				}
				if (line.buffer[line.index + 2] == ':') {
					if (grace) {
						syntax(1, errs.bad_grace)
						break
					}
					i = line.buffer.indexOf(']', line.index + 1)
					if (i < 0) {
						syntax(1, "Lack of ']'")
						break
					}
					text = line.buffer.slice(line.index + 3, i).trim()

					parse.istart = parse.bol + line.index;
					parse.iend = parse.bol + ++i;
					line.index = 0;
					do_info(c_next, text);
					line.index = i
					continue
				}
				// fall thru ('[' is start of chord)
			case 'n':				// note/rest
				s = self.new_note(grace, tp_fact)
				s = self.new_note(grace, sls)
				if (!s)
					continue
				if (s.type == C.NOTE) {
					if (slur_start) {
						s.slur_start = slur_start;
						slur_start = 0
					}

				}
				if (grace) {
				// handle the tuplets
				if (grace || !s.notes)
//fixme: tuplets in grace notes?
					if (tpn >= 0)
						s.in_tuplet = true
					continue
				}


				// set the tuplet values
				if (tpn >= 0 && s.notes) {
					s.in_tuplet = true
				if (tpn >= 0) {		// new tuplet
					s.tp = tp.slice(tpn)
//fixme: only one nesting level
					if (tpn > 0) {
						if (tp_a[0].p) {
							s.tp0 = tp_a[0].p;
							s.tq0 = tp_a[0].q;
					tpn = -1
					if (tps)
						s.tp[0].s = tps	// if nested
					tps = s
							s.tf = tp_a[0].f;
							tp_a[0].p = 0
						}
				} else if (!tps) {
					continue	// no tuplet active
				}
						tp_a[0].r--
						if (tp.p) {
							s.tp1 = tp.p;
							s.tq1 = tp.q;
							s.tf = tp.f;
							tp.p = 0
						}
					} else if (tp.p) {

				k = tp[tp.length - 1]
				if (--k.r > 0)
					continue	// not end of tuplet yet
						s.tp0 = tp.p;
						s.tq0 = tp.q;
						s.tf = tp.f;	// %%tuplets
						tp.p = 0
					}

					tp.r--
					if (tp.r == 0) {
						if (tpn-- == 0) {
							if (s.tp0)	// if one note
								s.tp0 = false
							else
								s.te0 = true;
							tp_fact = 1;
							curvoice.time = Math.round(curvoice.time);
							s.dur = curvoice.time - s.time
						} else {
				while (1) {
							if (s.tp1)	// if one note
								s.tp1 = false
							else
								s.te1 = true;
							tp = tp_a[0]
							if (tp.r == 0) {
					tp_adj(tps, k.q / k.p)
					i = k.ro	// number of notes of this tuplet
					if (k.s)
								tpn--;
								s.te0 = true;
								tp_fact = 1;
						tps = k.s  // start of upper tuplet
								curvoice.time = Math.round(curvoice.time);
								s.dur = curvoice.time - s.time
							} else {
								tp_fact = tp.fact
							}
						}

					tp.pop()		// previous level
					if (!tp.length) {
						tps = null	// done
						break
					}
					}
					k = tp[tp.length - 1]
					k.r -= i
					if (k.r > 0)
						break
				}
				continue
			case '<':				/* '<' and '>' */
				if (!curvoice.last_note) {
					syntax(1, "No note before '<'")
					break
				}
︙
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690

2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708






2709
2710
2711
2712

2713
2714
2715
2716


2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734

2735
2736
2737
2738
2739
2762
2763
2764
2765
2766
2767
2768

2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784

2785

2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811

2812




2813
2814





2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831

2832







-
















-
+
-

















+
+
+
+
+
+



-
+
-
-
-
-
+
+
-
-
-
-
-













+



-

				s = curvoice.last_note
				if (!grace || !s) {
					syntax(1, errs.bad_char, c)
					break
				}
				if (a_dcn)
					syntax(1, "Decoration ignored");
				s.gr_end = true;
				grace.extra = grace.next;
				grace.extra.prev = null;
				grace.next = null;
				curvoice.last_sym = grace;
				grace = null
				if (!s.prev			// if one grace note
				 && !curvoice.ckey.k_bagpipe) {
					for (i = 0; i <= s.nhd; i++)
						s.notes[i].dur *= 2;
					s.dur *= 2;
					s.dur_orig *= 2
				}
				curvoice.last_note = last_note_sav;
				a_dcn = a_dcn_sav
				break
			case "\\":
				c = line.buffer[line.index + 1]
				if (!line.buffer[line.index + 1]) {
				if (!c) {
					no_eol = true
					break
				}
				// fall thru
			default:
				syntax(1, errs.bad_char, c)
				break
			}
			line.index++
		}
	} // parse_seq()

	if (parse.state != 3) {		// if not in tune body
		if (parse.state != 2)
			return
		goto_tune()
	}

	if (parse.tp) {
		tp = parse.tp
		tps = parse.tps
		parse.tp = null
	}

	parse_seq()

	if (tpn >= 0) {
	if (tp.length) {
		syntax(1, "No end of tuplet")
		for (s = curvoice.last_note; s; s = s.prev) {
			if (s.tp1)
				s.tp1 = 0
		parse.tp = tp
		parse.tps = tps
			if (s.tp0) {
				s.tp0 = 0
				break
			}
		}
	}
	if (grace) {
		syntax(1, "No end of grace note sequence");
		curvoice.last_sym = grace.prev;
		curvoice.last_note = last_note_sav
		if (grace.prev)
			grace.prev.next = null
	}
	if (cfmt.breakoneoln && curvoice.last_note)
		curvoice.last_note.beam_end = true
	if (no_eol || cfmt.barsperstaff)
		return
	if (char_tb['\n'.charCodeAt(0)] == '\n'
	 && par_sy.voices[curvoice.v]
	 && par_sy.voices[curvoice.v].range == 0
	 && curvoice.last_sym)
		curvoice.last_sym.eoln = true
//--fixme: cfmt.alignbars
}
Changes to core/subs.js.
︙
92
93
94
95
96
97
98










99
100
101
102
103
104
105
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115







+
+
+
+
+
+
+
+
+
+







		h = font.size,
		w = 0,
		n = str.length,
		i0 = 0,
		i = 0

		el.style.font = style_font(font).slice(5);

		str = str.replace(/<|>|&[^&]*?;|&|  /g, function(c){
			switch (c) {
			case '<': return "&lt;"
			case '>': return "&gt;"
			case '&': return "&amp;"
			case "  ": return '  '	// space + nbspace
			}
			return c		// &xxx;
		})

		while (1) {
			i = str.indexOf('$', i)
			if (i < 0)
				break
			c = str[i + 1]
			if (c == '0') {
︙
178
179
180
181
182
183
184
185
186
187













188
189

190
191
192

193
194
195
196
197
198
199
200
201
202


203
204
205
206
207
208
209
188
189
190
191
192
193
194



195
196
197
198
199
200
201
202
203
204
205
206
207


208
209
210

211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230







-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+


-
+










+
+








// output a string handling the font changes
function out_str(str) {
	var	n_font,
		o_font = gene.curfont,
		c_font = o_font;

	output += str.replace(/\$./g, function(c) {
				if (c[1] == '0') {
					n_font = gene.deffont;
	output += str.replace(/<|>|&[^&]*?;|&|  |\$./g, function(c){
			switch (c) {
			case '<': return "&lt;"
			case '>': return "&gt;"
			case '&':
				 return "&amp;"
			case '  ':
				return '  '		// space + nbspace
			default:
				if (c[0] != '$')
					break
				if (c[1] == '0')
					n_font = gene.deffont
					use_font(n_font)
				} else if (c[1] >= '1' && c[1] <= '9')
				else if (c[1] >= '1' && c[1] <= '9')
					n_font = get_font("u" + c[1])
				else
					return c
					break
				c = ''
				if (n_font == c_font)
					return c
				if (c_font != o_font)
					c = "</tspan>";
				c_font = n_font
				if (c_font == o_font)
					return c
				return c + '<tspan\n\tclass="' +
						font_class(n_font) + '">'
			}
			return c		// &xxx;
		})
	if (c_font != o_font) {
		output += "</tspan>";
		gene.curfont = c_font	// keep current font for next paragraph
	}
}

︙
744
745
746
747
748
749
750
751

752
753
754


755
756
757
758
759
760

761
762
763
764
765
766
767
765
766
767
768
769
770
771

772
773
774

775
776
777
778
779
780
781

782
783
784
785
786
787
788
789







-
+


-
+
+





-
+







			if (info_sz[c])
				sz += info_sz[c];
			set_font(font);
			x = xa[align];
			y = ya[align] + sz

			if (c == 'Q') {			/* special case for tempo */
				set_width(glovar.tempo)
				self.set_width(glovar.tempo)
				if (!glovar.tempo.del) {
					if (align != 'l') {
						var w = tempo_width(glovar.tempo)
						tempo_build(glovar.tempo)
						w = glovar.tempo.tempo_wh[0]

						if (align == 'c')
							w *= .5;
						x -= w
					}
					write_tempo(glovar.tempo, x, -y)
					writempo(glovar.tempo, x, -y)
				}
			} else if (str) {
				xy_str(x, -y, str, align)
			}

			if (c == 'T') {
				font_name = info_font.T = "subtitle";
︙
Changes to core/svg.js.
︙
14
15
16
17
18
19
20
21


22
23
24
25
26
27






28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
14
15
16
17
18
19
20

21
22
23





24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

46
47
48
49
50
51
52
53







-
+
+

-
-
-
-
-
+
+
+
+
+
+
















-
+







// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

var	output = "",		// output buffer
	style = '\n.fill{fill:currentColor}\
	style = '\
\ntext, tspan{fill:currentColor}\
\n.stroke{stroke:currentColor;fill:none}\
\n.bW{stroke-width:1}\
\n.bthW{stroke-width:3}\
\n.slW{stroke-width:.7}\
\n.slthW{stroke-width:1.5}\
\n.sW{stroke-width:.7}',
\n.bW{stroke:currentColor;fill:none;stroke-width:1}\
\n.bthW{stroke:currentColor;fill:none;stroke-width:3}\
\n.slW{stroke:currentColor;fill:none;stroke-width:.7}\
\n.slthW{stroke:currentColor;fill:none;stroke-width:1.5}\
\n.sW{stroke:currentColor;fill:none;stroke-width:.7}\
\n.mtx{font:130% music}',
	font_style = '',
	posx = cfmt.leftmargin / cfmt.scale,	// default x offset of the images
	posy = 0,		// y offset in the block
	img = {			// image
		width: cfmt.pagewidth,	// width
		lm: cfmt.leftmargin,	// left and right margins
		rm: cfmt.rightmargin
//		chg: false
	},
	defined_glyph = {},
	defs = '',
	fulldefs = '',		// unreferenced defs as <filter>
	stv_g = {		/* staff/voice graphic parameters */
		scale: 1,
		dy: 0,
		st: -1,
		v: 0,
		v: -1,
		g: 0
//		color: undefined
	},
	block = {}		/* started & newpage */

// glyphs in music font
var tgls = {
︙
94
95
96
97
98
99
100

101
102
103
104
105
106
107
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110







+







  hd: {x:-3.7, y:0, c:"\ue0a4"},
  ghd: {x:2, y:0, c:"\ue0a4", sc:.66},	// grace note head
  pshhd: {x:-3.7, y:0, c:"\ue0a9"},
  pfthd: {x:-3.7, y:0, c:"\ue0b3"},
  x: {x:-3.7, y:0, c:"\ue0a9"},		// 'x' note head
  "circle-x": {x:-3.7, y:0, c:"\ue0b3"}, // 'circle-x' note head
  srep: {x:-5, y:0, c:"\ue101"},
  "dot+": {x:-5, y:0, sc:.7, c:"\ue101"},
  diamond: {x:-4, y:0, c:"\ue1b9"},
  triangle: {x:-4, y:0, c:"\ue1bb"},
  dot: {x:-2, y:0, c:"\ue1e7"},
  flu1: {x:-.3, y:0, c:"\ue240"},	// flags
  fld1: {x:-.3, y:0, c:"\ue241"},
  flu2: {x:-.3, y:0, c:"\ue242"},
  fld2: {x:-.3, y:0, c:"\ue243"},
︙
153
154
155
156
157
158
159
160

161
162
163
164
165
166
167
156
157
158
159
160
161
162

163
164
165
166
167
168
169
170







-
+







  sfz: {x:-4, y:-6, c:"\ue539"},
  trl: {x:-4, y:-4, c:"\ue566"},	// trill
  turn: {x:-5, y:-4, c:"\ue567"},
  turnx: {x:-5, y:-4, c:"\ue569"},
  umrd: {x:-7, y:-2, c:"\ue56c"},
  lmrd: {x:-7, y:-2, c:"\ue56d"},
  dplus: {x:-4, y:10, c:"\ue582"},	// plus
  sld: {x:-8, y:12, c:"\ue5d4"},	// slide
  sld: {x:-8, y:12, c:"\ue5d0"},	// slide
  grm: {x:-2, y:0, c:"\ue5e2"},		// grace mark
  dnb: {x:-4, y:0, c:"\ue610"},		// down bow
  upb: {x:-3, y:0, c:"\ue612"},		// up bow
  opend: {x:-2, y:0, c:"\ue614"},	// harmonic
  roll: {x:0, y:0, c:"\ue618"},
  thumb: {x:0, y:0, c:"\ue624"},
  snap: {x:-2, y:0, c:"\ue630"},
︙
185
186
187
188
189
190
191

192

193
194
195
196




197
198
199
200
201
202
203
188
189
190
191
192
193
194
195

196
197



198
199
200
201
202
203
204
205
206
207
208







+
-
+

-
-
-
+
+
+
+







}

// convert a meter string to a SmuFL encoded string
function m_gl(s) {
	return s.replace(/[Cco]\||[co]\.|./g,
		function(e) {
		    var	m = tgls["mtr" + e]
//fixme: !! no m.x nor m.y yet !!
			if (!m.x && !m.y)
//			if (!m.x && !m.y)
				return m.c
			return '<tspan dx="'+ m.x.toFixed(1) +
				'" dy="' + m.y.toFixed(1) + '">' +
				m.c + '</tspan>'
//			return '<tspan dx="'+ m.x.toFixed(1) +
//				'" dy="' + m.y.toFixed(1) +
//				'">' +
//				m.c + '</tspan>'
		})
}

// mark a glyph as used and add it in <defs>
function def_use(gl) {
	var	i, j, g

︙
276
277
278
279
280
281
282
283



284
285
286



287
288
289
290

291

292
293
294
295
296
297
298
281
282
283
284
285
286
287

288
289
290
291
292

293
294
295
296
297
298
299
300

301
302
303
304
305
306
307
308







-
+
+
+


-
+
+
+




+
-
+







	// check if new sequence needed
	if (stv_g.scale == 1 && !stv_g.color)
		return

	// open the new sequence
	output += '<g '
	if (stv_g.scale != 1) {
		if (stv_g.st >= 0)
		if (stv_g.st < 0)
			output += voice_tb[stv_g.v].scale_str
		else if (stv_g.v < 0)
			output += staff_tb[stv_g.st].scale_str
		else
			output += voice_tb[stv_g.v].scale_str
			output += 'transform="translate(0,' +
					(posy - stv_g.dy).toFixed(1) +
				') scale(' + stv_g.scale.toFixed(2) + ')"'
	}
	if (stv_g.color) {
		if (stv_g.scale != 1)
			output += ' ';
		output += 'color="' + stv_g.color +
		output += 'style="color:' + stv_g.color + '"'
			'" fill="' + stv_g.color + '"'
	}
	output += ">\n";
	stv_g.started = true
}

/* set the color */
function set_color(color) {
︙
316
317
318
319
320
321
322

323
324
325
326
327

328

329
330
331
332
333



334


335
336
337
338
339


340
341
342
343
344
345
346
326
327
328
329
330
331
332
333
334
335
336
337
338
339

340
341
342
343
344
345
346
347
348

349
350
351
352
353


354
355
356
357
358
359
360
361
362







+





+
-
+





+
+
+
-
+
+



-
-
+
+







	else
		dy = posy
	if (new_scale == stv_g.scale && dy == stv_g.dy)
		return
	stv_g.scale = new_scale;
	stv_g.dy = dy;
	stv_g.st = st;
	stv_g.v = -1;
	set_g()
}

/* -- set the voice or staff scale -- */
function set_scale(s) {
    var	new_dy,
	var	new_scale = s.p_v.scale
	new_scale = s.p_v.scale

	if (new_scale == 1) {
		set_sscale(s.st)
		return
	}
	new_dy = posy
	if (staff_tb[s.st].staffscale != 1) {
		new_scale *= staff_tb[s.st].staffscale;
/*fixme: KO when both staff and voice are scaled */
		new_dy = staff_tb[s.st].y
	}
	if (new_scale == stv_g.scale && stv_g.dy == posy)
		return
	stv_g.scale = new_scale;
	stv_g.dy = posy;
	stv_g.st = -1;
	stv_g.dy = new_dy;
	stv_g.st = staff_tb[s.st].staffscale == 1 ? -1 : s.st;
	stv_g.v = s.v;
	set_g()
}

// -- set the staff output buffer and scale when delayed output
function set_dscale(st, no_scale) {
	if (output) {
︙
363
364
365
366
367
368
369
370
371

372
373
374
375

376
377
378
379
380
381
382
383
379
380
381
382
383
384
385


386




387

388
389
390
391
392
393
394







-
-
+
-
-
-
-
+
-








// update the y offsets of delayed output
function delayed_update() {
	var st, new_out, text

	for (st = 0; st <= nstaff; st++) {
		if (staff_tb[st].sc_out) {
			output += '<g transform="translate(0,' +
					(posy - staff_tb[st].y).toFixed(1) +
			output += '<g ' + staff_tb[st].scale_str + '>\n' +
					') scale(' +
					 staff_tb[st].staffscale.toFixed(2) +
					')">\n' +
				staff_tb[st].sc_out +
				staff_tb[st].sc_out + '</g>\n';
				'</g>\n';
			staff_tb[st].sc_out = ""
		}
		if (!staff_tb[st].output)
			continue
		output += '<g transform="translate(0,' +
				(-staff_tb[st].y).toFixed(1) +
				')">\n' +
︙
473
474
475
476
477
478
479
480
481
482



483
484
485
486
487
488
489
484
485
486
487
488
489
490



491
492
493
494
495
496
497
498
499
500







-
-
-
+
+
+







}
Abc.prototype.sx = sx
function sy(y) {
	if (stv_g.g)
		return -y
	if (stv_g.scale == 1)
		return posy - y
	if (stv_g.st < 0)
		return (posy - y) / stv_g.scale	// voice scale
	return stv_g.dy - y			// staff scale
	if (stv_g.v >= 0)
		return (stv_g.dy - y) / voice_tb[stv_g.v].scale
	return stv_g.dy - y	// staff scale only
}
Abc.prototype.sy = sy;
Abc.prototype.sh = function(h) {
	if (stv_g.st < 0)
		return h / stv_g.scale
	return h
}
︙
505
506
507
508
509
510
511

512



513
514
515
516
517
518
519
520




521
522
523
524
525
526
527
528
529









530
531

532
533

534
535

536
537

538
539
540
541
542
543
544
516
517
518
519
520
521
522
523

524
525
526
527
528
529
530
531
532
533
534
535
536
537
538









539
540
541
542
543
544
545
546
547


548


549


550


551
552
553
554
555
556
557
558







+
-
+
+
+








+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+
-
-
+
-
-
+







	y = sy(y);
	output += x.toFixed(1) + sep + y.toFixed(1)
}
Abc.prototype.out_sxsy = out_sxsy

// define the start of a path
function xypath(x, y, fill) {
	if (fill)
	out_XYAB('<path class="A" d="mX Y\n', x, y, fill ? "fill" : "stroke")
		out_XYAB('<path d="mX Y', x, y)
	else
		out_XYAB('<path class="stroke" d="mX Y', x, y)
}
Abc.prototype.xypath = xypath

// output a glyph
function xygl(x, y, gl) {
// (avoid ps<->js loop)
//	if (psxygl(x, y, gl))
//		return
	if (glyphs[gl]) {
		def_use(gl)
		out_XYAB('<use x="X" y="Y" xlink:href="#A"/>\n', x, y, gl)
	} else {
	var 	tgl = tgls[gl]
	if (tgl && !glyphs[gl]) {
		x += tgl.x * stv_g.scale;
		y -= tgl.y
		if (tgl.sc)
			out_XYAB('<text transform="translate(X,Y) scale(A)">B</text>\n',
				x, y, tgl.sc, tgl.c);
		else
			out_XYAB('<text x="X" y="Y">A</text>\n', x, y, tgl.c)
	    var	tgl = tgls[gl]
		if (tgl) {
			x += tgl.x * stv_g.scale;
			y -= tgl.y
			if (tgl.sc)
				out_XYAB('<text transform="translate(X,Y) scale(A)">B</text>\n',
					x, y, tgl.sc, tgl.c);
			else
				out_XYAB('<text x="X" y="Y">A</text>\n', x, y, tgl.c)
		return
	}
		} else {
	if (!glyphs[gl]) {
		error(1, null, 'no definition of $1', gl)
			error(1, null, 'no definition of $1', gl)
		return
	}
		}
	def_use(gl);
	out_XYAB('<use x="X" y="Y" xlink:href="#A"/>\n', x, y, gl)
	}
}
// - specific functions -
// gua gda (acciaccatura)
function out_acciac(x, y, dx, dy, up) {
	if (up) {
		x -= 1;
		y += 4
︙
568
569
570
571
572
573
574
575
576

577
578
579
580
581
582
583
582
583
584
585
586
587
588


589
590
591
592
593
594
595
596







-
-
+







}

// staff system bracket
function out_bracket(x, y, h) {
	x += posx - 5;
	y = posy - y - 3;
	h += 2;
	output += '<path class="fill"\n\
	d="m' + x.toFixed(1) + ' ' + y.toFixed(1) + '\n\
	output += '<path d="m' + x.toFixed(1) + ' ' + y.toFixed(1) + '\n\
	c10.5 1 12 -4.5 12 -3.5c0 1 -3.5 5.5 -8.5 5.5\n\
	v' + h.toFixed(1) + '\n\
	c5 0 8.5 4.5 8.5 5.5c0 1 -1.5 -4.5 -12 -3.5"/>\n'
}
// hyphen
function out_hyph(x, y, w) {
	var	n, a_y,
︙
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610



611
612
613
614
615
616
617
618
619
620
621
622

623
624
625
626
627
628
629
630
631
632
633
634
635

636
637
638
639
640
641
642
604
605
606
607
608
609
610

611
612
613
614
615
616
617
618
619



620
621
622
623
624
625
626
627
628
629
630
631
632
633

634
635
636
637
638
639
640
641
642
643
644
645
646

647
648
649
650
651
652
653
654







-









-
-
-
+
+
+











-
+












-
+







	out_XYAB('<path class="stroke" stroke-width="1.2"\n\
	stroke-dasharray="5,A"\n\
	d="mX YhB"/>\n',
		x, y + 6,		// set the line a bit upper
		Math.round((d - 5) / stv_g.scale), d * n + 5)
}
// stem [and flags]
// fixme: h is already scaled - change that?
function out_stem(x, y, h, grace,
		  nflags, straight) {	// optional
//fixme: dx KO with half note or longa
	var	dx = grace ? GSTEM_XOFF : 3.5,
		slen = -h

	if (h < 0)
		dx = -dx;		// down
	x += dx * stv_g.scale
	if (stv_g.st < 0)
		slen /= stv_g.scale;
	out_XYAB('<path class="stroke sW" d="mX YvF"/>\n',	// stem
	if (stv_g.v >= 0)
		slen /= voice_tb[stv_g.v].scale;
	out_XYAB('<path class="sW" d="mX YvF"/>\n',	// stem
		x, y, slen)
	if (!nflags)
		return

	y += h
	if (h > 0) {				// up
		if (!straight) {
			if (!grace) {
				xygl(x, y, "flu" + nflags)
				return
			} else {		// grace
				output += '<path class="fill" d="'
				output += '<path d="'
				if (nflags == 1) {
					out_XYAB('MX Yc0.6 3.4 5.6 3.8 3 10\n\
	1.2 -4.4 -1.4 -7 -3 -7\n', x, y)
				} else {
					while (--nflags >= 0) {
						out_XYAB('MX Yc1 3.2 5.6 2.8 3.2 8\n\
	1.4 -4.8 -2.4 -5.4 -3.2 -5.2\n', x, y);
						y -= 3.5
					}
				}
			}
		} else {			// straight
			output += '<path class="fill" d="'
			output += '<path d="'
//fixme: to do
			if (!grace) {
//fixme: check endpoints
				y += 1
				while (--nflags >= 0) {
					out_XYAB('MX Yl7 3.2 0 3.2 -7 -3.2z\n',
						x, y);
︙
652
653
654
655
656
657
658
659

660
661
662
663
664
665
666
667
668
669
670
671
672

673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690

691
692
693
694
695
696
697
664
665
666
667
668
669
670

671
672
673
674
675
676
677
678
679
680
681
682
683

684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701

702
703
704
705
706
707
708
709







-
+












-
+

















-
+







		}
	} else {				// down
		if (!straight) {
			if (!grace) {
				xygl(x, y, "fld" + nflags)
				return
			} else {		// grace
				output += '<path class="fill" d="'
				output += '<path d="'
				if (nflags == 1) {
					out_XYAB('MX Yc0.6 -3.4 5.6 -3.8 3 -10\n\
	1.2 4.4 -1.4 7 -3 7\n', x, y)
				} else {
					while (--nflags >= 0) {
						out_XYAB('MX Yc1 -3.2 5.6 -2.8 3.2 -8\n\
	1.4 4.8 -2.4 5.4 -3.2 5.2\n', x, y);
						y += 3.5
					}
				}
			}
		} else {			// straight
			output += '<path class="fill" d="'
			output += '<path d="'
			if (!grace) {
//fixme: check endpoints
				y += 1
				while (--nflags >= 0) {
					out_XYAB('MX Yl7 -3.2 0 -3.2 -7 3.2z\n',
						x, y);
					y += 5.4
				}
//			} else {		// grace
//--fixme: error?
			}
		}
	}
	output += '"/>\n'
}
// tremolo
function out_trem(x, y, ntrem) {
	out_XYAB('<path class="fill" d="mX Y\n\t', x - 4.5, y)
	out_XYAB('<path d="mX Y\n\t', x - 4.5, y)
	while (1) {
		output += 'l9 -3v3l-9 3z'
		if (--ntrem <= 0)
			break
		output += 'm0 5.4'
	}
	output += '"/>\n'
︙
827
828
829
830
831
832
833
834

835
836
837
838
839
840
841
839
840
841
842
843
844
845

846
847
848
849
850
851
852
853







-
+







	}
}
function out_lped(x, y, val, defl) {
	y += 4;
	if (!defl.nost)
		xygl(x, y, "ped");
	if (!defl.noen)
		xygl(x + val, y, "pedoff")
		xygl(x + val + 6, y, "pedoff")
}
function out_8va(x, y, val, defl) {
	if (!defl.nost) {
		out_XYAB('<text x="X" y="Y" \
style="font:italic bold 12px serif">8\
<tspan dy="-4" style="font-size:10px">va</tspan></text>\n',
			x - 8, y);
︙
965
966
967
968
969
970
971

























































































































972
973
974
975
976
977
978
979
980
981
982
983
984
985

986
987






988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007

1008
1009
1010
1011
1012
1013

1014
1015
1016
1017
1018
1019
1020
1021
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117

1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145

1146

1147
1148
1149
1150

1151

1152
1153
1154
1155
1156
1157
1158







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+













-
+


+
+
+
+
+
+



















-
+
-




-
+
-







	var	name = de.dd.glyph

	if (deco_l_tb[name])
		deco_l_tb[name](x, y, de)
	else
		error(1, null, "No function for decoration '$1'", name)
}

// return a tempo note
function tempo_note(s, dur) {
    var	p,
	elts = identify_note(s, dur)

	switch (elts[0]) {		// head
	case C.OVAL:
		p = "\ueca2"
		break
	case C.EMPTY:
		p = "\ueca3"
		break
	default:
		switch (elts[2]) {	// flags
		case 2:
			p = "\ueca9"
			break
		case 1:
			p = "\ueca7"
			break
		default:
			p = "\ueca5"
			break
		}
		break
	}
	if (elts[1])
		p += '<tspan dx=".1em">\uecb7</tspan>'
	return p
} // tempo_note()

// build the tempo string
function tempo_build(s) {
    var	i, j, bx, p, wh, dy,
	w = 0,
	str = []

	if (s.tempo_str)	// already done
		return
	set_font("tempo")
	if (s.tempo_str1) {
		str.push(s.tempo_str1)
		w += strwh(s.tempo_str1)[0]
	}
	if (s.tempo_notes) {
		dy = ' dy="-.05em"'			// notes a bit higher
		for (i = 0; i < s.tempo_notes.length; i++) {
			p = tempo_note(s, s.tempo_notes[i])
			str.push('<tspan\n\tclass="mtx"' + dy + '>' +
				p + '</tspan>')
			j = p.length > 1 ? 2 : 1	// (note and optional dot)
			w += j * gene.curfont.swfac
			dy = ''
		}
		str.push('<tspan dy=".065em">=</tspan>')
		w += cwidf('=')
		if (s.tempo_ca) {
			str.push(s.tempo_ca)
			w += strwh(s.tempo_ca)[0]
			j = s.tempo_ca.length + 1
		}
		if (s.tempo) {			// with a number of beats per minute
			str.push(s.tempo)
			w += strwh(s.tempo.toString())[0]
		} else {			// with a beat as a note
			p = tempo_note(s, s.new_beat)
			str.push('<tspan\n\tclass="mtx" dy="-.05em">' +
				p + '</tspan>')
			j = p.length > 1 ? 2 : 1
			w += j * gene.curfont.swfac
			dy = 'y'
		}
	}
	if (s.tempo_str2) {
		if (dy)
			str.push('<tspan\n\tdy=".065em">' +
					s.tempo_str2 + '</tspan>')
		else
			str.push(s.tempo_str2)
		w += strwh(s.tempo_str2)[0]
	}

	// build the string
	s.tempo_str = str.join(' ')
	w += cwidf(' ') * (str.length - 1)
	s.tempo_wh = [w, 13.0]		// (the height is not used)
	if (dy)
		s.tempo_dy = dy
} // tempo_build()

// output a tempo
function writempo(s, x, y) {
    var	bx

	set_font("tempo")
	if (gene.curfont.box) {
		gene.curfont.box = false
		bx = x
	}

//fixme: xy_str() cannot be used because <tspan> in s.tempo_str
//fixme: then there cannot be font changes by "$n" in the Q: texts
	output += '<text class="' + font_class(gene.curfont) +
		'" x="'
	out_sxsy(x, '" y="', y + gene.curfont.size * .2)
	output += '">' + s.tempo_str + '</text>\n'

	if (bx) {
		gene.curfont.box = true
		bh = gene.curfont.size + 4;
		output += '<rect class="stroke" x="'
		out_sxsy(bx - 2, '" y="', y + bh - 1)
		output += '" width="' + (s.tempo_wh[0] + 2).toFixed(1) +
			'" height="' + bh.toFixed(1) +
			'"/>\n'
	}

	// don't display anymore
	s.del = true
} // writempo()

// update the vertical offset
function vskip(h) {
	posy += h
}

// create the SVG image of the block
function svg_flush() {
	if (multicol || !output || !user.img_out || posy == 0)
		return

    var	head = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	color="black" class="music" stroke-width=".7"',
	color="',
	g = ''

	if (cfmt.fgcolor)
		head += cfmt.fgcolor + '" fill="' + cfmt.fgcolor + '"'
	else
		head += 'black"';
	head += ' stroke-width=".7"'

	if (cfmt.bgcolor)
		head += ' style="background-color: ' + cfmt.bgcolor + '"';

	posy *= cfmt.scale

	if (user.imagesize) {
		head += '\n' +
			user.imagesize +
			' viewBox="0 0 ' + img.width.toFixed(0) + ' ' +
			 posy.toFixed(0) + '">\n'
	} else {
		head += '\n\twidth="' + img.width.toFixed(0) +
			'px" height="' + posy.toFixed(0) + 'px">\n'
	}

	if (style || font_style || musicfont) {
		head += '<style type="text/css">' + style + font_style
		if (musicfont) {
			if (musicfont.indexOf('(') > 0) {
				head += '\n\
				head += '\nsvg{font:24px music}\n\
.music {font:24px music;fill:currentColor}\n\
@font-face {\n\
  font-family:"music";\n\
  src:' + musicfont + '}';
			} else {
				head += '\n\
				head += '\nsvg{font:24px '+ musicfont +'}'
.music {font:24px '+ musicfont +';fill:currentColor}'
			}
		}
		head += '\n</style>\n'
	}
	defs += fulldefs
	if (defs)
		head += '<defs>' + defs + '\n</defs>\n'
︙
Changes to core/tail.js.
︙
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
13
14
15
16
17
18
19




20
21
22
23
24

25
26
27



28

29
30

31
32
33
34
35
36
37

38
39
40
41
42
43
44







-
-
-
-





-



-
-
-

-


-







-







// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

// PostScript hooks
function psdeco() { return false }
function psxygl() { return false }

// initialize
	init_tune()

// Abc functions used by the modules
Abc.prototype.add_style = function(s) { style += s };
Abc.prototype.calculate_beam = calculate_beam;
Abc.prototype.cfmt = function() { return cfmt };
Abc.prototype.clone = clone;
Abc.prototype.deco_cnv = deco_cnv;
Abc.prototype.do_pscom = do_pscom;
Abc.prototype.do_begin_end = do_begin_end;
Abc.prototype.draw_gchord = draw_gchord;
Abc.prototype.draw_note = draw_note;
Abc.prototype.draw_symbols = draw_symbols;
Abc.prototype.errs = errs;
Abc.prototype.font_class = font_class;
Abc.prototype.gch_build = gch_build;
Abc.prototype.gch_tr1 = gch_tr1;
Abc.prototype.get_a_gch = function() { return a_gch };
Abc.prototype.get_bool = get_bool;
Abc.prototype.get_cur_sy = function() { return cur_sy };
Abc.prototype.get_curvoice = function() { return curvoice };
Abc.prototype.get_delta_tb = function() { return delta_tb };
Abc.prototype.get_decos = function() { return decos };
Abc.prototype.get_fname = function() { return parse.fname };
Abc.prototype.get_font = get_font;
Abc.prototype.get_font_style = function() { return font_style };
Abc.prototype.get_glyphs = function() { return glyphs };
Abc.prototype.get_img = function() { return img };
Abc.prototype.get_maps = function() { return maps };
Abc.prototype.get_multi = function() { return multicol };
Abc.prototype.get_newpage = function() {
︙
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

99
100
101
102
103
104
105
52
53
54
55
56
57
58

59
60
61
62

63
64


65
66

67
68
69


70



71

72


73
74
75
76
77
78
79
80
81
82
83
84







-




-


-
-
+
+
-



-
-

-
-
-

-

-
-




+







Abc.prototype.get_top_v = function() { return par_sy.top_voice };
Abc.prototype.get_tsfirst = function() { return tsfirst };
Abc.prototype.get_unit = get_unit;
Abc.prototype.get_voice_tb = function() { return voice_tb };
Abc.prototype.goto_tune = goto_tune;
Abc.prototype.info = function() { return info };
Abc.prototype.new_block = new_block;
Abc.prototype.new_note = new_note;
Abc.prototype.out_arp = out_arp;
Abc.prototype.out_deco_str = out_deco_str;
Abc.prototype.out_deco_val = out_deco_val;
Abc.prototype.out_ltr = out_ltr;
Abc.prototype.output_music = output_music;
Abc.prototype.param_set_font = param_set_font;
Abc.prototype.parse = parse;
Abc.prototype.psdeco = psdeco;
Abc.prototype.psxygl = psxygl;
Abc.prototype.psdeco = empty_function;
Abc.prototype.psxygl = empty_function;
Abc.prototype.set_bar_num = set_bar_num;
Abc.prototype.set_cur_sy = function(sy) { cur_sy = sy };
Abc.prototype.set_dscale = set_dscale;
Abc.prototype.set_font = set_font;
Abc.prototype.set_format = set_format;
Abc.prototype.set_pitch = set_pitch;
Abc.prototype.set_scale = set_scale;
Abc.prototype.set_stem_dir = set_stem_dir;
Abc.prototype.set_stems = set_stems;
Abc.prototype.set_sym_glue = set_sym_glue;
Abc.prototype.set_tsfirst = function(s) { tsfirst = s };
Abc.prototype.set_vp = set_vp;
Abc.prototype.set_v_param = set_v_param;
Abc.prototype.set_width = set_width;
Abc.prototype.sort_pitch = sort_pitch;
Abc.prototype.strwh = strwh;
Abc.prototype.stv_g = function() { return stv_g };
Abc.prototype.svg_flush = svg_flush;
Abc.prototype.syntax = syntax;
Abc.prototype.tunes = tunes
Abc.prototype.unlksym = unlksym;
Abc.prototype.use_font = use_font;
Abc.prototype.xy_str = xy_str;
Abc.prototype.xygl = xygl;

    var	hook_init		// set after setting the first module hooks

︙
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
105
106
107
108
109
110
111


112
113
114
115
116
117
118
119







-
-








		}
		set_hs(gh);
		hook_init = true
	}
    } // set_hooks()
}	// end of Abc()

} // end of abc2svg

// compatibility
var Abc = abc2svg.Abc

// nodejs
if (typeof module == 'object' && typeof exports == 'object') {
	exports.abc2svg = abc2svg;
	exports.Abc = Abc
}
Changes to core/tune.js.
︙
23
24
25
26
27
28
29
30

31
32

33
34

35

36
37




38
39
40
41
42














43
44
45
46
47
48
49
23
24
25
26
27
28
29

30
31

32


33
34
35
36
37
38
39
40
41





42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62







-
+

-
+
-
-
+

+


+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+







	curvoice,
	staves_found,
	vover,		// voice overlay
	tsfirst

/* apply the %%voice options of the current voice */
function voice_filter() {
	var opt, sel, i
    var	opt

	for (opt in parse.voice_opts) {
	function vfilt(opts, opt) {
		if (!parse.voice_opts.hasOwnProperty(opt))
			continue
	    var	i,
		sel = new RegExp(opt)

		if (sel.test(curvoice.id)
		 || sel.test(curvoice.nm)) {
			for (i = 0; i < opts.length; i++)
				self.do_pscom(opts[i])
		}
	}
			for (i in parse.voice_opts[opt])
			    if (parse.voice_opts[opt].hasOwnProperty(i))
				self.do_pscom(parse.voice_opts[opt][i])
		}
	}

	// global
	if (parse.voice_opts)
	    for (opt in parse.voice_opts) {
		if (parse.voice_opts.hasOwnProperty(opt))
			vfilt(parse.voice_opts[opt], opt)
	}

	// tune
	if (parse.tune_v_opts)
	    for (opt in parse.tune_v_opts) {
		if (parse.tune_v_opts.hasOwnProperty(opt))
			vfilt(parse.tune_v_opts[opt], opt)
	}
}

/* -- link a ABC symbol into the current voice -- */
function sym_link(s) {
	if (!s.fname)
		set_ref(s)
	if (!curvoice.ignore) {
︙
125
126
127
128
129
130
131
132

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158







159
160
161
162




163
164
165















166

167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
138
139
140
141
142
143
144

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165






166
167
168
169
170
171
172
173
174
175
176
177
178
179
180



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204

205



206

207


208
209
210
211
212
213
214







-
+




















-
-
-
-
-
-
+
+
+
+
+
+
+




+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+







-
+
-
-
-

-

-
-







	/* copy the mrest decorations to the last rest */
	s2.a_dd = a_dd
}

/* -- sort all symbols by time and vertical sequence -- */
// weight of the symbols !! depends on the symbol type !!
var w_tb = new Uint8Array([
	2,	// bar
	4,	// bar
	1,	// clef
	8,	// custos
	0,	// (free)
	3,	// grace
	5,	// key
	6,	// meter
	9,	// mrest
	9,	// note
	0,	// part
	9,	// rest
	3,	// space
	0,	// staves
	7,	// stbrk
	0,	// tempo
	0,	// (free)
	0,	// block
	0	// remark
])

function sort_all() {
	var	s, s2, p_voice, v, time, w, wmin, ir, multi,
		prev, nb, ir2, v2, sy,
		nv = voice_tb.length,
		vtb = [],
		vn = [],			/* voice indexed by range */
		mrest_time = -1
    var	s, s2, p_voice, v, time, w, wmin, ir, multi,
	prev, nb, ir2, v2, fl, new_sy,
	nv = voice_tb.length,
	vtb = [],
	vn = [],			// voice indexed by range
	mrest_time = -1,
	sy = cur_sy

	for (v = 0; v < nv; v++)
		vtb.push(voice_tb[v].sym)

	// set the first symbol
	ir2 = nv
	multi = -1
	for (v = 0; v < nv; v++) {
	/* initialize the voice order */
	var	fl = 1,				// start a new time sequence
		new_sy = cur_sy
		if (!sy.voices[v])
			continue
		ir = sy.voices[v].range
		if (ir < ir2)
			ir2 = ir
		vn[ir] = v
		multi++
	}
	v = vn[ir2]
	tsfirst = prev = vtb[v]
	if (!tsfirst)
		return				// no music
	vtb[v] = tsfirst.next
	prev.seqst = true
	fl = !w_tb[prev.type] || tsfirst.type == tsfirst.next

	// loop on the symbols of all voices
	while (1) {
		if (new_sy && fl) {
			sy = new_sy;
			new_sy = null;
			multi = -1;
			vn = []
			for (v = 0; v < nv; v++) {
				if (!sy.voices[v]) {
				if (!sy.voices[v])
					sy.voices[v] = {
						range: -1
					}
					continue
				}
				ir = sy.voices[v].range
				if (ir < 0)
					continue
				vn[ir] = v;
				multi++
			}
		}

		/* search the min time and symbol weight */
		wmin = time = 1000000				/* big int */
︙
261
262
263
264
265
266
267
268

269
270
271
272

273
274
275
276
277
278
279
280
281
282
283

284
285
286
287
288
289
290
291
292
293
294
295

296
297
298
299
300
301
302
286
287
288
289
290
291
292

293




294
295
296
297
298
299
300
301
302
303


304


305
306
307
308
309
310
311
312
313

314
315
316
317
318
319
320
321







-
+
-
-
-
-
+









-
-
+
-
-









-
+







				// set all voices of previous and next staff systems
				// as reachable
				for (ir2 = 0; ir2 < nv; ir2++) {
					if (vn[ir2] == undefined)
						break
				}
				for (v2 = 0; v2 < nv; v2++) {
					if (!new_sy.voices[v2])
					if (!new_sy.voices[v2]
						continue
					ir = new_sy.voices[v2].range
					if (ir < 0
					 || sy.voices[v2].range >= 0)
					 || sy.voices[v2])
						continue
					vn[ir2++] = v2
				}
			}
			if (fl) {
				fl = 0;
				s.seqst = true
			}
			s.ts_prev = prev
			if (prev)
				prev.ts_next = s
			prev.ts_next = s
			else
				tsfirst = s;
			prev = s

			vtb[v] = s.next
		}
		fl = wmin		/* start a new sequence if some width */
	}
}

// adjust some voice elements
function voice_adj() {
function voice_adj(sys_chg) {
	var p_voice, s, s2, v

	// set the duration of the notes under a feathered beam
	function set_feathered_beam(s1) {
		var	s, s2, t, d, b, i, a,
			d = s1.dur,
			n = 1
︙
354
355
356
357
358
359
360
























361
362
363
364
365
366
367
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







				s.next.prev = s;
			p_voice.sym = s
		}
	}

	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (!sys_chg			// if not %%score
		 && p_voice.sls.length) {	// and no end of slur
			while (1) {
			    var	sl = p_voice.sls.shift()

				if (!sl)
					break
				s = sl.note.s
				for (s2 = s.next; s2; s2 = s2.next) {
					if (s2.bar_type && s2.bar_type[0] == ':')
						break
				}
				if (s2) {
					if (!s.sls)
						s.sls = []
					s.sls.push({
						ty: sl.ty,
						note: {s: s2}
					})
				} else {
					syntax(1, "Lack of ending slur(s)")
				}
			}
		}
		if (p_voice.ignore)
			p_voice.ignore = false
		for (s = p_voice.sym; s; s = s.next) {
			if (s.time >= staves_found)
				break
		}
		for ( ; s; s = s.next) {
︙
474
475
476
477
478
479
480
481

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505

506
507
508
509
510
511
512
517
518
519
520
521
522
523

524
525
526
527
528
529
530
531
532
533



534
535
536
537
538
539
540
541
542
543
544

545
546
547
548
549
550
551
552







-
+









-
-
-











-
+







	if (init) {				/* first staff system */
		cur_sy = par_sy = sy_new
		return
	}

	// update the previous system
	for (v = 0; v < voice_tb.length; v++) {
	    if (par_sy.voices[v].range >= 0) {
	    if (par_sy.voices[v]) {
		st = par_sy.voices[v].st
		var	sy_staff = par_sy.staves[st],
			p_voice = voice_tb[v]

		if (p_voice.staffnonote != undefined)
			sy_staff.staffnonote = p_voice.staffnonote
		if (p_voice.staffscale)
			sy_staff.staffscale = p_voice.staffscale;
	    }
		sy_new.voices[v] = clone(par_sy.voices[v]);
		sy_new.voices[v].range = -1;
		delete sy_new.voices[v].second
	}
	for (st = 0; st < par_sy.staves.length; st++) {
		sy_new.staves[st] = clone(par_sy.staves[st]);
		sy_new.staves[st].flags = 0
	}
	par_sy.next = sy_new;
	par_sy = sy_new
}

/* -- set the bar numbers -- */
// (possible hook)
function set_bar_num() {
Abc.prototype.set_bar_num = function() {
	var	s, s2, tim, bar_time, bar_num, rep_dtime,
		v = cur_sy.top_voice,
		wmeasure = voice_tb[v].meter.wmeasure,
		bar_rep = gene.nbar

	/* don't count a bar at start of line */
	for (s = tsfirst; ; s = s.ts_next) {
︙
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591




592


593
594
595
596
597
598
599
605
606
607
608
609
610
611


612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633

634
635
636
637
638
639
640
641
642







-
-


















+
+
+
+
-
+
+







		case C.MREST:
			bar_num += s.nmes - 1
			while (s.ts_next
			    && s.ts_next.type != C.BAR)
				s = s.ts_next
			break
		case C.BAR:
			if (s.bar_num)
				bar_num = s.bar_num	// (%%setbarnb)
			if (s.time < bar_time) {	// incomplete measure
				if (s.text && s.text[0] == '1') {
					bar_rep = bar_num;
					rep_dtime = bar_time - s.time
				}
				break
			}

			/* check if any repeat bar at this time */
			tim = s.time;
			s2 = s
			do {
				if (s2.dur)
					break
				if (s2.type == C.BAR && s2.text)	// if repeat bar
					break
				s2 = s2.next
			} while (s2 && s2.time == tim);

			if (s.bar_num)
				bar_num = s.bar_num	// (%%setbarnb)
			else
			bar_num++
				bar_num++

			if (s2 && s2.type == C.BAR && s2.text) {
				if (s2.text[0] == '1') {
					rep_dtime = 0;
					bar_rep = bar_num
				} else {			// restart bar numbering
					if (!cfmt.contbarnb)
						bar_num = bar_rep
︙
621
622
623
624
625
626
627
628
629


630
631
632

633
634
635



636
637
638




639
640
641
642
643
644
645
646
647
648
649
650
651
















652
653
654
655
656
657
658
659
660
661

662
663
664
665
666
667

668
669
670
671
672
673
674
675

676
677
678
679
680
681
682




683

684
685
686



687

688
689
690



691
692
693

694
695

696
697
698


699
700
701

702




703

704
705
706
707
708
709

710
711

712
713
714
715
716
717
718




719
720
721
722
723
724
725
726
727
728
729















730
731
732
733
734

735
736
737
738



739
740
741
742
743
744
745







746
747
748






749
750
751
752


































































































































































753
754
755
756
757


758
759
760
761
762
763
764
765
766
767
768








769
770
771


772
773

774
775
776
777

778
779
780
781
782
783
784
664
665
666
667
668
669
670


671
672
673
674

675
676
677
678
679
680
681



682
683
684
685













686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710

711
712
713
714
715
716

717
718
719
720
721
722
723
724

725
726
727
728
729
730
731
732
733
734
735
736

737
738
739

740
741
742
743
744



745
746
747



748


749



750
751



752
753
754
755
756
757

758
759
760
761
762
763

764
765

766
767
768
769
770
771
772
773
774
775
776
777











778
779
780
781
782
783
784
785
786
787
788
789
790
791
792





793




794
795
796







797
798
799
800
801
802
803



804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978


979
980

981




982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000

1001
1002
1003
1004

1005
1006
1007
1008
1009
1010
1011
1012







-
-
+
+


-
+



+
+
+
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
+





-
+







-
+







+
+
+
+
-
+


-
+
+
+

+
-
-
-
+
+
+
-
-
-
+
-
-
+
-
-
-
+
+
-
-
-
+

+
+
+
+
-
+





-
+

-
+







+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
-
-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
-
+
+
-

-
-
-
-





+
+
+
+
+
+
+
+



+
+

-
+



-
+








// note mapping
// %%map map_name note [print [note_head]] [param]*
function get_map(text) {
	if (!text)
		return

	var	i, note, notes, map, tmp, ns,
		a = info_split(text)
    var	i, note, notes, map, tmp, ns, ty,
	a = info_split(text)

	if (a.length < 3) {
		syntax(1, "Not enough parameters in %%map")
		syntax(1, not_enough_p)
		return
	}
	ns = a[1]
	if (ns[0] == '*' || ns.indexOf("all") == 0) {
		ns = 'all'
	} else {
	if (ns.indexOf("octave,") == 0
	 || ns.indexOf("key,") == 0) {		// remove the octave part
		ns = ns.replace(/[,']+$/m, '').toLowerCase(); //'
		if (ns.indexOf("octave,") == 0	// remove the octave part
		 || ns.indexOf("key,") == 0) {
			ty = ns[0]
			ns = ns.split(',')[1]
		if (ns[0] == 'k')		// remove the accidental part
			ns = ns.replace(/[_=^]+/, '')
	} else if (ns[0] == '*' || ns.indexOf("all") == 0) {
		ns = 'all'
	} else {				// exact pitch, rebuild the note
		tmp = new scanBuf();
		tmp.buffer = a[1];
		note = parse_acc_pit(tmp)
		if (!note) {
			syntax(1, "Bad note in %%map")
			return
		}
		ns = note2abc(note)
			ns = ns.replace(/[,']*/, '').toUpperCase(); //'
		} else {
			ty = ''
		}
		if (ty == 'k') {
			ns = 'k' + ntb.indexOf(ns)
		} else {
			tmp = new scanBuf;
			tmp.buffer = ns
			note = parse_acc_pit(tmp)
			if (!note) {
				syntax(1, "Bad note in %%map")
				return
			}
			ns = ty + abc2svg.pab40(note.pit, note.acc).toString()
		}
	}

	notes = maps[a[0]]
	if (!notes)
		maps[a[0]] = notes = {}
	map = notes[ns]
	if (!map)
		notes[ns] = map = []

	/* try the optional 'print' and 'heads' parameters */
	// try the optional 'print' and 'heads' parameters
	if (!a[2])
		return
	i = 2
	if (a[2].indexOf('=') < 0) {
		if (a[2][0] != '*') {
			tmp = new scanBuf();		// print
			tmp = new scanBuf;		// print
			tmp.buffer = a[2];
			map[1] = parse_acc_pit(tmp)
		}
		if (!a[3])
			return
		i++
		if (a[3].indexOf('=') < 0) {
			map[0] = a[3].split(',');
			map[0] = a[3].split(',')	// heads
			i++
		}
	}

	for (; i < a.length; i++) {
		switch (a[i]) {
		case "heads=":
			if (!a[++i]) {
				syntax(1, not_enough_p)
				break
			}
			map[0] = a[++i].split(',')
			map[0] = a[i].split(',')
			break
		case "print=":
			if (cfmt.sound == "play")
		case "play=":
			if (!a[++i]) {
				syntax(1, not_enough_p)
				break
			}
			tmp = new scanBuf();
			tmp.buffer = a[++i];
			map[1] = parse_acc_pit(tmp)
			tmp = new scanBuf;
			tmp.buffer = a[i];
			note = parse_acc_pit(tmp)
			break
//		case "transpose=":
//			switch (a[++i][0]) {
			if (a[i - 1][1] == 'r')
//			case "n":
//				map[2] = false
				map[1] = note
//				break
//			case "y":
//				map[2] = true
			else
				map[3] = abc2svg.pab40(note.pit, note.acc)
//				break
//			}
//			break
			break
		case "color=":
			if (!a[++i]) {
				syntax(1, not_enough_p)
				break
			}
			map[2] = a[++i]
			map[2] = a[i]
			break
		}
	}
}

// set the transposition in the previous or starting key
// set the transposition in the previous or first key signature
function set_transp() {
	var	s, transp, vtransp
    var	s, transp, sndtran

	if (curvoice.ckey.k_bagpipe || curvoice.ckey.k_drum)
		return

	if (cfmt.transp && curvoice.transp)	// if %%transpose and score=
		syntax(0, "Mix of old and new transposition syntaxes");


	if (cfmt.transp != undefined
	 || curvoice.transp != undefined
	 || curvoice.shift != undefined)
	transp = (cfmt.transp || 0) +		// %%transpose
		(curvoice.transp || 0) +	// score= / sound=
		(curvoice.shift || 0);		// shift=
	vtransp = curvoice.vtransp || 0
	if (transp == vtransp)
		return

	curvoice.vtransp = transp;

	s = curvoice.last_sym
	if (!s) {				// no symbol yet
		transp = (cfmt.transp || 0) +	 // %%transpose
			(curvoice.transp || 0) + // score= / sound= / instrument=
			(curvoice.shift || 0)	 // shift=
	if (curvoice.sndtran != undefined
	 || curvoice.sndsh != undefined)
		sndtran = (curvoice.sndtran || 0) +
			(curvoice.sndsh || 0)
	if (transp == undefined) {
		if (sndtran == undefined)
			return
	} else {
		curvoice.vtransp = transp
	}

	if (is_voice_sig()) {			// if no symbol yet
		curvoice.key = clone(curvoice.okey);
		key_transp(curvoice.key);
		curvoice.ckey = clone(curvoice.key)
		if (curvoice.key.k_none)
			curvoice.key.k_sf = 0
		curvoice.key = s = clone(curvoice.okey)
		return
	}

	// set the transposition in the previous K:
	} else {
		s = curvoice.last_sym
		while (1) {	// set the transposition in the previous K:
	while (1) {
		if (s.type == C.KEY)
			break
		if (!s.prev) {
			s = curvoice.key
			break
		}
			if (s.type == C.KEY)
				break
			s = s.prev
			if (!s) {
				s = curvoice.key
				break
			}
		s = s.prev
	}
	key_transp(s);
		}
	}
	if (transp != undefined)
		s.k_transp = transp
	if (sndtran != undefined)
		s.k_sndtran = sndtran
	curvoice.ckey = clone(s)
	if (curvoice.key.k_none)
		s.k_sf = 0
}

/* transpose a note / chord */
function note_transp(s, sk, note) {
    var	ak, an, d, b40,
	n = note.pit,
	a = note.acc

	if (!a && sk.k_a_acc)			// if accidental list
		a = sk.k_map[(n + 19) % 7]	// invisible accidental

	b40 = abc2svg.pab40(n, a) + sk.k_transp	// base-40 transposition

	note.pit = abc2svg.b40p(b40)		// new pitch

	if (!a) {				// if no old accidental
		if (!sk.k_a_acc			// if no accidental list
		 && !sk.k_none)			// and normal key
			return			// same accidental (in the key)
	}

	an = abc2svg.b40a(b40)			// new accidental
	if (a) {
		if (sk.k_a_acc) {		// if accidental list
			ak = sk.k_map[(note.pit + 19) % 7]
			if (ak == an)
				an = 0		// accidental in the key
		}
		if (!an)
			an = 3
	} else if (sk.k_none) {			// if no key
		if (acc_same_pitch(s, note.pit)) // and accidental from previous notes
			return			// no change
	} else if (sk.k_a_acc) {		// if accidental list
		if (acc_same_pitch(s, note.pit)) // and accidental from previous notes
			return			// no change
		ak = sk.k_map[(note.pit + 19) % 7]
		if (ak)
			an = 3		// natural
	} else {
		return			// same accidental (in the key)
	}

	d = note.micro_d
	if (d				/* microtone */
	 && a != an) {			/* different accidental type */
		n = note.micro_n
//fixme: double sharps/flats ?*/
//fixme: does not work in all cases (tied notes, previous accidental)
		switch (an) {
		case 3:			// natural
			if (n > d / 2) {
				n -= d / 2;
				note.micro_n = n;
				an = a
			} else {
				an = -a
			}
			break
		case 2:			// double sharp
			if (n > d / 2) {
				note.pit += 1;
				n -= d / 2
			} else {
				n += d / 2
			}
			an = a
			note.micro_n = n
			break
		case -2:		// double flat
			if (n >= d / 2) {
				note.pit -= 1;
				n -= d / 2
			} else {
				n += d / 2
			}
			an = a
			note.micro_n = n
			break
		}
	}
	note.acc = an
}

// adjust the pitches according to the transposition(s)
function pit_adj() {
    var	i, p_v, s, sk, g,
	nv = voice_tb.length

	while (--nv >= 0) {
		p_v = voice_tb[nv]
		if (p_v.vtransp == undefined)
			continue	// no transposition in this voice
		if (p_v.key.k_transp) {
			sk = p_v.key
			key_transp(sk)
		} else {
			sk = null
		}
		s = p_v.sym
		while (s) {

			// search a transposing key signature
			if (!sk) {
				for (; s; s = s.next) {
					if (s.type == C.KEY
					 && s.k_transp)
						break
				}
			}

			// transpose
			for (; s; s = s.next) {
				switch (s.type) {
				case C.GRACE:
					for (g = s.extra; g; g = g.next) {
						for (i = 0; i <= g.nhd; i++)
							note_transp(g, sk, g.notes[i])
					}
					continue
				case C.NOTE:
					for (i = 0; i <= s.nhd; i++)
						note_transp(s, sk, s.notes[i])
					continue
				case C.KEY:
					if (sk)
						s.k_sf = sk.k_sf
					key_transp(s)
					if (!s.k_transp) // end of transposition
						break
					sk = s
				default:
					continue
				}
				break
			}
			sk = null
		}
	}
} // pit_adj()

// get a transposition value as a base-40 interval
// The value may be
// - [+|-]<number of semitones>[s|f]
// - <note1>[<note2>]  % <note2> default is 'c'
function get_transp(param) {
	if (param[0] == '0')
		return 0
	if ("123456789-+".indexOf(param[0]) >= 0) {	// by semi-tone
	    var	val = parseInt(param)
		if (isNaN(val) || val < -36 || val > 36) {
//fixme: no source reference...
			syntax(1, "Bad transpose value")
			return
		}
		val += 36
		return (((val / 12) | 0) - 3) * 40 +
			(param.slice(-1) == 'b' ?
					abc2svg.ifb40 :
					abc2svg.isb40)[val % 12]
	}
	return get_interval(param, true)
} // get_transp()

/* -- process a pseudo-comment (%% or I:) -- */
// (possible hook)
function do_pscom(text) {
	var	h1, val, s, cmd, param, n, k, b,
Abc.prototype.do_pscom = function(text) {
    var	h1, val, s, cmd, param, n, k, b
		lock = false

	if (text.slice(-5) == ' lock') {
		lock = true;
		text = text.slice(0, -5).trim()
	}
	cmd = text.match(/(\w|-)+/)
	if (!cmd)
		return
	cmd = cmd[0];
	param = text.replace(cmd, '').trim()

	if (param.slice(-5) == ' lock') {
		fmt_lock[cmd] = true;
		param = param.slice(0, -5).trim()
	} else if (fmt_lock[cmd]) {
		return
	}

	switch (cmd) {
	case "center":
		if (parse.state >= 2) {
			if (parse.state == 2)
				goto_tune()
			s = new_block("text");
			s.text = cnv_escape(param);
			s.text = param
			s.opt = 'c'
			return
		}
		write_text(cnv_escape(param), 'c')
		write_text(param, 'c')
		return
	case "clef":
		if (parse.state >= 2) {
			if (parse.state == 2)
				goto_tune();
			s = new_clef(param)
			if (s)
︙
797
798
799
800
801
802
803
804

805
806
807
808
809
810
811
1025
1026
1027
1028
1029
1030
1031

1032
1033
1034
1035
1036
1037
1038
1039







-
+







	case "maxsysstaffsep":
		if (parse.state == 3) {
			par_sy.voices[curvoice.v].maxsep = get_unit(param)
			return
		}
		break
	case "multicol":
		generate()
		generate(1)
		switch (param) {
		case "start":
			blk_out();
			multicol = {
				posy: posy,
				maxy: posy,
				lmarg: cfmt.leftmargin,
︙
855
856
857
858
859
860
861

862

863
864
865
866
867
868
869
1083
1084
1085
1086
1087
1088
1089
1090

1091
1092
1093
1094
1095
1096
1097
1098







+
-
+







			goto_tune()
		}
		n = parseInt(param)
		if (isNaN(n) || n < -2 || n > 2) {
			syntax(1, errs.bad_val, "%%ottava")
			return
		}
		glovar.ottava = true;
		parse.ottava = n
		parse.ottava.push(n)
		return
	case "repbra":
		if (parse.state >= 2) {
			if (parse.state == 2)
				goto_tune();
			curvoice.norepbra = !get_bool(param)
		}
︙
917
918
919
920
921
922
923
924

925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942

943


944
945



946
947
948
949
950
951
952
953
954
1146
1147
1148
1149
1150
1151
1152

1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170

1171
1172
1173
1174


1175
1176
1177


1178
1179
1180
1181
1182
1183
1184







-
+

















-
+

+
+
-
-
+
+
+
-
-







		}
		if (h1 < 1)
			h1 = 14
		if (h2 < 1)
			h2 = h1
		if (len < 1)
			len = 90
		if (parse.state >= 2) {
		if (parse.state == 3) {
			s = new_block(cmd);
			s.x = (lwidth - len) / 2 / cfmt.scale;
			s.l = len / cfmt.scale;
			s.sk1 = h1;
			s.sk2 = h2
			return
		}
		blk_out();
		vskip(h1);
		output += '<path class="stroke"\n\td="M';
		out_sxsy((lwidth - len) / 2 / cfmt.scale, ' ', 0);
		output += 'h' + (len / cfmt.scale).toFixed(1) + '"/>\n';
		vskip(h2);
		blk_flush()
		return
	case "setbarnb":
		val = parseInt(param)
		if (isNaN(val) || val < 1)
		if (isNaN(val) || val < 1) {
			syntax(1, "Bad %%setbarnb value")
			break
		}
		else if (parse.state >= 2)
			glovar.new_nbar = val
		if (parse.state == 2)
			goto_tune()
		glovar.new_nbar = val
		else
			cfmt.measurefirst = val
		return
	case "staff":
		if (parse.state != 3) {
			if (parse.state != 2)
				return
			goto_tune()
		}
︙
990
991
992
993
994
995
996
997

998
999
1000
1001
1002





1003


1004
1005
1006
1007
1008
1009
1010
1011
1012
1013


1014
1015

1016
1017
1018
1019

1020
1021
1022
1023
1024
1025
1026
1027
1028
1029


1030

1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050

1051
1052
1053
1054
1055
1056
1057
1220
1221
1222
1223
1224
1225
1226

1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237

1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252

1253
1254
1255
1256

1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269

1270
1271



1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286

1287
1288
1289
1290
1291
1292
1293
1294







-
+





+
+
+
+
+
-
+
+










+
+

-
+



-
+










+
+
-
+

-
-
-















-
+







				s.stbrk_forced = true
		}
		sym_link(s)
		return
	case "stafflines":
	case "staffscale":
	case "staffnonote":
		self.set_v_param(cmd, param)
		set_v_param(cmd, param)
		return
	case "staves":
	case "score":
		if (parse.state == 0)
			return
		if (parse.scores && parse.scores.length > 0) {
			text = parse.scores.shift();
			cmd = text.match(/([^\s]+)\s*(.*)/);
			get_staves(cmd[1], cmd[2])
		} else {
		get_staves(cmd, param)
			get_staves(cmd, param)
		}
		return
	case "sysstaffsep":
//--fixme: may be global
		if (parse.state == 3) {
			par_sy.voices[curvoice.v].sep = get_unit(param)
			return
		}
		break
	case "text":
		if (parse.state >= 2) {
			if (parse.state == 2)
				goto_tune()
			s = new_block(cmd);
			s.text = cnv_escape(param);
			s.text = param
			s.opt = cfmt.textoption
			return
		}
		write_text(cnv_escape(param), cfmt.textoption)
		write_text(param, cfmt.textoption)
		return
	case "transpose":		// (abcm2ps compatibility)
		if (cfmt.sound)
			return
		switch (parse.state) {
		case 0:
			cfmt.transp = 0
			// fall thru
		case 1:
		case 2:
			val = get_transp(param)
			if (val)
			cfmt.transp = (cfmt.transp || 0) + get_transp(param)
				cfmt.transp = (cfmt.transp || 0) + val
			return
//		case 2:
//			goto_tune()
//			break
		}
		for (s = curvoice.last_sym; s; s = s.prev) {
			switch (s.type) {
			case C.NOTE:		// insert a key
				s = clone(curvoice.okey);
				s.k_old_sf = curvoice.ckey.k_sf;
				sym_link(s)
				break
			case C.KEY:
				break
			default:
				continue
			}
			break
		}
		do_info('V', curvoice.id + ' shift=' + param)
		do_info('V', curvoice.id + ' score=' + param)
		return
	case "tune":
//fixme: to do
		return
	case "user":
		set_user(param)
		return
︙
1065
1066
1067
1068
1069
1070
1071
1072

1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086

1087


1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099

1100
1101
1102
1103
1104


1105
1106
1107
1108
1109
1110
1111
1112
1113
1114

1115
1116
1117
1118
1119
1120
1121
1302
1303
1304
1305
1306
1307
1308

1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324

1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337

1338
1339
1340
1341


1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352

1353
1354
1355
1356
1357
1358
1359
1360







-
+














+
-
+
+











-
+



-
-
+
+









-
+







		return
	case "vskip":
		val = get_unit(param)
		if (val < 0) {
			syntax(1, "%%vskip cannot be negative")
			return
		}
		if (parse.state >= 2) {
		if (parse.state == 3) {
			s = new_block(cmd);
			s.sk = val
			return
		}
		vskip(val);
		return
	case "newpage":
	case "leftmargin":
	case "rightmargin":
	case "pagescale":
	case "pagewidth":
	case "printmargin":
	case "scale":
	case "staffwidth":
		if (parse.state >= 2) {
		if (parse.state == 3) {			// tune body
			if (parse.state == 2)
				goto_tune()
			s = new_block(cmd);
			s.param = param
			return
		}
		if (cmd == "newpage") {
			blk_flush();
			block.newpage = true;
			return
		}
		break
	}
	self.set_format(cmd, param, lock)
	self.set_format(cmd, param)
}

// treat the %%beginxxx / %%endxxx sequences
// (posible hook)
function do_begin_end(type,
// (possible hook)
Abc.prototype.do_begin_end = function(type,
			opt,
			text) {
	var i, j, action, s

	switch (type) {
	case "js":
		js_inject(text)
		break
	case "ml":
		if (parse.state >= 2) {
		if (parse.state == 3) {
			s = new_block(type);
			s.text = text
		} else {
			svg_flush();
			if (user.img_out)
				user.img_out(text)
		}
︙
1146
1147
1148
1149
1150
1151
1152



1153


1154
1155

1156
1157
1158
1159

1160
1161
1162
1163
1164
1165
1166










1167
1168
1169
1170
1171
1172
1173

1174
1175
1176
1177
1178
1179
1180
1181

1182
1183
1184
1185
1186
1187
1188
1189
1190
1191




1192
1193
1194
1195
1196
1197


1198
1199

1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211

1212
1213
1214

1215
1216
1217
1218

1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229

1230
1231
1232
1233
1234
1235
1236
1237


1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249


1250
1251
1252
1253

1254
1255
1256


1257
1258
1259

1260
1261
1262

1263
1264

1265
1266
1267
1268

1269
1270


1271
1272
1273

1274
1275

1276
1277

1278
1279
1280
1281
1282
1283
1284
1285

1286
1287
1288
1289
1290
1291
1292
1293


1294
1295
1296
1297
1298
1299
1300
1301

1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320


1321
1322
1323
1324
1325
1326

1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345


1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363


1364
1365
1366
1367
1368
1369
1370
1371
1372

1373
1374
1375
1376
1377
1378
1379
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398

1399
1400
1401
1402

1403
1404
1405
1406
1407
1408


1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424

1425
1426
1427
1428
1429
1430
1431
1432

1433

1434
1435
1436
1437
1438
1439
1440
1441

1442
1443
1444
1445
1446
1447
1448
1449


1450
1451
1452
1453
1454
1455
1456



1457
1458
1459
1460
1461
1462

1463



1464




1465











1466








1467
1468












1469
1470




1471



1472
1473



1474



1475


1476




1477


1478
1479



1480


1481


1482








1483
1484
1485
1486
1487
1488
1489


1490
1491





1492
1493

1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511


1512
1513
1514
1515
1516
1517
1518

1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536


1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554


1555
1556
1557
1558
1559
1560
1561
1562
1563
1564

1565
1566
1567
1568
1569
1570
1571
1572







+
+
+

+
+

-
+



-
+





-
-
+
+
+
+
+
+
+
+
+
+






-
+







-
+
-








-
+
+
+
+




-
-
+
+


+


-
-
-






-
+
-
-
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
+
-
-
-
+
+
-
-
-
+
-
-
-
+
-
-
+
-
-
-
-
+
-
-
+
+
-
-
-
+
-
-
+
-
-
+
-
-
-
-
-
-
-
-
+






-
-
+
+
-
-
-
-
-


-
+

















-
-
+
+





-
+

















-
-
+
+
















-
-
+
+








-
+







			defs_add(text.slice(i + 6, j))
		}
		break
	case "text":
		action = get_textopt(opt);
		if (!action)
			action = cfmt.textoption
		if (text.indexOf('\\') >= 0)	// convert the escape sequences
			text = cnv_escape(text)

		if (parse.state >= 2) {
			if (parse.state == 2)
				goto_tune()
			s = new_block(type);
			s.text = cnv_escape(text);
			s.text = text
			s.opt = action
			break
		}
		write_text(cnv_escape(text), action)
		write_text(text, action)
		break
	}
}

/* -- generate a piece of tune -- */
function generate() {
	var v, p_voice;
function generate(in_mc) {
    var s, v, p_voice;

	if (parse.tp) {
		syntax(1, "No end of tuplet")
		s = parse.tps
		if (s)
			delete s.tp
		delete parse.tp
	}

	if (vover) {
		syntax(1, "No end of voice overlay");
		get_vover(vover.bar ? '|' : ')')
	}

	if (voice_tb.length == 0)
	if (!voice_tb.length)
		return
	voice_adj();
	dupl_voice();
	sort_all()			/* define the time / vertical sequences */
	if (!tsfirst)
		return
	self.set_bar_num()
	if (!tsfirst)
	pit_adj()
		return				/* no more symbol */

	// give the parser result to the application
	if (user.get_abcmodel)
		user.get_abcmodel(tsfirst, voice_tb, anno_type, info)

	if (user.img_out)		// if SVG generation
		self.output_music()

	/* reset the parser */
	// if inside multicol, reset the parser
	if (!in_mc)
		return
	voice_tb = Object.create(voice_tb)
	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v];
		p_voice.time = 0;
		p_voice.sym = p_voice.last_sym = null;
		p_voice.st = cur_sy.voices[v].st;
		p_voice.second = cur_sy.voices[v].second;
//		p_voice.st = cur_sy.voices[v].st;
//		p_voice.second = cur_sy.voices[v].second;
//		p_voice.clef.time = 0;
		delete p_voice.have_ly;
		p_voice.sls = [];
		p_voice.hy_st = 0;
		delete p_voice.bar_start
		delete p_voice.slur_st
		delete p_voice.s_tie
		delete p_voice.s_rtie
	}
	staves_found = 0			// (for compress/dup the voices)
}

// transpose a key
//fixme: transpose of the accidental list is not done
function key_transp(s_key) {
function key_transp(sk) {
	var	t = (curvoice.vtransp / 3) | 0,
		sf = (t & ~1) + (t & 1) * 7 + s_key.k_sf

	if (sk.k_a_acc || sk.k_none)		// same displayed key
	switch ((curvoice.vtransp + 210) % 3) {
	case 1:
		sf = (sf + 4 + 12 * 4) % 12 - 4	/* more sharps */
		break
		return
	case 2:
		sf = (sf + 7 + 12 * 4) % 12 - 7	/* more flats */
		break
	default:
		sf = (sf + 5 + 12 * 4) % 12 - 5	/* Db, F# or B */
		break
	}
	s_key.k_sf = sf;
	s_key.k_delta = cgd2cde[(sf + 7) % 7]
}

    var	d,
/* -- set the accidentals when K: with modified accidentals -- */
function set_k_acc(s) {
	var i, j, n, nacc, p_acc,
		accs = [],
		pits = [],
		m_n = [],
		m_d = []

	k_b40 = sk.k_b40,
	n_b40 = (k_b40 + 200 + sk.k_transp) % 40
	if (s.k_sf > 0) {
		for (nacc = 0; nacc < s.k_sf; nacc++) {
			accs[nacc] = 1;			// sharp
			pits[nacc] = [26, 23, 27, 24, 21, 25, 22][nacc]
		}
	} else {
		for (nacc = 0; nacc < -s.k_sf; nacc++) {
			accs[nacc] = -1;		// flat
			pits[nacc] = [22, 25, 21, 24, 20, 23, 26][nacc]
		}
	}
	n = s.k_a_acc.length

	d = abc2svg.b40k[n_b40] - n_b40
	for (i = 0; i < n; i++) {
		p_acc = s.k_a_acc[i]
		for (j = 0; j < nacc; j++) {
			if (pits[j] == p_acc.pit) {
	if (d) {
				accs[j] = p_acc.acc
				if (p_acc.micro_n) {
					m_n[j] = p_acc.micro_n;
		if (sk.k_transp > 0)
			sk.k_transp += d
					m_d[j] = p_acc.micro_d
				}
				break
		else
			}
		}
		if (j == nacc) {
			sk.k_transp -= d
			accs[j] = p_acc.acc;
			pits[j] = p_acc.pit
		n_b40 += d
			if (p_acc.micro_n) {
				m_n[j] = p_acc.micro_n;
				m_d[j] = p_acc.micro_d
			}
	}
			nacc++
		}
	sk.k_b40 = n_b40

	}
	for (i = 0; i < nacc; i++) {
		p_acc = s.k_a_acc[i]
   var sf = abc2svg.b40sf[n_b40]
		if (!p_acc)
			p_acc = s.k_a_acc[i] = {}
	sk.k_old_sf = sk.k_sf
		p_acc.acc = accs[i];
		p_acc.pit = pits[i]
	sk.k_sf = sf
		if (m_n[i]) {
			p_acc.micro_n = m_n[i];
			p_acc.micro_d = m_d[i]
		} else {
			delete p_acc.micro_n
			delete p_acc.micro_d
		}
	}
	sk.k_map = abc2svg.keys[sf + 7]	// map of the notes with accidentals
}

/*
 * for transpose purpose, check if a pitch is already in the measure or
 * if it is tied from a previous note, and return the associated accidental
 */
function acc_same_pitch(pitch) {
	var	i, time,
function acc_same_pitch(s, pit) {
    var	i,
		s = curvoice.last_sym.prev

	if (!s)
		return //undefined;

	time = s.time

	for (; s; s = s.prev) {
	for (s = s.prev; s; s = s.prev) {
		switch (s.type) {
		case C.BAR:
			if (s.time < time)
				return //undefined // no same pitch
			while (1) {
				s = s.prev
				if (!s)
					return //undefined
				if (s.type == C.NOTE) {
					if (s.time + s.dur == time)
						break
					return //undefined
				}
				if (s.time < time)
					return //undefined
			}
			for (i = 0; i <= s.nhd; i++) {
				if (s.notes[i].pit == pitch
				 && s.notes[i].ti1)
				if (s.notes[i].pit == pit
				 && s.notes[i].tie_ty)
					return s.notes[i].acc
			}
			return //undefined
		case C.NOTE:
			for (i = 0; i <= s.nhd; i++) {
				if (s.notes[i].pit == pitch)
				if (s.notes[i].pit == pit)
					return s.notes[i].acc
			}
			break
		}
	}
	return //undefined
}

/* -- get staves definition (%%staves / %%score) -- */
function get_staves(cmd, parm) {
	var	s, p_voice, p_voice2, i, flags, v, vid,
		st, range,
		a_vf = parse_staves(parm) // array of [vid, flags]

	if (!a_vf)
		return

	if (voice_tb.length != 0) {
		voice_adj();
	if (voice_tb.length) {
		voice_adj(true);
		dupl_voice()
	}

	/* create a new staff system */
	var	maxtime = 0,
		no_sym = true

	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (p_voice.time > maxtime)
			maxtime = p_voice.time
		if (p_voice.sym)
			no_sym = false
	}
	if (no_sym				/* if first %%staves */
	 || (maxtime == 0 && staves_found < 0)) {
		for (v = 0; v < par_sy.voices.length; v++)
			par_sy.voices[v].range = -1
		par_sy.staves = []
		par_sy.voices = []
	} else {

		/*
		 * create a new staff system and
		 * link the 'staves' symbol in a voice which is seen from
		 * the previous system - see sort_all
		 */
		for (v = 0; v < par_sy.voices.length; v++) {
			if (par_sy.voices[v].range >= 0) {
			if (par_sy.voices[v]) {
				curvoice = voice_tb[v]
				break
			}
		}
		curvoice.time = maxtime;
		s = {
			type: C.STAVES,
︙
1401
1402
1403
1404
1405
1406
1407
1408

1409

1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420


1421
1422
1423
1424
1425
1426
1427
1594
1595
1596
1597
1598
1599
1600

1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623







-
+

+











+
+







		p_voice = new_voice(vid);
		p_voice.time = maxtime;
		v = p_voice.v
		if (i == 0)
			par_sy.top_voice = p_voice.v

		// if the voice is already here, clone it
		if (par_sy.voices[v].range >= 0) {
		if (par_sy.voices[v]) {
			p_voice2 = clone(p_voice);
//			p_voice2.id += "_c"		// for tests
			par_sy.voices[voice_tb.length] = clone(par_sy.voices[v]);
			v = voice_tb.length;
			p_voice2.v = v;
			p_voice2.sym = p_voice2.last_sym = null;
			p_voice2.time = maxtime;
			voice_tb.push(p_voice2)
			delete p_voice2.clone
			while (p_voice.clone)
				p_voice = p_voice.clone;
			p_voice.clone = p_voice2;
			p_voice = p_voice2
		} else {
			par_sy.voices[v] = {}
		}
		a_vf[i][0] = p_voice;
		par_sy.voices[v].range = range++
	}

	/* change the behavior from %%staves to %%score */
	if (cmd[1] == 't') {				/* if %%staves */
︙
1511
1512
1513
1514
1515
1516
1517
1518

1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550


1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563





1564
1565
1566
1567
1568
1569
1570
1707
1708
1709
1710
1711
1712
1713

1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727





1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768







-
+













-
-
-
-
-














+
+













+
+
+
+
+







	if (cmd[1] == 'c') {				/* if %%score */
		for (st = 0; st < nstaff; st++)
			par_sy.staves[st].flags ^= STOP_BAR
	}

	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (par_sy.voices[v].range < 0) {
		if (!par_sy.voices[v]) {
			p_voice.ignore = true
			continue
		}
		par_sy.voices[v].second = p_voice.second;
		st = p_voice.st
		if (st > 0 && !p_voice.norepbra
		 && !(par_sy.staves[st - 1].flags & STOP_BAR))
			p_voice.norepbra = true
	}

	curvoice = parse.state >= 2 ? voice_tb[par_sy.top_voice] : null
}

/* -- get a voice overlay -- */
function get_vover(type) {
	var	p_voice2, p_voice3, range, s, time, v, v2, v3,
		line = parse.line

	// get a voice or create a clone of the current voice
	function clone_voice(id) {
		var v, p_voice

		for (v = 0; v < voice_tb.length; v++) {
			p_voice = voice_tb[v]
			if (p_voice.id == id)
				return p_voice		// found
		}
		p_voice = clone(curvoice);
		p_voice.v = voice_tb.length;
		p_voice.id = id;
		p_voice.sym = p_voice.last_sym = null;

		p_voice.key = clone(curvoice.key)

		delete p_voice.nm
		delete p_voice.snm
		delete p_voice.new_name
		delete p_voice.lyric_restart
		delete p_voice.lyric_cont
		delete p_voice.ly_a_h;
		delete p_voice.sym_restart
		delete p_voice.sym_cont
		delete p_voice.have_ly

		voice_tb.push(p_voice)
		return p_voice
	} // clone_voice()

/* -- get a voice overlay -- */
function get_vover(type) {
    var	p_voice2, p_voice3, range, s, time, v, v2, v3,
	line = parse.line

	/* treat the end of overlay */
	if (curvoice.ignore)
		return
	if (type == '|'
	 || type == ')')  {
		if (!curvoice.last_note) {
︙
1616
1617
1618
1619
1620
1621
1622
1623


1624
1625
1626
1627
1628
1629
1630
1814
1815
1816
1817
1818
1819
1820

1821
1822
1823
1824
1825
1826
1827
1828
1829







-
+
+







		par_sy.voices[v2] = {
			st: curvoice.st,
			second: true
		}
		var f_clone = curvoice.clone != undefined ? 1 : 0;
		range = par_sy.voices[curvoice.v].range
		for (v = 0; v < par_sy.voices.length; v++) {
			if (par_sy.voices[v].range > range)
			if (par_sy.voices[v]
			 && par_sy.voices[v].range > range)
				par_sy.voices[v].range += f_clone + 1
		}
		par_sy.voices[v2].range = range + 1
		if (f_clone) {
			p_voice3 = clone_voice(p_voice2.id + 'c');
			p_voice3.second = true;
			v3 = p_voice3.v;
︙
1658
1659
1660
1661
1662
1663
1664











1665
1666
1667
1668
1669
1670
1671
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881







+
+
+
+
+
+
+
+
+
+
+







			syntax(1, "Wrong duration in voice overlay")
			if (curvoice.time > vover.p_voice.time)
				vover.p_voice.time = curvoice.time
		}
	}
	p_voice2.time = vover.time;
	curvoice = p_voice2

	// add a bar at start of the measure overlay
	// (needed for sort_all() in case of spaces - 'y')
	if (vover.bar) {
		sym_link({
			type: C.BAR,
			bar_type: type,
			dur: 0,
			multi: 0
		})
	}
}

// check if a clef, key or time signature may go at start of the current voice
function is_voice_sig() {
	var s

	if (!curvoice.sym)
︙
1690
1691
1692
1693
1694
1695
1696
1697

1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712

1713
1714
1715
1716
1717
1718
1719
1720
1721

1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736

1737
1738
1739




1740
1741
1742




1743
1744
1745
1746
1747





1748
1749
1750
1751
1752

1753
1754

1755


1756
1757


1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779

1780
1781
1782
1783
1784
1785
1786
1900
1901
1902
1903
1904
1905
1906

1907
1908
1909
1910
1911
1912




1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927

1928
1929
1930
1931
1932





1933
1934
1935
1936
1937

1938
1939
1940

1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954


1955
1956
1957
1958
1959
1960
1961
1962
1963

1964
1965

1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980













1981

1982
1983
1984
1985
1986
1987
1988
1989







-
+





-
-
-
-






+








-
+




-
-
-
-
-





-
+


-
+
+
+
+



+
+
+
+



-
-
+
+
+
+
+




-
+

-
+

+
+


+
+







-
-
-
-
-
-
-
-
-
-
-
-
-

-
+







	// clef change
	sym_link(s);
	s.clef_small = true
}

// treat K: (kp = key signature + parameters)
function get_key(parm) {
	var	v, p_voice, s, transp,
	var	v, p_voice, s, transp, sndtran,
//		[s_key, a] = new_key(parm)	// KO with nodejs
		a = new_key(parm),
		s_key = a[0];

	a = a[1]
	if (s_key.k_sf
	 && !s_key.k_exp
	 && s_key.k_a_acc)
		set_k_acc(s_key)

	switch (parse.state) {
	case 1:				// in tune header (first K:)
		if (s_key.k_sf == undefined && !s_key.k_a_acc) { // empty K:
			s_key.k_sf = 0;
			s_key.k_none = true
			s_key.k_map = abc2svg.keys[7]
		}
		for (v = 0; v < voice_tb.length; v++) {
			p_voice = voice_tb[v];
			p_voice.key = clone(s_key);
			p_voice.okey = clone(s_key);
			p_voice.ckey = clone(s_key)
		}
		parse.ckey = s_key
		if (a.length != 0)
		if (a.length)
			memo_kv_parm('*', a)
		if (!glovar.ulen)
			glovar.ulen = C.BLEN / 8;
		parse.state = 2;		// in tune header after K:

		set_page();
		write_heading();
		reset_gen();
		gene.nbar = cfmt.measurefirst	// measure numbering
		return
	case 2:					// K: at start of tune body
		goto_tune(true)
		break
	}
	if (a.length != 0)
	if (a.length)
		set_kv_parm(a);

	if (!curvoice.ckey.k_bagpipe && !curvoice.ckey.k_drum)
	if (!curvoice.ckey.k_bagpipe && !curvoice.ckey.k_drum
	 && (cfmt.transp != undefined
	  || curvoice.transp != undefined
	  || curvoice.shift != undefined))
	    transp = (cfmt.transp || 0) +
		(curvoice.transp || 0) +
		(curvoice.shift || 0)
	if (curvoice.sndtran != undefined
	 || curvoice.sndsh != undefined)
		sndtran = (curvoice.sndtran || 0) +
			(curvoice.sndsh || 0)

	if (s_key.k_sf == undefined) {
		if (!s_key.k_a_acc
		 && !transp)
			return
		 && transp == undefined) {
			if (sndtran == undefined)
				return		// not a key signature
			s_key.k_play = true	// play only
		}
		s_key.k_sf = curvoice.okey.k_sf
	}

	curvoice.okey = clone(s_key)
	if (transp) {
	if (transp != undefined) {
		curvoice.vtransp = transp;
		key_transp(s_key)
		s_key.k_transp = transp
	}
	if (sndtran != undefined)
		s_key.k_sndtran = sndtran

	s_key.k_old_sf = curvoice.ckey.k_sf;	// memorize the key changes
	if (!s_key.k_b40)
		s_key.k_b40 = curvoice.ckey.k_b40

	curvoice.ckey = s_key

	if (is_voice_sig()) {
		curvoice.key = clone(s_key)
		if (s_key.k_none)
			curvoice.key.k_sf = 0
		return
	}

	/* the key signature must appear before a time signature */
	s = curvoice.last_sym
	if (s && s.type == C.METER) {
		curvoice.last_sym = s.prev
		if (!curvoice.last_sym)
			curvoice.sym = null;
		sym_link(s_key);
		s_key.next = s;
		s.prev = s_key;
		curvoice.last_sym = s
	} else {
		sym_link(s_key)
		sym_link(s_key)			// (don't move the key)
	}
}

// get / create a new voice
function new_voice(id) {
	var	p_voice, v, p_v_sav,
		n = voice_tb.length
︙
1834
1835
1836
1837
1838
1839
1840


1841
1842
1843
1844
1845
1846
1847


1848

1849
1850
1851
1852
1853
1854
1855
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050


2051
2052

2053
2054
2055
2056
2057
2058
2059
2060







+
+





-
-
+
+
-
+







		wmeasure: glovar.meter.wmeasure,
		clef: {
			type: C.CLEF,
			clef_auto: true,
			clef_type: "a",		// auto
			time: 0
		},
		acc: [],		// accidentals of the measure (parse)
		sls: [],		// slurs - used in parsing and in generation
		hy_st: 0
	}

	voice_tb.push(p_voice);

	par_sy.voices[v] = {
		range: -1
//	par_sy.voices[v] = {
//		range: -1
	}
//	}

	return p_voice
}

// this function is called at program start and on end of tune
function init_tune() {
	nstaff = -1;
︙
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899



1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910

1911
1912
1913

1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934




1935
1936
1937
1938
1939
1940
1941

1942
1943
1944
1945
1946
1947
1948
1949
1950
1951


1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968










1969
1970
1971
1972
1973

1974
1975
1976
1977
1978
1979
1980
2095
2096
2097
2098
2099
2100
2101



2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114

2115
2116
2117

2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137


2138
2139
2140
2141
2142
2143
2144
2145
2146
2147

2148
2149
2150
2151
2152
2153
2154
2155
2156
2157

2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190

2191
2192
2193
2194
2195
2196
2197
2198







-
-
-
+
+
+










-
+


-
+



















-
-
+
+
+
+






-
+









-
+
+

















+
+
+
+
+
+
+
+
+
+




-
+







		tosvg(parse.fname, file, start, eol)
	}
	include--
}

// treat a 'V:' info
function get_voice(parm) {
	var	v, transp, vtransp, vs,
		a = info_split(parm),
		vid = a.shift();
    var	v, vs,
	a = info_split(parm),
	vid = a.shift()

	if (!vid)
		return				// empty V:

	if (vid.indexOf(',') > 0) {		// if many voices
		vs = vid.split(',');
		vid = vs.shift()
	}

	if (parse.state < 2) {
		if (a.length != 0)
		if (a.length)
			memo_kv_parm(vid, a)
		if (vid != '*' && parse.state == 1)
			new_voice(vid)
			curvoice = new_voice(vid)
		return
	}

	if (vid == '*') {
		syntax(1, "Cannot have V:* in tune body")
		return
	}
	curvoice = new_voice(vid);
	set_kv_parm(a)
	if (parse.state == 2)			// if first voice
		goto_tune();
	set_transp();

	v = curvoice.v
	if (curvoice.new) {			// if new voice
		delete curvoice.new
		if (staves_found < 0) {		// if no %%score/%%staves
			curvoice.st = curvoice.cst = ++nstaff;
			par_sy.nstaff = nstaff;
			par_sy.voices[v].st = nstaff;
			par_sy.voices[v].range = v;
			par_sy.voices[v] = {
				st: nstaff,
				range: v
			}
			par_sy.staves[nstaff] = {
				stafflines: curvoice.stafflines || "|||||",
				staffscale: 1
			}
		}
	
		if (par_sy.voices[v].range < 0) {
		if (!par_sy.voices[v]) {
//			if (cfmt.alignbars)
//				syntax(1, "V: does not work with %%alignbars")
			if (staves_found >= 0)
				curvoice.ignore = true
		}
	}

	if (!curvoice.filtered
	 && !curvoice.ignore
	 && parse.voice_opts) {
	 && (parse.voice_opts
	  || parse.tune_v_opts)) {
		curvoice.filtered = true;
		voice_filter()
	}

	if (vs)
		do_cloning(vs)
}

// change state from 'tune header after K:' to 'in tune body'
// curvoice is defined when called from get_voice()
function goto_tune(is_K) {
	var	v, p_voice,
		s = {
			type: C.STAVES,
			dur: 0,
			sy: par_sy
		}

	set_page();
	write_heading();
	reset_gen();
	if (glovar.new_nbar) {
		gene.nbar = glovar.new_nbar	// measure numbering
		glovar.new_nbar = 0
	} else {
		gene.nbar = 1
	}

	parse.state = 3;			// in tune body

	// if no voice yet, create the default voice
	if (voice_tb.length == 0) {
	if (!voice_tb.length) {
		get_voice("1");
		curvoice.clef.istart = curvoice.key.istart;
		curvoice.clef.iend = curvoice.key.iend;
//		nstaff = 0;
		curvoice.default = true
	} else if (!curvoice) {
		curvoice = voice_tb[staves_found < 0 ? 0 : par_sy.top_voice]
︙
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007





2008
2009
2010
2011
2012
2013
2014
2216
2217
2218
2219
2220
2221
2222



2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234







-
-
-
+
+
+
+
+








	// initialize the voices when no %%staves/score	
	if (staves_found < 0) {
		nstaff = voice_tb.length - 1
		for (v = 0; v <= nstaff; v++) {
			p_voice = voice_tb[v];
			delete p_voice.new;		// old voice
			p_voice.st = p_voice.cst =
				par_sy.voices[v].st =
					par_sy.voices[v].range = v;
			p_voice.st = p_voice.cst = v;
			par_sy.voices[v] = {
				st: v,
				range: v
			}
			par_sy.staves[v] = {
				stafflines: p_voice.stafflines || "|||||",
				staffscale: 1
			}
		}
		par_sy.nstaff = nstaff
	}
︙
Changes to editor/edit.js.
︙
27
28
29
30
31
32
33

34
35


36
37
38
39
40
41
42
43
44
45
46






47
48
49
50
51
52
53
54
55
56
57
58
59
60
61




62
63
64
65
66
67
68
69

70
71
72
73
74

75
76
77

78
79
80
81
82
83
84
27
28
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46


47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63




64
65
66
67

68
69
70
71
72
73

74
75
76
77
78

79
80
81

82
83
84
85
86
87
88
89







+

-
+
+









-
-
+
+
+
+
+
+











-
-
-
-
+
+
+
+
-






-
+




-
+


-
+







	else
		alert("window error: " + msg)
	return false
}

var	abc_images,			// image buffer
	abc_fname = ["noname.abc", ""],	// file names
	abc_mtime = [],			// associated last modification time
	abc,				// Abc object
	srcend,				// source symbol end index
	syms,				// music symbol at source index
	ctxMenu,			// context menu for play
	elt_ref = {},			// pointers to page HTML elements
	selx = [0, 0],			// selected source indexes
	selx_sav = [],			// (saved while playing/printing)
	play = {},			// play data
	pop,				// current popup message
	texts = {},			// language specific texts
	jsdir = document.currentScript ?
		document.currentScript.src.match(/.*\//) :
		(function() {
			var scrs = document.getElementsByTagName('script');
			return scrs[scrs.length - 1].src.match(/.*\//) || ''
		     var s_a = document.getElementsByTagName('script')
			for (var k = 0; k < s_a.length; k++) {
				if (s_a[k].src.indexOf('edit-') >= 0)
					return s_a[k].src.match(/.*\//) || ''
			}
			return ""	// ??
		})()

// -- Abc create argument
var user = {
	// -- required methods
	// include a file (%%abc-include - only one)
	read_file: function(fn) {
		elt_ref["s" + srcidx].style.display = "inline"
		return elt_ref.src1.value
	},
	// insert the errors
	errmsg: function(msg, l, c) {
		msg = clean_txt(msg)
		if (l)
			elt_ref.diverr.innerHTML += '<b onclick="gotoabc(' +
	errbld: function(sev, txt, fn, idx) {
	    var	msg = sev + ' ' + clean_txt(txt)
		if (idx >= 0)
			elt_ref.diverr.innerHTML += '<b onclick="gotoabc(-1,' + idx +
				l + ',' + c +
				')" style="cursor: pointer; display: inline-block">' +
				msg + "</b><br/>\n"
		else
			elt_ref.diverr.innerHTML += msg + "<br/>\n"
	},
	// image output
	my_img_out: function(str) {
	img_out: function(str) {
		abc_images += str
	},
	// -- optional methods
	// annotations
	anno_stop: function(type, start, stop, x, y, w, h) {
	anno_stop: function(type, start, stop, x, y, w, h, s) {
		if (["beam", "slur", "tuplet"].indexOf(type) >= 0)
			return
		srcend[start] = stop;	// source index of the end of the element
		syms[start] = s		// music symbol

		// create a rectangle
		abc.out_svg('<rect class="abcr _' + start +
			'_" x="');
		abc.out_sxsy(x, '" y="', y);
		abc.out_svg('" width="' + w.toFixed(2) +
			'" height="' + abc.sh(h).toFixed(2) + '"/>\n')
︙
155
156
157
158
159
160
161
162
163
164
165



166
167
168
169
170
171
172
160
161
162
163
164
165
166




167
168
169
170
171
172
173
174
175
176







-
-
-
-
+
+
+







//	}
	abc_fname[srcidx] = files[0].name

	var reader = new FileReader();

	// Closure to capture the file information
	reader.onloadend = function(evt) {
		var	i, j, sl,
			content = evt.target.result,
			s = srcidx == 0 ? "source" : "src1"
		elt_ref[s].value = content;
	    var	s = srcidx == 0 ? "source" : "src1";

		elt_ref[s].value = evt.target.result;
		elt_ref["s" + srcidx].value = abc_fname[srcidx];
		src_change()
	}

	// Read the file as text
	reader.readAsText(files[0], "UTF-8")
}
︙
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
189
190
191
192
193
194
195

196
197
198
199
200
201
202







-







}

// render the textarea content to the right side
function render() {
    var	i, j,
	content = elt_ref.source.value;

	play.a_pe = null
	if (!content)
		return			// empty source

	// if include file not loaded yet, ask it
	i = content.indexOf('%%abc-include ')
	if (i >= 0) {
		var sl = elt_ref.s1
︙
213
214
215
216
217
218
219
220
221









222
223
224
225
226
227
228

229
230
231
232
233
234
235
216
217
218
219
220
221
222


223
224
225
226
227
228
229
230
231
232
233
234
235
236
237

238
239
240
241
242
243
244
245







-
-
+
+
+
+
+
+
+
+
+






-
+







function render2() {
    var	content = elt_ref.source.value

	// load the required modules
	if (!abc2svg.modules.load(content + elt_ref.src1.value, render2))
		return

	user.img_out = user.my_img_out;
	user.get_abcmodel = null;
	// if page formatting, define a function to get the modification date
	if (abc2svg.modules.pageheight.loaded) {
		abc2svg.get_mtime = function(fn) {
		    var	files = document.getElementById("abcfile").files
			if (files && files[0].lastModified)
				return new Date(files[0].lastModified)
			return new Date()
		}
	}

	abc = new abc2svg.Abc(user);
	abc_images = '';
	abc.tosvg('edit', '%%bgcolor white');

//	document.body.style.cursor = "wait";
	srcend = []
	syms = []
	try {
		abc.tosvg(abc_fname[0], content)
	} catch(e) {
		alert(e.message + '\nabc2svg tosvg bug - stack:\n' + e.stack)
		return
	}
	abc2svg.abc_end()		// close the page if %%pageheight
︙
260
261
262
263
264
265
266
267

268
269
270
271
272
273
274

275
276
277
278
279
280
281
282
270
271
272
273
274
275
276

277
278
279
280
281
282
283

284

285
286
287
288
289
290
291







-
+






-
+
-







			idx = s.value.length - 1;
			c = 0
			break
		}
	}
	c = Number(c) + idx;
	s.focus();
	s.setSelectionRange(c, srcend[c] || c + 1)
	s.setSelectionRange(c, syms[c].iend || c + 1)
}

// click in the target
function selsvg(evt) {
    var	v,
	elt = evt.target,
	cl = elt.getAttribute('class'),
	cl = elt.getAttribute('class')
	ctxMenu = document.getElementById("ctxMenu");

	play.loop = false;

	evt.stopImmediatePropagation();
	evt.preventDefault()

	// remove the context menu if active
︙
292
293
294
295
296
297
298
299

300
301
302
303
304
305
306
301
302
303
304
305
306
307

308
309
310
311
312
313
314
315







-
+







		return false
	}

	// highlight the clicked element or clear the selection start
	s = elt_ref.source;
	if (cl && cl.substr(0, 4) == 'abcr') {
		v = Number(cl.slice(6, -1));
		s.setSelectionRange(v, srcend[v])
		s.setSelectionRange(v, syms[v].iend)
	} else {
		s.setSelectionRange(0, 0)
	}
	s.blur();
	s.focus()
}

︙
360
361
362
363
364
365
366
367
368


369
370
371
372

373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397

398

399
400
401
402
403





404
405
406

407
408
409
410
411
412
413
369
370
371
372
373
374
375


376
377
378
379
380

381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400


401
402
403
404
405

406





407
408
409
410
411
412
413

414
415
416
417
418
419
420
421







-
-
+
+



-
+



















-
-




+
-
+
-
-
-
-
-
+
+
+
+
+


-
+







		if (end == elt.value.length)
			return			// select all
		setsel(0, 0);			// clear selection
		setsel(1, 0)
		return
	}

	if (srcend) {
		srcend.forEach(function(ie, is) {
	if (syms) {
		syms.forEach(function(sym, is) {
			if (!s) {
				if (is >= start)
					s = is
			} else if (ie <= end) {
			} else if (sym.iend <= end) {
				e = is
			}
		})
	}
	if (!s)
		return
	if (selx[0] != s)
		setsel(0, s)
	if (selx[1] != e)
		setsel(1, e);
	elts = document.getElementsByClassName('_' + s + '_')
	if (elts[0])
		do_scroll(elts[0])	// move the element in the screen
}

// open a new window for file save
function saveas() {
	var	s = srcidx == 0 ? "source" : "src1",
		source = elt_ref[s].value,
		uriContent = "data:text/plain;charset=utf-8," +
				encodeURIComponent(source),

	// create a link for our script to 'click'
		link = document.createElement("a");

	if (abc_fname[srcidx] == "noname.abc")
	elt_ref["s" + srcidx].value =
		elt_ref["s" + srcidx].value =
		link.download =
			abc_fname[srcidx] =
				prompt(texts.fn, abc_fname[srcidx]);
	link.innerHTML = "Hidden Link";
	link.href = uriContent;
			abc_fname[srcidx] = prompt(texts.fn, abc_fname[srcidx])
	link.download = abc_fname[srcidx];
//	link.innerHTML = "Hidden Link";
	link.href = "data:text/plain;charset=utf-8," +
			encodeURIComponent(source);

	// open in a new tab
	link.target = '_blank';
//	link.target = '_blank';

	// when link is clicked call a function to remove it from
	// the DOM in case user wants to save a second file.
	link.onclick = destroyClickedElement;

	// make sure the link is hidden.
	link.style.display = "none";
︙
465
466
467
468
469
470
471
472

473
474

475
476
477

478
479
480
481
482
483
484
485
486

487
488
489
490
491





492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515













































516
517

518
519
520
521
522
523
524
525
526














527
528
529
530
531
532
533
534

535
536
537



538
539

540
541
542
543
544
545

546
547

548
549
550
551
552



553
554
555
556
557
558
559
560

561
562
563
564
565
566







567
568
569

570
571
572
573
574
575









576
577
578
579


580
581
582
583
584
585
586

587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608

609
610
611


612
613
614




615
616
617
618
619
620
621
622
623
624
625
626
627


628
629
630
631
632

633
634
635
636
637
638
639
640
641
642
643
644
645

646
647
648
649
650
651
652

653
654
655

656
657
658

659
660

661
662
663
664
665


666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692

693
694
695
696



697
698
699
700
701
702

703
704
705
706
707
708
709
473
474
475
476
477
478
479

480
481

482
483
484
485
486
487
488
489
490
491
492
493
494

495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
















514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558


559









560
561
562
563
564
565
566
567
568
569
570
571
572
573








574
575


576
577
578


579






580


581





582
583
584








585






586
587
588
589
590
591
592



593






594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614

615
616
617
618















619
620
621

622


623
624
625



626
627
628
629








630

631


632
633





634




635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650

651
652
653

654

655

656


657
658

659


660
661

























662

663
664



665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681







-
+

-
+



+








-
+





+
+
+
+
+








-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+

-
-
+
+
+
-
-
+
-
-
-
-
-
-
+
-
-
+
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+




+
+






-
+



-
-
-
-
-
-
-
-
-
-
-
-
-
-
-



-
+
-
-

+
+
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-

-

-
-
+
+
-
-
-
-
-
+
-
-
-
-









+






-
+


-
+
-

-
+
-
-
+

-

-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
+

-
-
-
+
+
+






+







	var elts = document.getElementsByClassName('_' + i + '_');
	if (elts && elts[0]) {
		if (on)
			do_scroll(elts[0]);
		elts[0].style.fillOpacity = on ? 0.4 : 0
	}
}
function endplay() {
function endplay(repv) {
	if (play.loop) {
		play.abcplay.play(play.si, play.ei, play.a_pe)
		play.abcplay.play(play.si, play.ei)
		return
	}
	play.playing = false;
	play.repv = repv		// repeat variant number for continue

	// redisplay the selection
	selx[0] = selx[1] = 0;
	setsel(0, selx_sav[0]);
	setsel(1, selx_sav[1])
}

// start playing
//	-1: All
//	-1: All (removed)
//	0: Tune
//	1: Selection
//	2: Loop
//	3: Continue
function play_tune(what) {
    var	i, si, ei, elt,
	C = abc2svg.C,
	tunes = abc.tunes,
	s = elt_ref.source.value

	if (play.playing) {
		if (!play.stop) {
			play.stop = -1;
			play.abcplay.stop()
		}
		return
	}

	// search a playing event from a source index
	function get_se(si) {			// get highest starting event
	    var	i, s, tim,
		sih = 1000000,
		pa = play.a_pe,
		ci = 0

		if (si <= pa[0][0])
			return 0
		if (si >= pa[pa.length - 1][0])
			return pa.length

		si = go_note(si);

		i = pa.length
		while (--i > 0) {
	// search a symbol to play

	function gnrn(sym, loop) {	// go to the next real note (not tied)
	    var	i
		while (1) {
			switch (sym.type) {
			case C.NOTE:
				i = sym.nhd + 1
				while (--i >= 0) {
					if (sym.notes[i].ti2)
						break
				}
				if (i < 0)
					return sym
				break
			case C.REST:
			case C.GRACE:
				return sym
			case C.BLOCK:
				switch (sym.subtype) {
				case "midictl":
				case "midiprog":
					return sym
				}
				break
			}
			if (!sym.ts_next) {
				if (!loop)
					return gprn(sym, 1)
				return sym
			}
			sym = sym.ts_next
		}
		// not reached
	}
	function gprn(sym, loop) {	// go to the previous real note (not tied)
	    var	i
		while (1) {
			switch (sym.type) {
//			case C.BAR:
//fixme: there may be a right repeat!
//			break
			case C.NOTE:
				i = sym.nhd + 1
				while (--i >= 0) {
			s = pa[i][0]
			if (s < si)
					if (sym.notes[i].ti2)
				continue
			if (s == si) {
				ci = i
				break
			}
			if (s < sih) {
				ci = i;
				sih = s
			}
						break
				}
				if (i < 0)
					return sym
				break
			case C.REST:
			case C.GRACE:
				return sym
			case C.BLOCK:
				switch (sym.subtype) {
				case "midictl":
				case "midiprog":
					return sym
				}
		}

		// go to the first voice at this time
		if (ci < pa.length) {
			tim = pa[ci][1]
			while (--ci >= 0) {
				if (pa[ci][1] != tim)
					break
				break
			}
		}
		return ci + 1
			if (!sym.ts_prev) {
				if (!loop)
					return gnrn(sym, 1)
	} // get_se()

				return sym
	function get_ee(si, i) {		// get lowest ending event
	    var	s, tim,
		sil = 0,
		pa = play.a_pe,
		ci = 0

			}
		if (si <= pa[0][0])
			return 0
			sym = sym.ts_prev
		if (si >= pa[pa.length - 1][0])
			return pa.length

		si = go_note(si)

		}
		// not reached
	}
		for ( ; i < pa.length; i++) {
			s = pa[i][0]
			if (s > si)
				continue
			if (s == si) {
				ci = i
				break
			}

			if (s > sil) {
				ci = i;
				sil = s
			}
		}

	function gsot(si) {	// go to the first playable symbol of a tune
		return gnrn(syms[si].p_v.sym)
	}
	function get_se(si) {			// get the starting symbol
	    var	sym = syms[si]

		while (!sym.seqst)
		// go to after the last voice at this time
		if (ci > 0) {
			tim = pa[ci++][1]
			sym = sym.ts_prev
			for ( ; ci < pa.length; ci++) {
				if (pa[ci][1] != tim)
					break
			}
		}
		return ci
		return sym
	} // get_se()

	function get_ee(si) {			// get the ending symbol
	    var	sym = syms[si]

		while (sym.ts_next && !sym.ts_next.seqst)
			sym = sym.ts_next
		return sym
	} // get_ee()

	// start playing
	function play_start(si, ei) {
		if (!si)
			return
		selx_sav[0] = selx[0];		// remove the colors
		selx_sav[1] = selx[1];
		setsel(0, 0);
		setsel(1, 0);

		play.stop = 0;
		play.abcplay.play(si, ei, play.a_pe)	// start playing
		play.abcplay.play(si, ei, play.repv)
	}

	// play tune()
    var	abc, i, si, ei, elt, tim,
	s = elt_ref.source.value,
	ctxMenu = document.getElementById("ctxMenu");

	// search a note/rest in the source from a selection
	function go_note(si) {
		for (var i = si; ; i++) {
			if (!s[i])
				return si
			if ('ABCDEFGabcdefg[^_='.indexOf(s[i]) >= 0)
				break
		}
		return i
	}

	ctxMenu.style.display = "none";	// remove the play menu

	play.playing = true;
	if (!play.a_pe) {		// if no playing event
	if (tunes.length) {		// if new display
		user.img_out = null	// get the schema and stop SVG generation
		user.get_abcmodel = play.abcplay.add // inject the model in the play engine

		// generate the play data of all tunes
		while (1) {
		abc = new abc2svg.Abc(user);

		play.abcplay.clear();
			elt = tunes.shift()
			if (!elt)
				break
			play.abcplay.add(elt[0], elt[1])
		abc.tosvg("play", "%%play")
		try {
			abc.tosvg(abc_fname[0], s)
		} catch(e) {
			alert(e.message + '\nabc2svg tosvg bug - stack:\n' + e.stack);
			play.playing = false;
			play.a_pe = null
			return
		}
		play.a_pe = play.abcplay.clear(); // keep the playing events

		play.si = play.ei = play.stop = 0;
		play.loop = false
		play.si = play.ei = null
		play.stop = 0
	}

	// play all
	if (what < 0) {
		play.loop = false;
		play.loop = false
		play.si = 0;
		play.ei = play.a_pe.length;
		play_start(play.si, play.ei)
		return
	}

	// if loop again
	if (what == 2 && play.loop) {
		play_start(play.si, play.ei)
		return
	}

	// get the starting and ending play indexes, and start playing

	if (what == 3 && play.stop > 0) {	// if stopped and continue
		play_start(get_se(play.stop), play.ei)
		return
	}
	if (what != 0 && selx[0] && selx[1]) {	// if full selection
		si = get_se(selx[0]);
		ei = get_ee(selx[1], si)
		ei = get_ee(selx[1])
	} else if (what != 0 && selx[0]) {	// if selection without end
		si = get_se(selx[0]);
		i = s.indexOf('\nX:', selx[0]);
		ei = null
		ei = i < 0 ? play.a_pe.length : get_ee(i, si)
	} else if (what != 0 && selx[1]) {	// if selection without start
		i = s.lastIndexOf('\nX:', selx[1]);
		si = gsot(selx[1])
		si = i < 0 ? 0 : get_se(i);
		ei = get_ee(selx[1], si)
		ei = get_ee(selx[1])
	} else {				// no selection => tune
		si = play.click.svg.getElementsByClassName('abcr');
		elt = play.click.svg		// (dummy)
		for (i = 0; ; i++) {
			if (!si[i]) {
		si = elt.getElementsByClassName('abcr') // symbols in the SVG
		if (!si.length) {
				elt = null
				break
			}
			if (si[i].parentNode == elt.parentNode)
				continue		// same container
			elt = si[i];
			ei = elt.parentNode.getBoundingClientRect()
			if (ei.y < play.click.Y
			 && ei.y + ei.height > play.click.Y)
				break
		}
		i = elt ? Number(elt.getAttribute('class').slice(6, -1)) : 0;
		si = ei = 0
		if (s[0] == 'X' && s[1] == ':')
			si = 1
		while (1) {		// search the start and end of the tune
			ei = s.indexOf('\nX:', ei)
			if (ei < 0 || ei > i)
				break
			si = s.indexOf('\nK:', ++ei)
			if (si < 0)
				break
			ei = s.indexOf('\n', si + 1) + 1
		}
		if (si <= 0) {
			play.playing = false
			return		// no tune!
			return			// not in a tune!
		}

		si = get_se(si);
		ei = ei < 0 ? play.a_pe.length : get_ee(ei, si)
		i = Number(si[0].getAttribute('class').slice(6, -1))
		si = gsot(i)
		ei = null
	}

	if (what != 3) {		// if not continue
		play.si = si;
		play.ei = ei;
		play.loop = what == 2
		play.repv = 0
	}

	play_start(si, ei)
}

// set the version and initialize the playing engine
function edit_init() {
︙
739
740
741
742
743
744
745



746













747

748
749
750
751
752
753
754
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734

735
736
737
738
739
740
741
742







+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
-
+







		if (v) {
			elt_ref.source.style.fontSize =
				elt_ref.src1.style.fontSize =
					v + "px";
			document.getElementById("fontsize").value =
					Number(v)
		}

		// set the language
		// if not defined, get the one of the navigator
		v = storage(true, "lang");
		if (!v) {
			v = (navigator.languages ?
				navigator.languages[0] :
				navigator.language).split('-')[0]
			switch (v) {
			case "de":
			case "en":
			case "fr":
			case "it": break
			case "pt": v = "pt_BR"; break
			default: v = "en"; break
			}
		}
		loadlang(v || 'en', true)
		loadlang(v, true)
	}

	document.getElementById("abc2svg").innerHTML =
		'abc2svg-' + abc2svg.version + ' (' + abc2svg.vdate + ')'

	// keep references on the page elements
	var a = ["diverr", "source", "src1", "s0", "s1", "target"]
︙
775
776
777
778
779
780
781
782

783
784
785
786
787
788
789
763
764
765
766
767
768
769

770
771
772
773
774
775
776
777







-
+







		setsel(0, selx_sav[0]);
		setsel(1, selx_sav[1])
	}

	// if playing is possible, load the playing script
	if (window.AudioContext || window.webkitAudioContext
	 || navigator.requestMIDIAccess) {
		abc2svg.loadjs("play-@MAJOR@.js", function() {
		abc2svg.loadjs("snd-@MAJOR@.js", function() {
			play.abcplay = AbcPlay({
					onend: endplay,
					onnote:notehlight,
					});
			document.getElementById("playdiv1").style.display =
				document.getElementById("playdiv3").style.display =
				document.getElementById("playdiv4").style.display =
︙
815
816
817
818
819
820
821
822

823
824
825
826
827
828
829
803
804
805
806
807
808
809

810
811
812
813
814
815
816
817







-
+








			// otherwise, display the play menu
			play.click = {	// keep the click references for 'play tune'
				svg: elt,
				Y: evt.pageY
			}

			var ctxMenu = document.getElementById("ctxMenu");
			ctxMenu = document.getElementById("ctxMenu");
			ctxMenu.style.display = "block";
			x = evt.pageX - elt_ref.target.parentNode.offsetLeft
					+ elt_ref.target.parentNode.scrollLeft;
			y = evt.pageY + elt_ref.target.parentNode.scrollTop;
			ctxMenu.style.left = (x - 30) + "px";
			ctxMenu.style.top = (y - 10) + "px"
			return false
︙
Changes to editor/edit.xhtml.
︙
14
15
16
17
18
19
20
21

22
23
24
25
26
27
28
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28







-
+







<!-- left -->
<div id="dleft">

<!-- menu bar -->
    <ul id="nav">
	<li class="dropbutton"><label id="f">File</label>
		<ul class="droplinks">
		    <li><input type="file" id="abcfile" accept="text/*"
		    <li><input type="file" id="abcfile" accept="*"
				style="display:none" onchange="loadtune()"/>
			<label id="fl" for="abcfile">Load file</label>
		    </li>
		    <li><label id="saveas">Save file</label></li>
		</ul>
	</li>
	<li class="dropbutton"><label id="pr">Preferences</label>
︙
100
101
102
103
104
105
106

107

108
109
110
111
112
113
114
100
101
102
103
104
105
106
107

108
109
110
111
112
113
114
115







+
-
+








<!-- right - needed for the scrollbars -->
<div id="dright">

<!-- context menu -->
<div id="ctxMenu" title="Play">
	<ul class="droplinks">
<!-- 'All' does not work anymore -->
	<li id="cmpa" onclick="play_tune(-1)">All</li>
	<li id="cmpa" onclick="play_tune(-1)" style="display:none">All</li>
	<li id="cmpt" onclick="play_tune(0)">Tune</li>
	<li id="cmps" onclick="play_tune(1)">Selection</li>
	<li id="cmpl" onclick="play_tune(2)">Loop</li>
	<li id="cmpc" onclick="play_tune(3)">Continue</li>
	</ul>
</div>

︙
138
139
140
141
142
143
144
145

146
147
148

149
150
151
152

153
154
155
156
157
139
140
141
142
143
144
145

146
147
148
149
150
151
152
153

154


155
156
157







-
+



+



-
+
-
-



</ul>
</div>

<!-- about popup window -->
<div id="about" class="popup" style="width: 350px">
<div class="close" onclick="popshow('about')">x</div>
<p id="abc2svg">abc2svg</p>
<p>Copyright &#x00A9; 2016-2018 Jean-François Moine</p>
<p>Copyright &#x00A9; 2016-2019 Jean-François Moine</p>
</div>

<!-- javascript must be enabled -->
<noscript>
<div id="okjs" class="popup" style="width:50%;background:#ff8080;visibility:visible">
Please enable Javascript.
</div>
<script>
</noscript>
document.getElementById('okjs').style.visibility = 'hidden'
</script>

</body>
</html>
Changes to err-de.js.
︙
12
13
14
15
16
17
18
19

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49


50
51
52
53
54
55
56
57
58
59

60
61
62
63
64
65

66
67
68

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83


84
85
86
87

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

107
108
109
110
111
112
113
114
115
116
12
13
14
15
16
17
18

19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

36
37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

66
67
68

69
70
71
72
73
74
75
76
77
78
79
80
81
82


83
84
85
86
87

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

111
112
113
114
115
116
117







-
+
















-



-









+
+










+





-
+


-
+













-
-
+
+



-
+



















+


-









user.textrans = {
    "+: lyric without music": "+: Liedtext ohne Musik",
	"+: symbol line without music": "+: Symbolzeile ohne Musik",
    "+: without previous info field": "+: ohne vorhergehends Info-Feld",
    "'{' in grace note": "'{' in Verzierungsnote",
    "'}', ')' or ']' missing in %%staves": "'}', ')' oder ']' fehlt in  %%staves",
    "'}', ')' or ']' missing in %%score": "'}', ')' oder ']' fehlt in  %%score",
    "',' lacking in annotation '@x,y'": "',' fehlt in Beschriftung  '@x,y'",
	"'$1' is not a note": "'$1' is keine Note",
    "'$1:' line ignored": "'$1:' Zeile ignoreirt",
    "$1: inside tune - ignored": "$1: innerhalb der Stimme ignoriert",
    "!$1! must be on a note": "!$1! muss an einer Note stehen",
    "!$1! must be on a note or a rest": "!$1! muss an einer Note oder Pause stehen",
    "!$1! must be on the last of a couple of notes": "!$1! muss an der letzten Notengruppe stehen",
    "!beamon! must be on a bar": "!beamon! muss an einem Taktstrich stehen",
    "!xstem! must be on a note": "!xstem! muss an einer Note stehen",
    "%%deco: abnormal h/wl/wr value '$1'": "%%deco: falscher Wert '$1'  für h/wl/wr",
    "%%deco: bad C function index '$1'": "%%deco: falscher C Funktionsindex '$1'",
    "%%deco: bad C function value '$1'": "%%deco: falscher C Funtionswert '$1'",
    "%%deco: cannot have a negative value '$1'": "%%deco: '$1' kann keinen egativen Wert haben",
    "%%multicol end without start": "%%multicol end ohne start",
    "%%multicol new without start": "%%multicol new ohne stand",
    "%%repeat cannot start a tune": "%%repeat kann nicht am Anfang einer Stimme stehen",
    "%%select ignored": "%%select ignoriert",
    "%%staffwidth too big": "%%staffwidth zu groß",
    "%%staffwidth too small": "%%staffwidth zu klein",
    "%%tune not treated yet": "%%tune noch nicht behandelt",
    "%%voice ignored": "%%voice ignoriert",
    "%%vskip cannot be negative": "%%vskip kann nicht negativ sein",
    "Bad !xstem!": "!xstem! fehlerhaft",
    "Bad %%setbarnb value": "fehlerhaft Wert für %%setbarnb",
    "Bad %%staff number $1 (cur $2, max $3)": "falsche %%staff Nummer $1 (cur $2, max $3)",
    "Bad %%staff value '$1'": "fehlerhafte %%staff Nummer '$1'",
    "Bad %%stafflines value": "fehlerhafter Wert für %%stafflines",
    "Bad %%staffscale value": "fehlerhafter Wert für %%staffscale",
    "Bad char '$1' in M:": "fehlerhaftes Zeichen '$1' in M:",
    "Bad character '$1'": "fehlerhaftes Zeichen '$1'",
	"Bad character in grace note sequence":
				"fehlerhaftes Zeichen in Sequenz von Vorschlagnoten",
	"Bad controller number in %%MIDI": "Bad controller number in %%MIDI",
	"Bad controller value in %%MIDI": "Bad controller value in %%MIDI",
    "Bad duration '$1' in M:": "fehlerhafte Dauer '$1' in M:",
    "Bad integer value": "fehlerhafte Ganzzahl",
	"Bad instr= value": "fehlerhafter Wert für instr=",
    "Bad L: value": "Fehlerhafter Wert für  L:",
    "Bad note in %%map": "fehlerhafte Note in %%map",
    "Bad page width": "fehlerhafte Seitenbreite",
	"Bad program in %%MIDI": "falsches Program in %%MIDI",
    "Bad scale value in %%font": "fehlerhafter Wert für scale in %%font",
	"Bad tempo value": "Bad tempo value",
    "Bad tie": "fehlerhafter Haltebogen",
    "Bad transpose value": "fehlerhafter Wert für transpose",
    "Bad user character '$1'": "falsches benutzerdefiniertes Zeichen '$1' in U:/%%user",
    "Bad value '$1' in %%linebreak - ignored": "fehlerhafter Wert '$1' in %%linebreak - ignoriert",
    "Bad value in $1": "fehlerhafter Wert in $1",
    "Bad voice ID in %%staves": "Fehlerhafte ID für Stimme in %%staves",
    "Bad voice ID in %%score": "Fehlerhafte ID für Stimme in %%score",
    "Bar in repeat sequence": "Taktstrich in Wiederholungssequenz",
    "Cannot have !$1! on a head": "!$1! im Kopfbereich nicht erlaubt",
    "Cannot have %%fullsvg inside a tune": "'%%fullsvg' innerhalb einer Stimme nicht erlaubt",
    "Cannot have '$1' inside a tune": "'$1' innerhalb einer Stimme nicht erlaubt",
    "Cannot have a bar in grace notes": "Taktstrich in Vorschlagnote nicht erlaubt",
	  "Cannot have a broken rhythm in grace notes": "gebrochener Rythmus in Ziernoten nicht erlaubt ",
    "Cannot have V:* in tune body": "V:* im Notenbereich nicht erlaubt",
    "Cannot read file '$1'": "File '$1' kann nicht gelesen werden",
    "Decoration ignored": "Dekoration ignoriert",
    "Empty line in tune header - ignored": "Leerzeile in Kopfbereich - ignoriert",
    "Erroneous end of voice overlay": "Fehlerhaftes Ende einer Stimme in überlagerten Stimmen",
    "Incorrect 1st value in %%repeat": "erster Wert fehlerhaft in %%repeat",
    "Incorrect 2nd value in %%repeat": "zweiter Wert fehlerhaft in %%repeat",
    "Invalid 'r' in tuplet": "'r' fehlerhaft in Tuplet",
    "Invalid decoration '$1'": "fehlerhafte Dekoration '$1'",
    "Invalid note duration $1": "Fehlerhafte Notendekoration '$1'",
    "Lack of ']'": "']' fehlt",
    "Lack of ending $1 in U:/%%user": "Ende von $1 fehlt in  U:/%%user",
    "Lack of starting ! or \" in U: / %%user": "! or \" fehlt in U: / %%user",
	"Lack of starting [, ! or \" in U: / %%user":
				"[, ! or \" fehlt in U: / %%user",
    "Line split problem - adjust maxshrink and/or breaklimit": "Probleme beim Aufteilen der Zeile - justiere maxshrink und/oder breaklimit",
    "Line too much shrunk $1 $2 $3": "Zeile zu stark geschrumpft $1 $2 $3",
    "Line underfull ($1pt of $2pt)": "Zeile nicht voll genug ($1pt of $2pt)",
    "Misplaced '$1' in %%staves": "'$1' fehlerhaft platziert in %%staves",
    "Misplaced '$1' in %%score": "'$1' fehlerhaft platziert in %%score",
    "Misplaced dot": "fehlerhaft platzierter Punkt",
	"Mix of old and new transposition syntaxes":
				"Mix von alter und neuer Transponierungs-syntax",
    "No $1 after %%$2": "kein $1 nach %%$2",
    "No </defs> in %%beginsvg sequence": "kein </defs> in %%beginsvg squenz",
    "No </style> in %%beginsvg sequence": "kein </style> in %%beginsvg sequenz",
    "No accidental after 'exp'": "kein Vorzeichen nach 'exp'",
    "No end of decoration": "kein Ende der Dekoration",
    "No end of grace note sequence": "kein ende der Vorschlagnoten",
    "No end of guitar chord": "kein Ende des Akkordsymbols",
    "No end of repeat string": "kein Ende des Wiederholungszeichens",
    "No end of tuplet": "kein Ende des Tuplets",
    "No end of tuplet in this music line":
			"Kein ende des Tuplets in dieser Zeile",
	"No end of voice overlay": "kein Ende der Stimmüberlagerung",
    "No function for decoration '$1'": "keine Funktion für Dekoration '$1'",
    "No note before '-'": "keine Note vor '-'",
    "No note before '<'": "keine Note vor '<'",
    "No note in voice overlay": "keine Note in Stimmüberlagerung",
//	"Non standard note duration $1": "??",
    "No read_file support": "Einlesen von Files nicht unterstützt",
    "No start of !$1!": "kein Start von  !$1!",
    "Not a note in grace note sequence":  "Keine Note in Sequenz von Vorschlagnoten",
    "Not an ASCII character": "kein ASCII - Zeichen",
    "Not enough measure bars for lyric line": "nicht genug Takte für Liedtextzeile",
	"Not enough measure bars for symbol line":
				"Nicht genügend Takte für eine Symbolzeile",
    "Not enough measures for %%repeat": "nicht genug Takte für %%repeat",
    "Not enough parameters in %%map":  "nicht genug Parameter in  %%map",
    "Not enough symbols after repeat sequence":  "nicht genug Symbole nach einer Wiederholungssequenz",
︙
Changes to err-fr.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14


15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12


13
14
15
16
17
18
19
20
21












-
-
+
+







//JS abc2svg - french translation of the error codes
//	traduction des codes d'erreur en français
//	par Jean-François Moine
// Appelez ce fichier dans la ligne de commande pour avoir
// les messages d'erreur en français

user.textrans = {
	"+: lyric without music": "+: paroles sans musique",
	"+: symbol line without music":
			"+: plus de musique pour s:",
	"+: without previous info field": "Pas de ligne info avant +:",
	"'{' in grace note": "'{' dans notes d'agrément",
	"'}', ')' or ']' missing in %%staves":
				"Il manque '}', ')' ou ']' dans %%staves",
	"'}', ')' or ']' missing in %%score":
				"Il manque '}', ')' ou ']' dans %%score",
	"',' lacking in annotation '@x,y'":
				"Il manque une ',' dans l'annotation '@x,y'",
	"'$1' is not a note": "'$1' n'est pas une note",
	"'$1:' line ignored": "'$1:' ligne ignorée",
	"$1: inside tune - ignored": "$1: en milieu de morceau - ignoré",
	"!$1! must be on a note": "!$1! doit être sur une note",
	"!$1! must be on a note or a rest":
︙
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

48
49
50
51
52


53
54
55
56
57
58
59
60
61
62

63
64
65
66
67
68
69

70
71
72
73

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93


94
95
96
97
98

99
100
101
102
103
104
105
31
32
33
34
35
36
37

38
39
40

41
42
43
44

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71
72


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90



91
92
93
94
95
96

97
98
99
100
101
102
103
104







-



-




-
+





+
+










+






-
+


-
-
+

















-
-
-
+
+




-
+







	"%%deco: bad C function value '$1'":
				"%%deco: mauvaise valeur de fonction '$1'",
	"%%deco: cannot have a negative value '$1'":
				"%%deco: '$1' ne peut pas avoir de valeur négative",
	"%%multicol end without start": "%%multicol end sans start",
	"%%multicol new without start": "%%multicol new sans start",
	"%%repeat cannot start a tune": "%%repeat ne peut pas être en début de morceau",
	"%%select ignored": "%%select ignoré",
	"%%staffwidth too big": "%%staffwidth trop grand",
	"%%staffwidth too small": "%%staffwidth trop petit",
	"%%tune not treated yet": "%%tune n'est pas encore traité",
	"%%voice ignored": "%%voice ignoré",
	"%%vskip cannot be negative": "%%vskip ne peut pas être négatif",
	"Bad !xstem!": "!xstem! erroné",
	"Bad %%setbarnb value": "Valeur incorrecte dans %%setbarnb",
	"Bad %%staff number $1 (cur $2, max $3)":
				"Numéro $1 %%staff incorrect (cur $2, max $3)",
				"Numéro %%staff $1 incorrect (cur $2, max $3)",
	"Bad %%staff value '$1'": "Valeur incorrecte '$1' dans %%staff",
	"Bad %%stafflines value": "Valeur incorrecte dans %%stafflines",
	"Bad %%staffscale value": "Valeur incorrecte dans %%staffscale",
	"Bad char '$1' in M:": "Mauvais caractère '$1' dans M:",
	"Bad character '$1'": "Mauvais caractère '$1'",
	"Bad character in grace note sequence":
				"Caractère incorrect dans notes d'agrément",
	"Bad controller number in %%MIDI": "Mauvais numéro de controleur dans %%MIDI",
	"Bad controller value in %%MIDI": "Mauvaise valeur de controle dans %%MIDI",
	"Bad duration '$1' in M:": "Durée '$1' incorrecte dans M:",
	"Bad integer value": "Mauvaise valeur entière",
	"Bad instr= value": "Mauvaise valeur de instr=",
	"Bad L: value": "Mauvaise valeur pour L:",
	"Bad note in %%map": "Note incorrecte dans %%map",
	"Bad page width": "Mauvaise largeur de page",
	"Bad program in %%MIDI": "Mauvais 'program' dans %%MIDI",
	"Bad scale value in %%font": "Mauvaise échelle dans %%font",
	"Bad tempo value": "Mauvaise valeur de tempo",
	"Bad tie": "Mauvaise liaison de prolongation",
	"Bad transpose value": "Mauvaise valeur de transposition",
	"Bad user character '$1'": "Caractère illégal '$1' dans U:/%%user",
	"Bad value '$1' in %%linebreak - ignored":
				"Valeur incorrecte '$1' dans %%linebreak - ignorée",
	"Bad value in $1": "Mauvaise valeur dans $1",
	"Bad voice ID in %%staves": "Mauvaise identité de voix dans %%staves",
	"Bad voice ID in %%score": "Mauvaise identité de voix dans %%score",
	"Bar in repeat sequence": "Barre dans la séquence à répéter",
	"Cannot have !$1! on a head": "!$1! ne peut pas être sur une tête de note",
	"Cannot have %%fullsvg inside a tune":
				"'%%fullsvg' ne peut pas être dans un morceau",
	"Cannot have '$1' inside a tune": "'$1' ne peut pas être dans un morceau",
	"Cannot have a bar in grace notes":
			"Il ne peut pas y avoir de barre dans les notes d'agrément",
	"Cannot have a broken rhythm in grace notes":
			"Il ne peut pas y avoir '<' ou '>' dans les notes d'agrément",
	"Cannot have V:* in tune body":
			"Il ne peut pas y avoir V:* dans le corps du morceau",
	"Cannot read file '$1'": "Fichier '$1' inexistant",
	"Decoration ignored": "Décoration ignorée",
	"Empty line in tune header - ignored":
				"Ligne vide dans en-tête de morceau - ignorée",
	"Erroneous end of voice overlay": "Fin de superposition de voix erronée",
	"Incorrect 1st value in %%repeat": "Première valeur incorrecte dans %%repeat",
	"Incorrect 2nd value in %%repeat": "Deuxième valeur incorrecte dans %%repeat",
	"Invalid 'r' in tuplet": "'r' invalide dans le tuplet",
	"Invalid decoration '$1'": "Décoration '$1' invalide",
	"Invalid note duration $1": "Durée de note $1 invalide",
	"Lack of ']'": "']' manquant",
	"Lack of ending $1 in U:/%%user": "Pas de $1 terminant U:/%%user",
	'Lack of starting ! or " in U: / %%user':
				'U: / %%user ne commence pas par ! ou "',
	'Lack of starting [, ! or " in U: / %%user':
				'U: / %%user ne commence pas par [, ! ou "',
	"Line split problem - adjust maxshrink and/or breaklimit":
		"Problème de découpe en lignes - ajuster maxshrink et/ou breaklimit",
	"Line too much shrunk $1 $2 $3": "Ligne trop compressée $1 $2 $3",
	"Line underfull ($1pt of $2pt)": "Trop d'espacement ($1pt of $2pt)",
	"Misplaced '$1' in %%staves": "'$1' mal placé dans %%staves",
	"Misplaced '$1' in %%score": "'$1' mal placé dans %%score",
	"Misplaced dot": "Point mal placé",
	"Mix of old and new transposition syntaxes":
			"Mélange des syntaxes de transposition ancienne et nouvelle",
	"No $1 after %%$2": "Pas de $1 après %%$2",
	"No </defs> in %%beginsvg sequence":
				"Pas de </defs> dans séquence %%beginsvg",
	"No </style> in %%beginsvg sequence":
︙
113
114
115
116
117
118
119

120
121
122
123
124
125
126
127
128
129
130
112
113
114
115
116
117
118
119
120
121


122
123
124
125
126
127
128







+


-
-







	"No end of tuplet in this music line":
				"Pas de fin de tuplet dans cette ligne de musique",
	"No end of voice overlay": "Pas de fin de superposition de voix",
	"No function for decoration '$1'": "Pas de fonction pour décoration '$1'",
	"No note before '-'": "Pas de note devant '-'",
	"No note before '<'": "Pas de note devant '<'",
	"No note in voice overlay": "Pas de note dans voix superposée",
	"Non standard note duration $1": "Durée de note $1 non standard"
	"No read_file support": "La fonction read_file n'existe pas",
	"No start of !$1!": "Pas de début pour !$1!",
	"Not a note in grace note sequence":
				"Caractère incorrect dans notes d'agrément",
	"Not an ASCII character": "Ce n'est pas un caractère ASCII",
	"Not enough measure bars for lyric line":
				"Pas assez de barres pour la ligne de paroles",
	"Not enough measure bars for symbol line":
				"Pas assez de barres pour ligne s:",
	"Not enough measures for %%repeat":
				"Pas assez de mesures pour %%repeat",
︙
Changes to err-it.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14


15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12


13
14
15
16
17
18
19
20
21












-
-
+
+







//JS abc2svg - Italian translation of the error codes
//	Traduzione dei codici di errore in italiano
//	di Guido Gonzato, guido . gonzato at gmail . com
// Richiamare questo file da linea di comando per avere 
// i messaggi di errore in italiano.

user.textrans = {
	"+: lyric without music": "+: parole senza musica",
	"+: symbol line without music":
			"+: linea di simboli senza musica",
	"+: without previous info field": "Nessun campo di informazioni prima di +:",
	"'{' in grace note": "'{' in nota di abbellimento",
	"'}', ')' or ']' missing in %%staves":
				"'}', ')' o ']' mancanti in %%staves",
	"'}', ')' or ']' missing in %%score":
				"'}', ')' o ']' mancanti in %%score",
	"',' lacking in annotation '@x,y'":
				"Manca una ',' nell'annotazione '@x,y'",
	"'$1' is not a note": "'$1' non è una nota",
	"'$1:' line ignored": "'$1:' linea ignorata",
	"$1: inside tune - ignored": "$1: nel corpo del brano - ignorato",
	"!$1! must be on a note": "!$1! deve stare su di una nota",
	"!$1! must be on a note or a rest":
︙
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52


53
54
55
56
57
58
59
60
61
62

63
64
65
66
67
68
69

70
71
72
73

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93


94
95
96
97
98

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126
127
128
129
31
32
33
34
35
36
37

38
39
40

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

70
71
72


73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90



91
92
93
94
95
96

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120


121
122
123
124
125
126
127







-



-










+
+










+






-
+


-
-
+

















-
-
-
+
+




-
+




















+


-
-







	"%%deco: bad C function value '$1'":
				"%%deco: valore errato per la funzione C '$1'",
	"%%deco: cannot have a negative value '$1'":
				"%%deco: '$1' non può avere un valore negativo",
	"%%multicol end without start": "%%multicol end senza start",
	"%%multicol new without start": "%%multicol new senza start",
	"%%repeat cannot start a tune": "%%repeat non può stare all'inizio del brano",
	"%%select ignored": "%%select ignorato",
	"%%staffwidth too big": "%%staffwidth troppo grande",
	"%%staffwidth too small": "%%staffwidth troppo piccolo",
	"%%tune not treated yet": "%%tune non ancora gestito",
	"%%voice ignored": "%%voice ignorato",
	"%%vskip cannot be negative": "%%vskip non può essere negativo",
	"Bad !xstem!": "!xstem! errato",
	"Bad %%setbarnb value": "Valore errato in %%setbarnb",
	"Bad %%staff number $1 (cur $2, max $3)":
				"Numero $1 in %%staff errato (attuale $2, max $3)",
	"Bad %%staff value '$1'": "Valore errato '$1' in %%staff",
	"Bad %%stafflines value": "Valore errato in %%stafflines",
	"Bad %%staffscale value": "Valore errato in %%staffscale",
	"Bad char '$1' in M:": "Carattere errato '$1' in M:",
	"Bad character '$1'": "Carattere errato '$1'",
	"Bad character in grace note sequence":
				"Carattere errato nelle note di abbellimento",
	"Bad controller number in %%MIDI": "Numero errato del controller in %%MIDI",
	"Bad controller value in %%MIDI": "Valore errato del controller in %%MIDI",
	"Bad duration '$1' in M:": "Durata '$1' errata in M:",
	"Bad integer value": "Valore intero errato",
	"Bad instr= value": "Valore errato di instr=",
	"Bad L: value": "Valore errato per L:",
	"Bad note in %%map": "Nota errata in %%map",
	"Bad page width": "Larghezza di pagina errata",
	"Bad program in %%MIDI": "Numero di programma non valido in %%MIDI",
	"Bad scale value in %%font": "Valore di scala non valido in %%font",
	"Bad tempo value": "Bad tempo value",
	"Bad tie": "Legatura non valida",
	"Bad transpose value": "Valore non valido per la trasposizione",
	"Bad user character '$1'": "Carattere illegale '$1' in U:/%%user",
	"Bad value '$1' in %%linebreak - ignored":
				"Valore errato '$1' in %%linebreak - ignorato",
	"Bad value in $1": "Valore errato in $1",
	"Bad voice ID in %%staves": "ID di voce non valido in %%staves",
	"Bad voice ID in %%score": "ID di voce non valido in %%score",
	"Bar in repeat sequence": "Stanghetta in sequenza ripetuta",
	"Cannot have !$1! on a head": "!$1! non è consentito su una nota",
	"Cannot have %%fullsvg inside a tune":
				"'%%fullsvg' non è consentito entro il brano",
	"Cannot have '$1' inside a tune": "'$1' non è consentito entro il brano",
	"Cannot have a bar in grace notes":
			"Stanghette non consentite nelle note di abbellimento",
	"Cannot have a broken rhythm in grace notes":
			"'Broken rhythm' non consentito nelle note di abbellimento",
	"Cannot have V:* in tune body":
			"V:* non consentito nel corpo del brano",
	"Cannot read file '$1'": "File '$1' inesistente",
	"Decoration ignored": "Decorazione ignorata",
	"Empty line in tune header - ignored":
				"Linea vuota nella testata - ignorata",
	"Erroneous end of voice overlap": "Fine di voice overlay errata",
	"Incorrect 1st value in %%repeat": "Primo valore in %%repeat errato",
	"Incorrect 2nd value in %%repeat": "Secondo valore in %%repeat errato",
	"Invalid 'r' in tuplet": "'r' non valido in gruppo irregolare",
	"Invalid decoration '$1'": "Decorazione '$1' non valida",
	"Invalid note duration $1": "Durée de note $1 non valida",
	"Lack of ']'": "']' mancante",
	"Lack of ending $1 in U:/%%user": "Manca $1 alla fine di U:/%%user",
	'Lack of starting ! or " in U: / %%user':
				'U: / %%user non inizia con ! o "',
	'Lack of starting [, ! or " in U: / %%user':
				'U: / %%user non inizia con [, ! o "',
	"Line split problem - adjust maxshrink and/or breaklimit":
		"Problema di linea divisa - modificare maxshrink e/o breaklimit",
	"Line too much shrunk $1 $2 $3": "Linea troppo compressa $1 $2 $3",
	"Line underfull ($1pt of $2pt)": "Linea troppo vuota ($1pt di $2pt)",
	"Misplaced '$1' in %%staves": "'$1' in posizione errata in %%staves",
	"Misplaced '$1' in %%score": "'$1' in posizione errata in %%score",
	"Misplaced dot": "Punto in posizione errata",
	"Mix of old and new transposition syntaxes":
			"Sintassi di trasposizione vecchia e nuova allo stesso tempo",
	"No $1 after %%$2": "Manca $1 dopo %%$2",
	"No </defs> in %%beginsvg sequence":
				"Manca </defs> nella sequenza %%beginsvg",
	"No </style> in %%beginsvg sequence":
				"manca </style> nella sequenza %%beginsvg",
	"No accidental after 'exp'": "Manca l'alterazione dopo 'exp'",
	"No end of decoration": "Decorazione non terminata",
	"No end of grace note sequence": "Sequenza di note di abbellimento non terminata",
	"No end of guitar chord": "Accordo di accompagnamento non terminato",
	"No end of repeat string": "Stringa di ripetizione non terminata",
	"No end of tuplet": "Gruppo irregolare non terminato",
	"No end of tuplet in this music line":
				"Gruppo irregolare non terminato in questa linea",
	"No function for decoration '$1'": "Funzione inesistente per la decorazione '$1'",
	"No note before '-'": "Nota mancante davanti a '-'",
	"No note before '<'": "Nota mancante davanti a '<'",
	"No note in voice overlay": "Nota mancante in voce sovrapposta (overlay)",
//	"Non standard note duration $1": "??",
	"No read_file support": "La funzione read_file non è supportata",
	"No start of !$1!": "Manca l'inizio di !$1!",
	"Not a note in grace note sequence":
				"Carattere errato nelle note di abbellimento",
	"Not an ASCII character": "Non è un carattere ASCII",
	"Not enough measure bars for lyric line":
				"Battute insufficienti per le parole",
	"Not enough measure bars for symbol line":
				"Battute insufficienti per la linea s:",
	"Not enough measures for %%repeat":
				"Battute insufficienti per %%repeat",
︙
Changes to err-pt_BR.js.
1
2
3
4
5
6
7
8
9
10
11
12
13


14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11


12
13
14
15
16
17
18
19
20











-
-
+
+







//JS abc2svg - Brazilian Portuguese translation of the error codes:
//			Tradução dos códigos de erro para português brasileiro
// Adicione este arquivo na linha de comando para obter as
// mensagens de erro em português do Brasil.
// (Hudson Lacerda - 11/dez/2016 - 22/jan/2018)

user.textrans = {
	"+: lyric without music": "+: palavras sem música",
	"+: symbol line without music": "+: linha de símbolos sem música",
	"+: without previous info field": "+: continuação sem campo anterior",
	"'{' in grace note": "'{' em notinha ornamental",
	"'}', ')' or ']' missing in %%staves":
				"Falta '}', ')' ou ']' em %%staves",
	"'}', ')' or ']' missing in %%score":
				"Falta '}', ')' ou ']' em %%score",
	"',' lacking in annotation '@x,y'":
				"Falta uma ',' na anotação '@x,y'",
	"'$1' is not a note": "'$1' não é uma nota",
	"'$1:' line ignored": "'$1:' linha ignorada",
	"$1: inside tune - ignored": "$1: no meio da música - ignorando",
	"!$1! must be on a note": "!$1! deveria estar em uma nota",
	"!$1! must be on a note or a rest":
︙
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51




52
53
54
55
56
57
58
59
60
61

62
63
64

65
66
67
68

69
70
71
72

73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91


92
93
94
95
96

97
98
99
100
101
102
103
30
31
32
33
34
35
36

37
38
39

40
41
42
43
44
45
46
47


48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

65
66
67
68

69
70
71


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88



89
90
91
92
93
94

95
96
97
98
99
100
101
102







-



-








-
-
+
+
+
+










+


-
+



-
+


-
-
+
















-
-
-
+
+




-
+







	"%%deco: bad C function value '$1'":
				"%%deco: valor de função '$1' incorreto",
	"%%deco: cannot have a negative value '$1'":
				"%%deco: valor '$1' não pode ser negativo",
	"%%multicol end without start": "%%multicol end sem 'start'",
	"%%multicol new without start": "%%multicol new sem 'start'",
	"%%repeat cannot start a tune": "%%repeat não pode estar no início de uma música",
	"%%select ignored": "%%select ignorado",
	"%%staffwidth too big": "%%staffwidth muito grande",
	"%%staffwidth too small": "%%staffwidth muito pequeno",
	"%%tune not treated yet": "%%tune ainda não é tratado",
	"%%voice ignored": "%%voice ignorado",
	"%%vskip cannot be negative": "%%vskip não pode ser negativo",
	"Bad !xstem!": "!xstem! incorreto",
	"Bad %%setbarnb value": "Valor incorreto para %%setbarnb",
	"Bad %%staff number $1 (cur $2, max $3)":
				"Número incorreto $1 em %%staff (atual $2, máximo $3)",
	"Bad %%staff value '$1'": "Valor incorreto '$1' em %%staff",
	"Bad %%stafflines value": "Valor incorreto em %%stafflines",
	"Bad %%staffscale value": "Valor incorreto em %%staffscale",
	"Bad char '$1' in M:": "Caractere inválido '$1' em M:",
	"Bad character '$1'": "Caractere inválido '$1'",
	"Bad char '$1' in M:": "Caráter inválido '$1' em M:",
	"Bad character '$1'": "Caráter inválido '$1'",
	"Bad character in grace note sequence":
				"Caráter inválido em grupo de notinhas ornamentais",
	"Bad controller number in %%MIDI": "Número inválido do controlador em %%MIDI",
	"Bad controller value in %%MIDI": "Valor incorreto para controlador em %%MIDI",
	"Bad duration '$1' in M:": "Duração '$1' incorreta em M:",
	"Bad integer value": "Número inteiro incorreto",
	"Bad instr= value": "Valor incorreto para instr=",
	"Bad L: value": "Valor incorreto de L:",
	"Bad note in %%map": "Nota incorreta em %%map",
	"Bad page width": "Largura de página incorreta",
	"Bad program in %%MIDI": "Valor incorreto de instrumento %%MIDI",
	"Bad scale value in %%font": "Tamanho incorreto em %%font",
	"Bad tempo value": "Bad tempo value",
	"Bad tie": "Ligadura de prolongamento incorreta",
	"Bad transpose value": "Valor incorreto de transposição",
	"Bad user character '$1'": "Caractere ilegal '$1' em U:/%%user",
	"Bad user character '$1'": "Caráter ilegal '$1' em U:/%%user",
	"Bad value '$1' in %%linebreak - ignored":
				"Valor incorreto '$1' em %%linebreak - ignorado",
	"Bad value in $1": "Valor incorreto em $1",
	"Bad voice ID in %%staves": "Identificador de voz incorreto em %%staves",
	"Bad voice ID in %%score": "Identificador de voz incorreto em %%score",
	"Bar in repeat sequence": "Barra de compasso em um fragmento %%repeat",
	"Cannot have !$1! on a head": "!$1! não pode estar em cabeça de nota",
	"Cannot have %%fullsvg inside a tune":
				"'%%fullsvg' não pode estar dentro de uma música",
	"Cannot have '$1' inside a tune": "'$1' não pode estar dentro de uma música",
	"Cannot have a bar in grace notes":
			"Não pode haver barra de compasso em grupo de notinhas ornamentais",
	"Cannot have a broken rhythm in grace notes":
			"Não pode haver ritmo quebrado em notinhas ornamentais",
	"Cannot have V:* in tune body": "Não pode haver V:* no corpo da música",
	"Cannot read file '$1'": "Arquivo '$1' não pôde ser aberto ou não existe",
	"Decoration ignored": "Decoração ignorada",
	"Empty line in tune header - ignored":
				"Linha em branco no cabeçalho da música - ignorada",
	"Erroneous end of voice overlay": "Terminação incorreta de superposição de vozes",
	"Incorrect 1st value in %%repeat": "Primeiro valor incorreto em %%repeat",
	"Incorrect 2nd value in %%repeat": "Segundo valor incorreto em %%repeat",
	"Invalid 'r' in tuplet": "'r' inválido em quiáltera",
	"Invalid decoration '$1'": "Decoração '$1' inválida",
	"Invalid note duration $1": "Duração de nota $1 inválida",
	"Lack of ']'": "']' faltando",
	"Lack of ending $1 in U:/%%user": "Falta de $1 final em U:/%%user",
	'Lack of starting ! or " in U: / %%user':
				'U: / %%user não começou com ! ou "',
	'Lack of starting [, ! or " in U: / %%user':
				'U: / %%user não começou com [, ! ou "',
	"Line split problem - adjust maxshrink and/or breaklimit":
		"Problema de quebra de linha - ajuste maxshrink e/ou breaklimit",
	"Line too much shrunk $1 $2 $3": "Linha muito encolhida $1 $2 $3",
	"Line underfull ($1pt of $2pt)": "Linha muito espaçada ($1pt de $2pt)",
	"Misplaced '$1' in %%staves": "'$1' deslocado em %%staves",
	"Misplaced '$1' in %%score": "'$1' deslocado em %%score",
	"Misplaced dot": "Ponto fora de lugar",
	"Mix of old and new transposition syntaxes":
				"Mistura de sintaxes de transposição antiga e nova",
	"No $1 after %%$2": "Falta $1 após %%$2",
	"No </defs> in %%beginsvg sequence":
				"Falta </defs> em bloco %%beginsvg",
	"No </style> in %%beginsvg sequence":
︙
112
113
114
115
116
117
118

119
120
121
122
123

124
125
126
127
128
129
130
111
112
113
114
115
116
117
118
119
120



121
122
123
124
125
126
127
128







+


-
-
-
+







				"Falta terminar quiáltera nesta linha de música",
	"No end of voice overlay": "Falta fim de divisão de vozes",
	"No function for decoration '$1'": "Falta função para decoração '$1'",
	"No note before '-'": "Falta nota antes de '-'",
	"No note before '<'": "Falta nota antes de '<'",
	"No note in voice overlay":
				"Falta nota em superposição de vozes",
	"Non standard note duration $1": "Duração não padronizada: $1",
	"No read_file support": "A função read_file não existe",
	"No start of !$1!": "Falta início de !$1!",
	"Not a note in grace note sequence":
				"Caractere inválido em grupo de notinhas ornamentais",
	"Not an ASCII character": "Não é um caractere ASCII",
	"Not an ASCII character": "Não é um caráter ASCII",
	"Not enough measure bars for lyric line":
				"Faltam barras de compasso na linha de palavras (letra)",
	"Not enough measure bars for symbol line":
				"Faltam barras de compasso na linha de símbolos",
	"Not enough measures for %%repeat": "Faltam compassos para %%repeat",
	"Not enough parameters in %%map":
				"Parâmetros insuficientes para %%map",
︙
Changes to font.js.
1
2
3
4
5
6
7
8
9
10









11
12
13
14
15
16
17
18
19
20
21









22
23
24
25
26
27







28
29
30



31
32
33

34
35
36
37
38
39
40
41








42
43
44
45
46
47










48
49
50
51



52
53
54

55
56
57
58
59





60
61
62
63
64
65
66
67
68
69










70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133






































































134
135
136
137
138

139
140


141
142
143
144
145




146
147

148
149
150



151
152
153
154
155
156
157
158
159
160
161
162













163
164
165
166
167







168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186




















187
188

189
190
191
192
193
194
195











196
197


198
199
200



201
202


203
204
205



206
207
208






209
210
211
212
213
214
215







216
217
218
219
220














221
222


223
224

225
226
227
228



229
230
231



232
233
234


235
236
237
238
239



240
241




242
243
244




245
246
247
248

249
250
251
252
253
254
255







256
257

258
259
260
261
262









263
264
265
266
267
268






269
270


271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287









288
289
290
291
292





293
294
295
296
297
298






299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321

























322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369























































370
371
372
373
374
375
376
377




1









2
3
4
5
6
7
8
9
10
11
12
13








14
15
16
17
18
19
20
21
22






23
24
25
26
27
28
29
30


31
32
33
34
35

36








37
38
39
40
41
42
43
44






45
46
47
48
49
50
51
52
53
54




55
56
57



58





59
60
61
62
63










64
65
66
67
68
69
70
71
72
73
































































74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143





144


145
146





147
148
149
150


151



152
153
154












155
156
157
158
159
160
161
162
163
164
165
166
167





168
169
170
171
172
173
174



















175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194


195







196
197
198
199
200
201
202
203
204
205
206


207
208



209
210
211


212
213



214
215
216



217
218
219
220
221
222







223
224
225
226
227
228
229





230
231
232
233
234
235
236
237
238
239
240
241
242
243


244
245


246




247
248
249



250
251
252



253
254





255
256
257


258
259
260
261



262
263
264
265




266







267
268
269
270
271
272
273


274





275
276
277
278
279
280
281
282
283






284
285
286
287
288
289


290
291

















292
293
294
295
296
297
298
299
300





301
302
303
304
305






306
307
308
309
310
311























312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
















































337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396



397
398
399
400

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+



-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+

-
-
+
+
+


-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
-
-
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
-
-
+
+
-
-
-
-
-
+
+
+
+
-
-
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
+
+
+
-
-
+
+
-
-
-
+
+
+
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
+
-
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
-
-
-
-
-
+
+
+
-
-
+
+
+
+
-
-
-
+
+
+
+
-
-
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
-
+
+
+
+
// abc2svg music font
var musicfont = 'url("data:application/x-font-ttf;base64,\
AAEAAAAOAIAAAwBgRkZUTYGiLYcAAFMIAAAAHEdERUYAFQAUAABS7AAAABxPUy8yWLxbCQAAAWgA\
AABWY21hcM7xzSEAAAPMAAADKmN2dCAAIgKIAAAG+AAAAARnYXNw//8AAwAAUuQAAAAIZ2x5ZvUw\
oLYAAAgEAABCqGhlYWQNXJHdAAAA7AAAADZoaGVhCWn/CwAAASQAAAAkaG10eM2n+wQAAAHAAAAC\
CmxvY2HVL+UYAAAG/AAAAQhtYXhwAMoBPQAAAUgAAAAgbmFtZcjHNnAAAEqsAAADGHBvc3TQFqwa\
AABNxAAABR8AAQAAAAEAAGk1jGtfDzz1AAsEAAAAAADRlyIXAAAAANgjLVL/OPzvBUsEiAAAAAgA\
AgAAAAAAAAABAAAEiPzvAFwEJf84/XQFSwABAAAAAAAAAAAAAAAAAAAAggABAAAAgwEMAAUAAAAA\
AAIAAAABAAEAAABAAC4AAAAAAAEBlwGQAAUACAKZAswAAACPApkCzAAAAesAMwEJAAACAAUDAAAA\
AAAAAAAAARAAAAAAAAAAAAAAAFBmRWQAQAAA6qQDM/8zAFwEiAMRAAAAAQAAAAAAAAF2ACIAAAAA\
var musicfont = 'url("data:application/octet-stream;base64,\
AAEAAAAOAIAAAwBgRkZUTYTd79wAAFggAAAAHEdERUYAFQAUAABYBAAAABxPUy8yWLxdCwAAAWgA\
AABWY21hcFxKS5kAAAQIAAADemN2dCAAIgKIAAAHhAAAAARnYXNw//8AAwAAV/wAAAAIZ2x5Zp27\
6QgAAAiwAABGeGhlYWQPFh8MAAAA7AAAADZoaGVhCWn/GwAAASQAAAAkaG10eNzF+scAAAHAAAAC\
SGxvY2GpopfcAAAHiAAAASZtYXhwANkBPQAAAUgAAAAgbmFtZbni4sIAAE8oAAADG3Bvc3Qtfw6A\
AABSRAAABbUAAQAAAAEAAI87udRfDzz1AAsEAAAAAADRlyIXAAAAANncuoH/OPzvBUsEiAAAAAgA\
AgAAAAAAAAABAAAEiPzvAFwEJf84/XQFSwABAAAAAAAAAAAAAAAAAAAAkgABAAAAkgEMAAUAAAAA\
AAIAAAABAAEAAABAAC4AAAAAAAEBhgGQAAUACAKZAswAAACPApkCzAAAAesAMwEJAAACAAUDAAAA\
AAAAAAAAARAAAAAAAAAAAAAAAFBmRWQAQAAA7LcDM/8zAFwEiAMRAAAAAQAAAAAAAAF2ACIAAAAA\
AVUAAAGQAAACWAAAAFcAAAFK/7ACE/+wANL/sAAjAAAAIwAAACMAAABkAAAEIwAABCUAAAHg/9wD\
XgB6AwsAAALSAAACv/+6AdYAAAMLAAADDgAAAyf/yADIAAABrgAAASIAAAGQAAABfAAAAZAAAAGQ\
AAABgQAAAZAAAAGQAAABgQAAAZkACQGYAAkB9AAAAQQAFAEEAAoCawAkAhIAAAHCAAABSQAAAUAA\
AAFK//4BLAAAAjAAAAFKAAABSgAAAGQAAAE7AAABOwAAATsAAAE7AAABOwAAATsAAAE7AAABOwAA\
ATsAAAE7AAABDQAAAMgAAAD/AAABCwAUAW4AAAENADIBbv/1AKkAAAE6AAABQP/9AFAAAAFAAAAB\
QAAAARgAAAJYAAAAtgAAAIIAAACCAAABLAAAASwAAADuAAAA/wAAAUkAAAGPAAAB2AAAAdgAAANT\
AAACM//wAyD/4QIz/7QBuP/bAV//fgIzAAACM//kAr//tAIz/7QCv/+0Ayv/2wFf/9sCaf9+AV//\
fgJp/34BXwAAAf0ABQG1AAABtQAAAkQADQJEAA0BGAAAATYAAAEs//8BLAAAAPoAAADIAAABGP84\
APoAAADIAAAEDQAAAhwADAH0AAAB9AAAAfQAAAH0AAAB9AAAAfQAAAB4AAAALQAAAhwAAAD6AAD/\
6gAAAAAAAwAAAAMAAAAcAAEAAAAAAiQAAwABAAAAHAAEAggAAAB+AEAABQA+AAAAIOAA4CTgMOA5\
4EPgSOBQ4FzgYuBp4H3gjOCV4KTgqeCz4QHhueG74efiSeJk4oPkoOSi5KTkqOSs5MDkzuTq5O7l\
AAFK//4BLAAAAjAAAAFKAAABSgAAAGQAAAFAAAABQAAAAUAAAAFAAAAAZAAAATYAAADmAAABNgAA\
ATsAAAE7AAABOwAAATsAAAE7AAABOwAAATsAAAE7AAABOwAAATsAAAENAAAAyAAAAP8AAAELABQB\
bgAAAQ0AMgFu//UAqQAAAToAAAFA//0AUAAAAUAAAABkAAABGAAAAlgAAAC2AAAAggAAAIIAAAEs\
AAABLAAAAO4AAAD/AAABSQAAAY8AAAHYAAAB2AAAA1MAAAIz//ADIP/hAXv/tAG4/9sBFv9+ARP/\
2wDcAAAA6P/kAr//tAIz/7QCv/+0Ayv/2wFf/9sCaf9+AV//fgJp/34BXwAAAf0ABQG1AAABtQAA\
AkQADQJEAA0BGAAAATYAAAEs//8BLAAAAPoAAADIAAABGP84APoAAADIAAAEDQAAAhwADAH0AAAB\
9AAAAfQAAAH0AAAB9AAAAfQAAAB4AAAALQAAAhwAAAD6AAAA+v/oAcIAAAFIAAABQAAAAgoAAAIK\
AAAAZAAAAAAAAwAAAAMAAAAcAAEAAAAAAnQAAwABAAAAHAAEAlgAAACSAIAABgASAAAAIOAA4CTg\
MOA54EPgSOBQ4FzgYuBp4H3gjOCV4KTgqeCz4QHhueG74efh8uH04ffh+eH84gDiSeJk4oPkoOSi\
AeUi5SXlLeUx5TnlZ+Vp5W3lguXQ5eLmEOYS5hTmGOYk5jDmUOZV6RLpFekY6SDpJeld6gLqpP//\
AAAAAAAg4ADgIuAw4DjgQ+BF4FDgXOBi4GngeuCA4JTgoOCp4LPhAeG54bvh5+JA4mDigOSg5KLk\
pOSo5KzkwOTO5OHk7uUA5SDlJOUp5S/lOeVm5WnlbOWC5dDl4uYQ5hLmFOYY5iTmMOZQ5lXpEOkU\
6RjpIOkl6V3qAuqk//8AA//kIAUf5B/ZH9IfyR/IH8Efth+xH6sfmx+ZH5IfiB+EH3sfLh53HnYe\
Sx3zHd0dwhumG6UbpBuhG54bixt+G2wbaRtYGzobORs2GzUbLhsCGwEa/xrrGp4ajRpgGl8aXhpb\
GlAaRRomGiIXaBdnF2UXXhdaFyMWfxXeAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
5KTkqOSs5MDkzuTq5O7lAeUl5S3lMeU55WflaeVt5YLl0OXi5hDmEuYU5hjmJOYw5lDmVekS6RXp\
GOkg6SXpXeoC6qTso+yl7Kfsqey3//8AAAAAACDgAOAi4DDgOOBD4EXgUOBc4GLgaeB64IDglOCg\
4Kngs+EB4bnhu+Hn4fLh9OH34fnh/OH+4kDiYOKA5KDkouSk5KjkrOTA5M7k4eTu5QDlIOUp5S/l\
OeVm5WnlbOWC5dDl4uYQ5hLmFOYY5iTmMOZQ5lXpEOkU6RjpIOkl6V3qAuqk7KLspeyn7Knst///\
AAP/5CAFH+Qf2R/SH8kfyB/BH7YfsR+rH5sfmR+SH4gfhB97Hy4edx52HkseQR5AHj4ePR47Hjod\
+x3lHcobrhutG6wbqRumG5Mbhht0G3EbYBtCGz8bPhs3GwsbChsIGvQapxqWGmkaaBpnGmQaWRpO\
Gi8aKxdxF3AXbhdnF2MXLBaIFecT6hPpE+gT5xPaAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEGAAADAAAAAAAAAAECAAAA\
AgAAAAAAAAAAAAAAAAAAAAEAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAQYAAAMAAAAAAAAAAQIAAAACAAAAAAAAAAAAAAAAAAAAAQAABAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAogAAAAqACoAKgA2\
AAAAAAAAAAAAAAAAAAAAAAAAIgKIAAAAKgAqACoANgA+AG4AfACKAJgApACwAL4A3AFQAawCHAJe\
AvADggPkA/gElAUmBX4FvAXeBfIGRAaUBrQG+gc6B3QHwAgACEYIoAi0CNoJAAkyCU4JcAmcCbIJ\
zAoCChAKHAooCjoKVApuCqIK1gsqC3QL1AwyDKgNHA1CDVwNjA22Dg4ONA6ODq4O7A8UDygPNA9C\
D1IPeg+gD6wPuA/ED9AP8hAYEE4QnBD8EXARhhGsEdoSPhK2ExQTXBO4FRIWHhbYF5gYaBkEGeIa\
/hwAHEIchhzQHOodDh0iHTYdZh12HYgdpB26Hd4eDh7wH6wf1B/yICQgYiCUINYg6CD2IRIhLCFU\
AAIAIgAAATICqgADAAcALrEBAC88sgcEAO0ysQYF3DyyAwIA7TIAsQMALzyyBQQA7TKyBwYB/Dyy\
AQIA7TIzESERJzMRIyIBEO7MzAKq/VYiAmYAAAEAAAAAAZEBkAADAAAxESERAZEBkP5wAAEAAAAA\
AAAAAAAAAAAxAAABAAAAAABXBAMAIAAAETU2NTQnJjU0NwYVFBcWFRQHFhUUBwYVFBcmNTQ3NjU0\
AD4AbgB8AIoAmACkALAAvgDcAVABrAIcAl4C6gN8A9wD8ASMBR4FdgW0BdYF6gY8BowGrAbyBzIH\
bAe4B/gIPgiYCKwI0gj4CSoJRgloCZQJqgnECfoKCAoUCiAKMgpOCnAKfgqSCqQKtAsACxALKgtE\
C3wLtAwCDE4MtA0WDZQODA4yDkwOfA6mDv4PJA9+D54P3BAEEBgQJBAyEEIQahCQEJwQqBC0EMAQ\
4hEIET4RjBHsEmASdhKcEsoTHhOSE+YUJhRqFMAWGhcmF+AYoBlwGgwa6hwGHQgdSh2OHdgd8h4W\
HioePh5uHn4ekB6sHsIe5h8WH/ggtCDcIPohLCFqIZwh3iHwIf4iGiI0IloifCKqIsQi7iMqIzwA\
AAACACIAAAEyAqoAAwAHAC6xAQAvPLIHBADtMrEGBdw8sgMCAO0yALEDAC88sgUEAO0ysgcGAfw8\
sgECAO0yMxEhESczESMiARDuzMwCqv1WIgJmAAABAAAAAAGRAZAAAwAAMREhEQGRAZD+cAABAAAA\
AAAAAAAAAAAAMQAAAQAAAAAAVwQDACAAABE1NjU0JyY1NDcGFRQXFhUUBxYVFAcGFRQXJjU0NzY1\
NSMSVz8VJU1NJRU/VxIjAgMCGEM2YDk0ZjoySyI5YU1hGBhmTGA5JUoyOmY0OWA2SAAB/7D/7AGa\
ABQAAwAAJyEVIVAB6v4WFCgAAAAAAf+w/+wCYgAUAAMAACchFSFQArL9ThQoAAAAAAH/sP/sASIA\
FAADAAAnIRUhUAFy/o4UKAAAAAABAAAAAAAjA+gAAwAAETMRIyMjA+j8GAABAAAB9AAjA+gAAwAA\
ETMRIyMjA+j+DAABAAAC+AAjBFYAAwAAETcRIyMjBEwK/qIAAAAAAgAAAUAAZAKeAAcADwAAEiIm\
NDYyFhQCIiY0NjIWFEcqHR0qHR0qHR0qHQI6HSodHSr+6R0qHR0qAAAABQAAAAAEJAGuAC8ANwA/\
AEcAUwAAITUzHgEzMjY1NCcuBDU0NjMyFhc3MxcjLgEjIgYVFB4DFx4BFRQGIyInByAiJjQ2MhYU\
NDUjElc/FSVNTSUVP1cSIwIDAhhDNmA5NGY6MksiOWFNYRgYZkxgOSVKMjpmNDlgNkgAAf+w/+wB\
mgAUAAMAACchFSFQAer+FhQoAAAAAAH/sP/sAmIAFAADAAAnIRUhUAKy/U4UKAAAAAAB/7D/7AEi\
ABQAAwAAJyEVIVABcv6OFCgAAAAAAQAAAAAAIwPoAAMAABEzESMjIwPo/BgAAQAAAfQAIwPoAAMA\
ABEzESMjIwPo/gwAAQAAAvgAIwRWAAMAABE3ESMjIwRMCv6iAAAAAAIAAAFAAGQCngAHAA8AABIi\
JjQ2MhYUAiImNDYyFhRHKh0dKh0dKh0dKh0COh0qHR0q/ukdKh0dKgAAAAUAAAAABCQBrgAvADcA\
PwBHAFMAACE1Mx4BMzI2NTQnLgQ1NDYzMhYXNzMXIy4BIyIGFRQeAxceARUUBiMiJwcgIiY0NjIW\
FAQiJjQ2MhYUAREzMjY0JiMDNTMRIzUzMhYVFCMCTh4VTzIpO5QZGioWEVk9JCcZHh4HHg9KMB85\
ECIZMghOTVtPRS4jAZsqHR0qHf3qKh0dKh3+iSg8RkY83EZG3HGJ+qA8SyAhLSgHCBQUIxVDTQsO\
GZs6SCgbDxcPCQsCFTczOk4gIB0qHR0qHR0qHR0qAWn+mGGmYf56HgFoHmdr0gAFAAAAAAQkAa4A\
GgAiACoAMgA+AAAhIiY1NDYzMhYXNzMXByYjIgYUFjMyNjcXDgEyIiY0NjIWFAQiJjQ2MhYUAREz\
BCImNDYyFhQBETMyNjQmIwM1MxEjNTMyFhUUIwJOHhVPMik7lBkaKhYRWT0kJxkeHgceD0owHzkQ\
IhkyCE5NW09FLiMBmyodHSod/eoqHR0qHf6JKDxGRjzcRkbccYn6oDxLICEtKAcIFBQjFUNNCw4Z\
mzpIKBsPFw8JCwIVNzM6TiAgHSodHSodHSodHSoBaf6YYaZh/noeAWgeZ2vSAAUAAAAABCQBrgAa\
ACIAKgAyAD4AACEiJjU0NjMyFhc3MxcHJiMiBhQWMzI2NxcOATIiJjQ2MhYUBCImNDYyFhQBETMy\
MjY0JiMDNTMRIzUzMhYVFCMDAmd1glolKRseHgghJF44NjY4Nk0RIxRZsSodHSod/eoqHR0qHf6J\
KDxGRjzcRkbccYn6a2dldwwSHqYEjG2YbUk+CkpRHSodHSodHSodHSoBaf6YYaZh/noeAWgeZ2vS\
AAAAA//cAAIB3gKzAAcADwBNAAAAIiY0NjIWFAQiJjQ2MhYUFzQ2MzIWFRQHFjMyNjU0LwEDJxMu\
NjQmIwM1MxEjNTMyFhUUIwMCZ3WCWiUpGx4eCCEkXjg2Njg2TREjFFmxKh0dKh396iodHSod/oko\
PEZGPNxGRtxxifprZ2V3DBIepgSMbZhtST4KSlEdKh0dKh0dKh0dKgFp/phhpmH+eh4BaB5na9IA\
AAAD/9wAAgHeArMABwAPAE0AAAAiJjQ2MhYUBCImNDYyFhQXNDYzMhYVFAcWMzI2NTQvAQMnEy4B\
ATU0PgE3NjMyFhUUBiMiJjU0NyYjIgYVFB8BExcDHgEVFA4BBwYjIiYBniAYGCAY/n4gGBggGFsb\
NTQ+ATc2MzIWFRQGIyImNTQ3JiMiBhUUHwETFwMeARUUDgEHBiMiJgGeIBgYIBj+fiAYGCAYWxsU\
Ex4sFykmNiZ7zyvRWkgcExQmMzA4GxQTHiwXKSY2JnrUK9VaSBwTFCYzMDgBUhggGBggVBggGBgg\
0xIcGhEdDhctJi0mZf7eIAElR3E4Fi0RERM4IRIcGhEdDhctJi0mZQEmH/7XR3I4Fi0RERM4AAUA\
ev80A14CVAAXABsAHwAjACcAAAEzFR4BFzMVIw4BBxUjNS4BJyM1Mz4BNxEjFhc3FTY3JzMmJwc1\
BgcB2ChVeAeKigd4VShVeAeKigd4VXAFayhrBXBwBWsoawUCVIkIi2AoYIsIiYkIi2AoYokI/uW6\
FBMeLBcpJjYme88r0VpIHBMUJjMwOBsUEx4sFykmNiZ61CvVWkgcExQmMzA4AVIYIBgYIFQYIBgY\
INMSHBoRHQ4XLSYtJmX+3iABJUdxOBYtERETOCESHBoRHQ4XLSYtJmUBJh/+10dyOBYtERETOAAF\
AHr/NANeAlQAFwAbAB8AIwAnAAABMxUeARczFSMOAQcVIzUuAScjNTM+ATcRIxYXNxU2NyczJicH\
NQYHAdgoVXgHiooHeFUoVXgHiooHeFVwBWsoawVwcAVrKGsFAlSJCItgKGCLCImJCItgKGKJCP7l\
ug7IyA66KLoOyMkOuwAAAAAEAAD9bwKnBIgACwBEAE4AYwAAAQYVFBc+ATU0Jw4BExcVFAYjIiY1\
DsjIDrooug7IyQ67AAAAAAQAAP1vAqcEiAALAEgAUgBnAAABBhUUFz4BNTQnDgETFxQVFAYjIiY1\
NDYzMhYUBgcWMzI2NTQ1JwYjIi4CNTQ3PgY/ASY1NDY3FhUUBgcXNjMyFhUUJzQmIyIGIxM+AScO\
ARUUFhcuATU0NycOAQceATMyNwFsBwVIdTY5QksXT01SX0AyL0E/Ly0YLkEXFBVJhmc9Og0kIC4d\
MRIXFg1sSWJceRQQD3GITmJQAgcCIVtB4jZGJx09P6YRj2wBAqB6EBADUzYuF2YxlUhyDQde+2zs\
AQJMXFNBLUg7WDcBGT1FAgHpAjNbh0+FZhcwJS0aJw4REJZmjKEHOuWIqV/PAp1yzro9ZwH+nRJd\
8gtGMSBCEg5FR6kuvXKXZ42jAgAAAAACAAD9/ALSAgAAZABoAAABMjY1NCcmIyIHDgIHJicmJxEj\
ETMRNjc2Nx4DFxYzMjY1NCcmIyIHFhcUFhUUBisBJjU0NzY3NjMyFxYXFRQGBwYjIicHFzYzMhYX\
Fh0BBgcGIyImNTQ3MzIWFRQGFQYHFgEzESMB3j5KDRpJRjwCBgoEIhoeLhwcLh4aIgYUDBkPJyUx\
PRIkUi8xMg0CMyEFRAUaVScjXlU3CFpIHy00PyIiPzRCZh4oCDdWXUxyRAUhMwIPMDX+TXt7/iB4\
Si0tbEkFDhsKYicrH/4EBAD+ER8rJ2ILKhcdCRt7QjE2YhoQKAMNBB4rGTIUC0QZDU80UxJObxwN\
NDYzMhYUBgcWMzI2PQEnBiMiJjU0Nz4GPwEmNTQ2NxYVFAYHFzYzMhYVFCc0JiMiBiMTPgEnDgEV\
FBYXLgE1NDcnDgEHHgEzMjcBbAcFSHU2OUJLF09NUl9AMi9BPy8tGC9AFxwNmNs6DSQgLh0xEhcW\
DWxJYlx5FBQLcYhOYlACBwIhW0HiNkYnHT0/phGPbAECoXkKFgNTNy0jWjGVSHINB177bOwDTFxT\
QS1IO1g3ARk8RgPpAsedhWYXMCUtGicOERCWZoyhBzrliKlfzwKcc866PmYB/p0SXfILRjEgQhIO\
RUepLr1yl2eNowIAAgAA/fwC0gIAAGQAaAAAATI2NTQnJiMiBw4CByYnJicRIxEzETY3NjceAxcW\
MzI2NTQnJiMiBxYXFBYVFAYrASY1NDc2NzYzMhcWFxUUBgcGIyInBxc2MzIWFxYdAQYHBiMiJjU0\
NzMyFhUUBhUGBxYBMxEjAd4+Sg0aSUY8AgYKBCIaHi4cHC4eGiIGFAwZDyclMT0SJFIvMTINAjMh\
BUQFGlUnI15VNwhaSB8tND8iIj80QmYeKAg3Vl1MckQFITMCDzA1/k17e/4geEotLWxJBQ4bCmIn\
Kx/+BAQA/hEfKydiCyoXHQkbe0IxNmIaECgDDQQeKxkyFAtEGQ1PNFMSTm8cDRdLShc+LDlDElM0\
UE48MhkrHgQOBCYQHAPe/AAAAAP/uv2lAsMA/wAqADYAQgAANzQ2MzIWFxYVFAYHDgEHNjc+ATc2\
F0tKFz4sOUMSUzRQTjwyGSseBA4EJhAcA978AAAAA/+6/aUCwwD/ACsANwBDAAA3NDYzMhYXFhUU\
BgcOAQc+ATc+ATc2NTQmJy4BIyIGBz4BMzIWFRQHBiMiJgUiJjU0NjMyFhUUBiciJjU0NjMyFhUU\
BhOLZ1VrKy9CVWjVkXevSDEsEw4RHR80MD9iERcjHC49JyExM0UCghYdGhQVHhoaFxscFRQcGx1i\
gDQ5PnJ/sE9iVQggVUgxVFA7akdQIyQdTE0dFEEvMiAeUYocFxYcHRUWHfIeGRUaGxQZHgAAAAIA\
AP8GAXIA+gADAAcAADczEyMDMxMj3JQCltyUApb6/gwB9P4MAAAEAAD+CgIfA6oACQAgAGIAbQAA\
JRYXPgE1NCYjIgMCJw4BFRQXLgE1NDY3JicOAQceATMyFx4BHwEdARQjIiY1NDYzMhYVFAYHFjMy\
NjU0LwEGIyImNTQ3PgE3PgI3JjU0NjceARUUBgceARc2MzIXFhUUBwYDBhUUFz4BNTQnBgFJEwZN\
R1ZCDg0YASw5HyAqTDsFCXFVAQVtggMiAwYCAno2UzMoJTUxJxUiIywBDQkVjZkuDkweBSMnEg5Z\
QC8ZSWECCAQSCFw5MmM2ZAMGN14pX3XEWxJPMzZW/uABCxAJNCcpJhJBKThOED5aWnlTcX4aIEAX\
FxsHf0gxJDM3JCIoAQwzNQ8JjQGSimpRHFAYBCAhDcIHbn8TM2JbbYdMEW4kAkM2YncwGwNWHiI5\
JCV7M0YmJgACAAD+YwJCAZoAYwBnAAABMjY1NCcmIyIHDgEHJicmJxEjETMRNjc2Nx4DFxYzMjY1\
NCcmIyIHFhcUFhUUBisBJjU0NzY3NjMyFxYXFRQGBwYjIicHFzYzMhYXFh0BBgcGIyImNTQ3MzIW\
FRQGFQYHFgEzESMBfjI7ChU6ODACDAQdExglFhYlGBMdBBEKEwwgHScxDhxDJScoCgIqGgQ2BBVE\
IxhJRi0GSDoYJSsxGxs0KDVSGCAGLUZJPVs2BBoqAgwmK/6jYmL+gGA7JCRXOwUdC1IcIhn+agMz\
/nQZIhxSCCISFwgWYjUrKE4VDSACCwMYIhQoEAk1FQtAK0EOPloWChI8OxMyIy41D0ErQD8wKBQj\
GAMLAx8NFgMY/M0AAAAAA//I/h4CNgDMACYALwA7AAA3NDYzMhcWFRQHDgEHNjc2NzY1NCcuASMi\
Bgc+ATMyFhUUBwYjIiYFIiY0NjIWFAYnIiY1NDYzMhYVFAYPcFKCPCN4Q8ZpwF5HHgktGC0gMFIK\
EhYWJTkgGSUsOQICEhcUIhgVFRIWFRIRFhUhTV5XM1rGbDtXBjNkSowvKWYzGhRCNhcNPCYoGhhT\
dRckFhciGMIYFBEUFRATGQAAAAADAAAAAADIAPAACQATACkAADcGFRQWMzI2NTQnIgYVFBc2NTQm\
ByImNTQ2Ny4BNTQ2MzIWFRQHFhUUBlAjGxARFAIOEyAjEzAlNSMiEQwoHiQ2PCg0dxQdFB4bERaG\
EQ4XGQshDxTcKB4XGgsPExAaIiAcIxAgGyAmAAAAAgAA/wYBrgD6AAsAFAAAMxQWMzI2NTQmIyIG\
BzQ2MhYUBiImiiojIisnJiUoin20fX20fWJ4eWFldXZhZ5CR0pGSAAABAAD/BgEiAPoACQAAMTcz\
ERcVIzU3EWR9QfBB+v4+HhQUHgEsAAAAAQAA/wYBjwD6ADwAADcyFRQHDgMHNjMyFjMyNz4CMw4C\
BwYHBiMiJiMiBiMiNTQnPgU1NCciBzIWFRQGIyI1ND4Bx8gFDTZAbzYTIBtkHBgeBRAMAQEFBQEH\
EBopGnQVH1YCBwECLD5HPChTThocKTceTDxY+n4aDiEuHUQtDCMOAw0LBRYWAykOGCcmEAECIUU4\
PDI4GGIBNSUeHylnKDkZAAABAAD/BgF1APoAOQAANzIWFRQGIyImNTQ3NjMyFxYVFAYHHgEVFAcG\
IyImJyY0NjMyFhUUBiMWMzI2NTQmJyY0Nz4BNCYjImYbIichGzIfM1lGJkRGPT5RSyRNJ1cYIzIg\
IiolGww/JCtILhYWL0spJDyqHBcbIysjLxoqEyJILkQLC0UtQycTFhQdTC4hGxkeKTEnJjoIBCIE\
CTdQMAAAAQAA/wYBkAD6ABEAAAUXIzc1IzU2NTMBMz8BETMVIwFFMsgy4ZOj/vuwAWNLS9EpKTEo\
8oD+jpaR/tkoAAAAAAEAAP8HAX4A+gAvAAAXNjMyFhUUBiMWMzI3PgE1NCcmIyIHEyEOASsBBzYz\
MhceARUUBw4CIyInLgE1NBIcIRsqIBwaJDEcEwkeHChOSAoBYgs1JdUGOUJTMSErQxZAKyU8KxAe\
XyAgFxwgIR4UHyA5HBo1ASIkOnkeHxVBJU8vEBACFAkyEiMAAAAAAgAA/wYBgQD6AAkALAAAFzI2\
NTQmIyIHFhMWFRQGIyImNTQ2MyYjIgYVPgIzMhYVFAYjIiYnPgEzMhbIKS0qKCwwB90bIxgeIhsQ\
Fjc1LxUYLR5MT3FIYWYBAWxbMD/SRSwiMCWeAZ8aJhkoHhsMHiN4XwsKCUA2RFmCeGmREgABAAD/\
BgGQAPsAKAAANyIOAwc3PggzMhYzMjY3DgQVIzY3Njc2NwYjIiZhFBoUCREFCgEMAgsFCwkNEAkv\
dSMaOxEbRRoeCIIBCBFoHi0RHiVgoQUOCRsGdAELAgkBBgEDASYXDkOnQVxCK0UbNocnOQooAAMA\
AP8GAYQA+gAOABwANAAAFw4BFRQWMzI2NTQuAzc+ATU0JiIGFRQeAwcuATU0NjcyFhUUBgceARUU\
BiMiJjU0Npo2LFgsKj8PIB0xPjMjRFIzChwSMXAxKWZKS2UqMDoydU1MdjkqGSQbHTApHw4XEg0T\
WhoiHB0wKCAPFxMKFT8YPDUzTQFGMic0Fxo6NTdKSDAkNQAAAgAA/wYBgQD6AAkALAAANyIGFRQW\
MzI3JgMmNTQ2MzIWFRQGIxYzMjY1DgIjIiY1NDYzMhYXDgEjIia5KS0qKCwwB90bIxgeIhsQFjc1\
LxUYLR5MT3FIYWYBAWxbMD/SRSwiMCWe/mEaJhkoHhsMHiN4XwsKCUA2RFmCeGmREgABAAn/CgGZ\
APkAMAAAJTAXNjU0JiMOARUUFxYzMjc2NxQeARUOAQciJyYnNCY1NDcyFhcWFRQGIyImNT4BMwEv\
EgQ8HzJBJyEwKygcKgkIG1VWTzs7BAHbJEARIiQcICkCIBqkAwUIFCICZWuOMyoiGFgBBAMBVVAB\
OTlmAisC5gIeFCckJTkuHBYmAAIACf6iAZkBXgA4AD8AACUwFzY1NCYjIgcRFjMyNzY3FB4BFQ4B\
ByMVIzUmJyYnNCY1NDc1MxUyNjMyFhcWFRQGIyImNT4BMwMRBhUUFxYBLxIEPB8DEAwMKygcKgkI\
G1FVASM9LjsEAasjAwcDJEARIiQcICkCIBp6PScKpAMFCBQiBP5LBCIYWAEEAwFUUQFoawktOWYC\
KwLLGWlmAR4UJyQlOS4cFib+lwGWLpGOMw0AAAEAAP8GAfQA+gALAAA1MzUzFTMVIxUjNSPXRtfX\
Rtcj19dG19cAAAABABT+BgDjAgAAEwAAExYHBicmAjU0Ejc2FxYHBgIVFBLcBw0JBUlra0kJCwYG\
PEZH/hYIBQMGVwEgfXwBIlYLBwYISf7niIb+5QAAAQAK/gIA3AH9ABMAABM2EjU0AicmNzYXFhIV\
FAIHBicmFDtHRjwJDAoHSWtrSQgLBv4WSQEbhogBGUkLBAQJVv7efH3+4FcJCQQAAAQAJP9WAkwA\
qgALAA8AEwAeAAAFNCYjIgYVFBYzMjY3MxEjATMRIyQUBiMiJjU0NjMyAahUPCA0Vz0hL3IyMv4K\
MjIB9XtmZXx5aGYgNU4nHzVLI+r+rAFU/qzmeEZJOT9DAAACAAD/JAISANwAAwAPAAA3FSE1JTMV\
ITUzESM1IRUjHgHW/gweAdYeHv4qHkGCgps3N/5INzcAAAIAAP9/AcIAgQALABMAAAU0JiMiBhUU\
FjMyPgEUBiImNDYyAVFaNiA0XTchL3F9yH19yB40TycfNEwjdGxLS2xLAAAAAgAA/2wBSACUAA0A\
GwAAJSYjIgYVFBcWMzI2NTQ3FhUUBiMiJyY1NDYzMgEkDSU8lwYLJjyXEA9+SE8kD35IT0QXYSsK\
CRdhKwkXHh1DZ0MeHUNnAAAAAAEAAP95AUAAhwALAAAlFAYjIiY1NDYzMhYBQHlZMjx6WDI8KEZp\
OCdFajgAAf/+/28BTACRAAsAACc3FzcXBxcHJwcnNwIbjIwbhoYci4schnEgdnUgcHAhdXUhcAAA\
AAUAAP9qASwAlgAFAAsAEQAXAB8AABcHFjMyNy8BBhUUFz8BJiMiBx8BNjU0JwY0NjIWFAYilkcd\
KikfXUcdHVxIHykqHVxHHR3yWHxYWHwSSB0dWkkfKSodWEgdHVpIHykqHYV8WFh8WAAAAAEAAP8G\
AjAA+gADAAAVATMBAbh4/kf6AfT+DAABAAD/dAFKAIwAAwAAMTcXB6WlpYyMjAABAAD/dAFKAIwA\
AgAAFRsBpaWMARj+6AABAAD/zgBkADIABwAAFiImNDYyFhRHKh0dKh0yHSodHSoAAAABAAD87wE7\
AAAADwAAFTUzHgQVFAc2NTQmJx4GP1FQNy4SkXDv7zVwZWyJSWBpQUmP3yoAAQAAAAABOwMRAA8A\
ADE1Mz4BNTQnFhUUDgMHHnCSEy43UFE/Bu8f4ZNHSGdeSIptZ3E1AAIAAP1EATwAAAAWACMAABkB\
Mx4EFRQHFhUUBzY1NC4DIzUeAxc2NTQuAx4JQlBONBITHgUoPklAFglETVYVASk+SUD+qQFXJFBK\
NTQmJy4BIyIGBz4BMzIWFRQHBiMiJgUiJjU0NjMyFhUUBiciJjU0NjMyFhUUBhOLZ1VrKy9GVWbP\
leWFMTQTChEdHDQzO2EWGCMbLj0nITEzRQKCFh0aFBUeGhoXGxwVFBwbHWKANDk+cn+6T2BNCD2F\
MWNQK2ZHUCMiF09GHhdBLzIgHlGKHBcWHB0VFh3yHhkVGhsUGR4AAAIAAP8GAXIA+gADAAcAADcz\
EyMDMxMj3JQCltyUApb6/gwB9P4MAAAEAAD+CgIfA6oACQAgAGIAbQAAJRYXPgE1NCYjIgMCJw4B\
FRQXLgE1NDY3JicOAQceATMyFx4BHwEdARQjIiY1NDYzMhYVFAYHFjMyNjU0LwEGIyImNTQ3PgE3\
PgI3JjU0NjceARUUBgceARc2MzIXFhUUBwYDBhUUFz4BNTQnBgFJEwZNR1ZCDg0YASw5HyAqTDsF\
CXFVAQVtggMiAwYCAno2UzMoJTUxJxUiIywBDQkVjZkuDkweBSMnEg5ZQC8ZSWECCAQSCFw5MmM2\
ZAMGN14pX3XEWxJPMzZW/uABCxAJNCcpJhJBKThOED5aWnlTcX4aIEAXFxsHf0gxJDM3JCIoAQwz\
NQ8JjQGSimpRHFAYBCAhDcIHbn8TM2JbbYdMEW4kAkM2YncwGwNWHiI5JCV7M0YmJgACAAD+YwJC\
AZoAYwBnAAABMjY1NCcmIyIHDgEHJicmJxEjETMRNjc2Nx4DFxYzMjY1NCcmIyIHFhcUFhUUBisB\
JjU0NzY3NjMyFxYXFRQGBwYjIicHFzYzMhYXFh0BBgcGIyImNTQ3MzIWFRQGFQYHFgEzESMBfjI7\
ChU6ODACDAQdExglFhYlGBMdBBEKEwwgHScxDhxDJScoCgIqGgQ2BBVEIxhJRi0GSDoYJSsxGxs0\
KDVSGCAGLUZJPVs2BBoqAgwmK/6jYmL+gGA7JCRXOwUdC1IcIhn+agMz/nQZIhxSCCISFwgWYjUr\
KE4VDSACCwMYIhQoEAk1FQtAK0EOPloWChI8OxMyIy41D0ErQD8wKBQjGAMLAx8NFgMY/M0AAAAA\
A//I/h4CNgDMACYALwA7AAA3NDYzMhcWFRQHDgEHNjc2NzY1NCcuASMiBgc+ATMyFhUUBwYjIiYF\
IiY0NjIWFAYnIiY1NDYzMhYVFAYPcFKCPCN4Q8ZpwF5HHgktGC0gMFIKEhYWJTkgGSUsOQICEhcU\
IhgVFRIWFRIRFhUhTV5XM1rGbDtXBjNkSowvKWYzGhRCNhcNPCYoGhhTdRckFhciGMIYFBEUFRAT\
GQAAAAADAAAAAADIAPAACQATACkAADcGFRQWMzI2NTQnIgYVFBc2NTQmByImNTQ2Ny4BNTQ2MzIW\
FRQHFhUUBlAjGxARFAIOEyAjEzAlNSMiEQwoHiQ2PCg0dxQdFB4bERaGEQ4XGQshDxTcKB4XGgsP\
ExAaIiAcIxAgGyAmAAAAAgAA/wYBrgD6AAsAFAAAMxQWMzI2NTQmIyIGBzQ2MhYUBiImiiojIisn\
JiUoin20fX20fWJ4eWFldXZhZ5CR0pGSAAABAAD/BgEiAPoACQAAMTczERcVIzU3EWR9QfBB+v4+\
HhQUHgEsAAAAAQAA/wYBjwD6ADwAADcyFRQHDgMHNjMyFjMyNz4CMw4CBwYHBiMiJiMiBiMiNTQn\
PgU1NCciBzIWFRQGIyI1ND4Bx8gFDTZAbzYTIBtkHBgeBRAMAQEFBQEHEBopGnQVH1YCBwECLD5H\
PChTThocKTceTDxY+n4aDiEuHUQtDCMOAw0LBRYWAykOGCcmEAECIUU4PDI4GGIBNSUeHylnKDkZ\
AAABAAD/BgF1APoAOQAANzIWFRQGIyImNTQ3NjMyFxYVFAYHHgEVFAcGIyImJyY0NjMyFhUUBiMW\
MzI2NTQmJyY0Nz4BNCYjImYbIichGzIfM1lGJkRGPT5RSyRNJ1cYIzIgIiolGww/JCtILhYWL0sp\
JDyqHBcbIysjLxoqEyJILkQLC0UtQycTFhQdTC4hGxkeKTEnJjoIBCIECTdQMAAAAQAA/wYBkAD6\
ABEAAAUXIzc1IzU2NTMBMz8BETMVIwFFMsgy4ZOj/vuwAWNLS9EpKTEo8oD+jpaR/tkoAAAAAAEA\
AP8HAX4A+gAvAAAXNjMyFhUUBiMWMzI3PgE1NCcmIyIHEyEOASsBBzYzMhceARUUBw4CIyInLgE1\
NBIcIRsqIBwaJDEcEwkeHChOSAoBYgs1JdUGOUJTMSErQxZAKyU8KxAeXyAgFxwgIR4UHyA5HBo1\
ASIkOnkeHxVBJU8vEBACFAkyEiMAAAAAAgAA/wYBgQD6AAkALAAAFzI2NTQmIyIHFhMWFRQGIyIm\
NTQ2MyYjIgYVPgIzMhYVFAYjIiYnPgEzMhbIKS0qKCwwB90bIxgeIhsQFjc1LxUYLR5MT3FIYWYB\
AWxbMD/SRSwiMCWeAZ8aJhkoHhsMHiN4XwsKCUA2RFmCeGmREgABAAD/BgGQAPsAKAAANyIOAwc3\
PggzMhYzMjY3DgQVIzY3Njc2NwYjIiZhFBoUCREFCgEMAgsFCwkNEAkvdSMaOxEbRRoeCIIBCBFo\
Hi0RHiVgoQUOCRsGdAELAgkBBgEDASYXDkOnQVxCK0UbNocnOQooAAMAAP8GAYQA+gAOABwANAAA\
Fw4BFRQWMzI2NTQuAzc+ATU0JiIGFRQeAwcuATU0NjcyFhUUBgceARUUBiMiJjU0Npo2LFgsKj8P\
IB0xPjMjRFIzChwSMXAxKWZKS2UqMDoydU1MdjkqGSQbHTApHw4XEg0TWhoiHB0wKCAPFxMKFT8Y\
PDUzTQFGMic0Fxo6NTdKSDAkNQAAAgAA/wYBgQD6AAkALAAANyIGFRQWMzI3JgMmNTQ2MzIWFRQG\
IxYzMjY1DgIjIiY1NDYzMhYXDgEjIia5KS0qKCwwB90bIxgeIhsQFjc1LxUYLR5MT3FIYWYBAWxb\
MD/SRSwiMCWe/mEaJhkoHhsMHiN4XwsKCUA2RFmCeGmREgABAAn/CgGZAPkAMAAAJTAXNjU0JiMO\
ARUUFxYzMjc2NxQeARUOAQciJyYnNCY1NDcyFhcWFRQGIyImNT4BMwEvEgQ8HzNAJyEwKygcKgkI\
G1VWTzs7BAHbJEARIiQcICkCIBqkAwUIFB4CYmqOMyoiGFgBBAMBVVABOTlmAisC5gIeFCckJTku\
HBYmAAIACf6iAZkBXgA4AD8AACUwFzY1NCYjIgcRFjMyNzY3FB4BFQ4BByMVIzUmJyYnNCY1NDc1\
MxUyNjMyFhcWFRQGIyImNT4BMwMRBhUUFxYBLxIEPB8DEAwMKygcKgkIG1FVASM9LjsEAasjAwcD\
JEARIiQcICkCIBp6PScKpAMFCBQiBP5LBCIYWAEEAwFUUQFoawktOWYCKwLLGWlmAR4UJyQlOS4c\
Fib+lwGWLpGOMw0AAAEAAP8GAfQA+gALAAA1MzUzFTMVIxUjNSPXRtfXRtcj19dG19cAAAABABT+\
BgDjAgAAEwAAExYHBicmAjU0Ejc2FxYHBgIVFBLcBw0JBUlra0kJCwYGPEZH/hYIBQMGVwEgfXwB\
IlYLBwYISf7niIb+5QAAAQAK/gIA3AH9ABMAABM2EjU0AicmNzYXFhIVFAIHBicmFDtHRjwJDAoH\
SWtrSQgLBv4WSQEbhogBGUkLBAQJVv7efH3+4FcJCQQAAAQAJP9WAkwAqgALAA8AEwAeAAAFNCYj\
IgYVFBYzMjY3MxEjATMRIyQUBiMiJjU0NjMyAahUPCA0Vz0hL3IyMv4KMjIB9XtmZXx5aGYgNU4n\
HzVLI+r+rAFU/qzmeEZJOT9DAAACAAD/JAISANwAAwAPAAA3FSE1JTMVITUzESM1IRUjHgHW/gwe\
AdYeHv4qHkGCgps3N/5INzcAAAIAAP9/AcIAgQALABMAAAU0JiMiBhUUFjMyPgEUBiImNDYyAVFa\
NiA0XTchL3F9yH19yB40TycfNEwjdGxLS2xLAAAAAgAA/2wBSACUAA0AGwAAJSYjIgYVFBcWMzI2\
NTQ3FhUUBiMiJyY1NDYzMgEkDSU8lwYLJjyXEA9+SE8kD35IT0QXYSsKCRdhKwkXHh1DZ0MeHUNn\
AAAAAAEAAP95AUAAhwALAAAlFAYjIiY1NDYzMhYBQHlZMjx6WDI8KEZpOCdFajgAAf/+/28BTACR\
AAsAACc3FzcXBxcHJwcnNwIbjIwbhoYci4schnEgdnUgcHAhdXUhcAAAAAUAAP9qASwAlgAFAAsA\
EQAXAB8AABcHFjMyNy8BBhUUFz8BJiMiBx8BNjU0JwY0NjIWFAYilkcdKikfXUcdHVxIHykqHVxH\
HR3yWHxYWHwSSB0dWkkfKSodWEgdHVpIHykqHYV8WFh8WAAAAAEAAP8GAjAA+gADAAAVATMBAbh4\
/kf6AfT+DAABAAD/dAFKAIwAAwAAMTcXB6WlpYyMjAABAAD/dAFKAIwAAgAAFRsBpaWMARj+6AAB\
AAD/zgBkADIABwAAFiImNDYyFhRHKh0dKh0yHSodHSoAAAABAAD/eQFAAqgADwAAESERFAYjIiY1\
NDYzMhcRIQFAeVkyPHpYMR/+3gKo/YBGaTgnRWocAcUAAAEAAP95AUACqAATAAARIREUBiMiJjU0\
NjMyFxEhNSE1IQFAeVkyPHpYMR/+3gEi/t4CqP2ARmk4J0VqHAEReDwAAAAAAQAAAjABQAKoAAMA\
ABEhFSEBQP7AAqh4AAAAAAIAAAF8AUACqAADAAcAABEhFSERIRUhAUD+wAFA/sAB9HgBLHgAAAAB\
AAD/2gBkAD4ABwAAFiImNDYyFhRHKh0dKh0mHSodHSoAAAABAAADAgE2A8AABQAAESEVIRUjATb+\
6B4DwB6gAAAAAAEAAAMqAOYEOAA5AAATIiY1NDYzMhYVFA8BFDMyNjU0JisBIjU0OwEyNjU0JiMi\
FRQWFRQjIiY1NDYzMhYVFA4BFRQWFRQGUCcpEA4PEgsMIRUkEQ8SFBQoFyUODhUCIQwSJSsjLSMj\
FDMDKh4YDhYQDBIHBw0qHw8ZDhAlFQ0TDQMICBwRDBUeIhoYIxMCASIPIy0AAAAAAQAAAwIBNgPA\
AAUAAAEVIzUhNQE2Hv7oA8C+oB4AAAABAAD87wE7AAAADwAAFTUzHgQVFAc2NTQmJx4GP1FQNy4S\
kXDv7zVwZWyJSWBpQUmP3yoAAQAAAAABOwMRAA8AADE1Mz4BNTQnFhUUDgMHHnCSEy43UFE/Bu8f\
4ZNHSGdeSIptZ3E1AAIAAP1EATwAAAAYACYAABkBMx4GFRQHFhUUBzY1NC4DIzUeAxc0NjU0LgMe\
T2IzKi0qKjU6HR03ZEUzGaskVENdKQsJOGRFNBgAAgAA//8BPAK8ABYAIwAAFREzMj4DNTQnFhUU\
BxYVFA4DBzUyPgM1NCcOAx4WQEk+KAUeExI0TlBCCRZAST4pARVWTUQBAVcZM0VkNx0dOjUqKi0q\
M2JPSlAkqxg0RWQ4CQopXUNTAAADAAD9KgE8AJEAGwArADsAABkBMx4GFRQHFhUUBxYVFAc2NTQu\
AiM1HgMXNDY1NCcmJyYjIiceAxc0NjU0JyYnJiMiHgYnMzs4LhwSEhITHgU9V1UcCURNVhUBckZA\
BwUBAQlETVYVAXJGQAcFAf6QAgEbOTM3Oj1IJSwrJy0sKykrNjkZIUR3SCmpJFRBXSkEDQOIXzsI\
BiczOzguHBITHgUpPkhAFglETVYVASk+SEH+qQFXGzkzNzo9SCUsKykrNjkZITdkRTQYqyRUQ10p\
AaskU0JdKQQNA4hfOwgBAAADAAD/VgE8Ar0AGQAmADMAABEzMj4DNTQnFhUUBxYUBxYVFA4DByM3\
Mj4DNTQnDgMnMj4DNTQnDgMeFkBJPigFHhMSEhI0TlBCCR4eFkBJPikBFVZNRAkWQEk+KQEVVk1E\
BAwEN2VFMxkAAAACAAD//wE8ArwAGAAmAAAVETMyPgM1NCcWFRQHFhUUDgUHNTI+AzU0JjUOAx4W\
QEg+KQUeExIcLjg7MycGFkFIPikBFVZNRAEBVxg0RWQ3IRk5NispKywlSD06NzM5G6sZM0VlNwQM\
AVcZM0VkNx0dOjUqKixVKi0qM2JPSlAkqhk0RWQ4CgopXUNUhhk0RWQ4CgopXUNUAAAABAAA/UIB\
PAFUABwAKQA2AEMAABkBMx4EFRQHFhQHFhQHFhUUBzY1NC4DIzUeAxc2NTQuAyceAxc2NTQuAyce\
Axc2NTQuAx4JQlBONBISEhISEx4FKD5JQBYJRE1WFQEpPklAFglETVYVASk+SUAWCURNVhUBKT5J\
QP6oAqwkUEpPYjMqLSpVLCpVLCoqNTodHTdkRTMZqyRUQ10pCwk4ZEU0GKskVENdKQsJOGRFNBir\
JFRDXSkLCThkRTQYAAAABAAA/o4BPAKgABwAKQA2AEMAABkBMzI+AzU0JxYVFAcWFAcWFAcWFRQO\
AyldQ1MAAAAAAwAA/SoBPACRABsAKQA3AAAZATMeBhUUBxYVFAcWFRQHNjU0LgIjNR4DFzQ2NTQu\
AiMnHgMXNDY1NC4CIx4GJzM7OC4cEhISEx4FPVdVHAlETVYVATxWVR0CCURNVhUBPFZVHf6QAgEb\
OTM3Oj1IJSwrJy0sKykrNjkZIUR3SCmpJFRBXSkEDQNEdkgpqyRTQl0pBA0DRHZIKQAAAwAA/1YB\
PAK9ABwAKQA2AAARMzI+AzU0JxYVFAcWFRQHFhUUDgUHIzcyPgI1NCY1DgMnMj4CNTQmNQ4DHhZA\
Awc1Mj4DNTQnDgMnMj4DNTQnDgMnMj4DNTQnDgMeFkBJPigFHhMSEhISEjROUEIJFkBJPikBFVZN\
RAkWQEk+KQEVVk1ECRZAST4pARVWTUT+jgKsGTNFZDcdHTo1KiosVSosVSotKjNiT0pQJKoZNEVk\
SD4pBR4TEhISHC44OzMnBh4eHVVXPQEVVk1ECR1VVz0BFVZNRAFXGDRFZDchGTk2KykrLC0nKywl\
OAoKKV1DVIYZNEVkOAoKKV1DVIYZNEVkOAoKKV1DVAAFAAD9VQE8AhIAIAAtADoARwBUAAAZATMe\
BRUUBxYUBxYUBxYUBxYVFAc2NTQuAyM1HgMXNjU0LgMnHgMXNjU0LgMnHgMXNjU0LgMnHgMXNjU0\
LgMeBzI/RjolEhISEhISEhMeBSg+SUAWCURNVhUBKT5JQBYJRE1WFQEpPklAFglETVYVASk+SUAW\
SD06NzM5G6opSXhEBAwEKV1DVIYpSXhEAw0EKV1DVAAEAAD9QgE8AVQAIAAuADwASgAAGQEzHgYV\
FAcWFRQHFhUUBxYVFAc2NTQuAyM1HgMXNDY1NC4DJx4DFzQ2NTQuAyceAxc0NjU0LgMeBiczOzgu\
HBISEhISEx4FKT5IQBYJRE1WFQEpPkhBFglETVYVASk+SEEWCURNVhUBKT5IQf6oAqwbOTM3Oj1I\
CURNVhUBKT5JQP67A1cfQzpGQVQrKi0qVSwqVSwqVSwqKjU6HR03ZEUzGaskVENdKQsJOGRFNBir\
JFRDXSkLCThkRTQYqyRUQ10pCwk4ZEU0GKskVENdKQoKOGRFNBgAAAUAAP28ATwCeQAfACwAOQBG\
AFMAABkBMzI+AzU0JxYVFAcWFAcWFAcWFAcWFRQOAwc1Mj4DNTQnDgMnMj4DNTQnDgMnMj4DNTQn\
DgMnMj4DNTQnDgMeFkBJPigFHhMSEhISEhISNE5QQgkWQEk+KQEVVk1ECRZAST4pARVWTUQJFkBJ\
PikBFVZNRAkWQEk+KQEVVk1E/bwDVxkzRWQ3HR06NSoqLFUqLFUqLFUqLSozYk9KUCSqGTRFZDgK\
CildQ1SGGTRFZDgKCildQ1SGGTRFZDgKCildQ1SGGTRFZDgKCildQ1QAAAACAAD/ZADhAbAACgAW\
AAA3IgYdATY3NjU0JjcyFhUUBwYjETMRNmcUKyImKx0HIzlLUkQoI3omE7kPODsvGyYmMiNJTFIC\
TP68NAAAAgAA/oYAxQF6AAMADAAAFzc1BxEVNxEjNQcRNxyQkKkZrAFlLZYtAUnoNP3A4jMCQwEA\
AAIAAP6YAP8BaAADAB8AADcVNzUDIzUHNTc1BzU3NTMVNzUzFTcVBxU3FQcVIzUHU1paHjU1NTUe\
Wh01NTU1HVpGpxun/jejD1wPpw9aD6ifHKujD1wPpw9aD6ifHAAAAAEAFP+EAQsAegAeAAAXNSYn\
BzAVIzUzNycwIzUzFRYXNzA1MxUjBgcXMDMVwygMM0g5MzM5SCESNEg5IRM0OXw7Jg0zO0oyMkg5\
IhEzOUciETRIAAQAAP9qAWwBsAAOABwAKwA6AAA3DgEdATI3Njc2NTQnJiM3MhYVFAcGBwYjETMR\
JSwrJy0sKyctLCspKzY5GSE3ZEU0GKskVENdKQQMBDdlRTMZqyRUQ10pAw4DN2VFMxmrJFRDXSkE\
DAQ3ZUUzGQAEAAD+jgE8AqAAHwAsADkARgAAGQEzMj4CNTQnFhUUBxYVFAcWFRQHFhUUDgUHNTI+\
AjU0JjUOAycyPgI1NCY1DgMnMj4CNTQmNQ4DHhxVVz0FHhMSEhISEhwuODszJwYdVVc9ARVWTUQJ\
HVVXPQEVVk1ECR1VVz0BFVZNRP6OAqwpSHdEIRk5NispKywtJyssLScrLCVIPTo3MzkbqilJeEQD\
DQQpXUNUhilJeEQDDQQpXUNUhilJeEQDDQQpXUNUAAAABQAA/VUBPAISACQAMgBAAE4AXAAAGQEz\
HgYVFAcWFRQHFhUUBxYVFAcWFRQHNjU0LgMjNR4DFzQ2NTQuAyceAxc0NjU0LgMnHgMXNDY1NC4D\
Jx4DFzQ2NTQuAx4GJzM7OC4cEhISEhISEhMeBSk+SEAWCURNVhUBKT5IQRYJRE1WFQEpPkhBFglE\
TVYVASk+SEEWCURNVhUBKT5IQf67A1cbOTM3Oj1IJSwrJy0sKyctLCsnLSwrKSs2ORkhN2RFNBir\
JFRDXSkDDQQ3ZUUzGaskVENdKQQMBDdlRTMZqyRUQ10pAw4DN2VFMxmrJFRDXSkEDAQ3ZUUzGQAA\
BQAA/bwBPAJ5ACMAMAA9AEoAVwAAGQEzMj4CNTQnFhUUBxYVFAcWFRQHFhUUBxYVFA4FBzUyPgI1\
NCY1DgMnMj4CNTQmNQ4DJzI+AjU0JjUOAycyPgI1NCY1DgMeHFVXPQUeExISEhISEhIcLjg7MycG\
HVVXPQEVVk1ECR1VVz0BFVZNRAkdVVc9ARVWTUQJHVVXPQEVVk1E/bwDVylId0QhGTk2KykrLC0n\
KywtJyssLScrLCVIPTo3MzkbqilJeEQDDQQpXUNUhilJeEQDDQQpXUNUhilJeEQDDQQpXUNUhilJ\
NhcOAR0BMjc2NzY1NCcmIzcyFhUUBwYHDgEjETMRNk4RHg4eHwwEChARGR0rCRgrNS8fGdERHRAe\
HQsGCxAPFh8qCxkoFjcWHht9AR4QxikrNA0ZHhQVJjkhEiA5NEACRv7BMiYBHRHGKS8wExMcFhUm\
NiQWHD4vGyUCRv7BMgAAAgAy/2QBDQGwAAoAFgAANyIGFRQXFhc1NCYnMhcRMxEiJyY1NDarFyAu\
LhoqMzojIzpSTzl6JhswRUIDyxIeJjQBRP20UlBFIzIAAAT/9f9qAWwBsAAOAB0ALAA7AAA3IyIH\
BhUUFxYXFjM1NCYnMhcRMxEiJicmJyY1NDYXDgEdATI3Njc2NTQnJiM3MhYVFAcGBw4BIxEzETZX\
eEQDDQQpXUNUAAAAAgAA/2QA4QGwAAoAFgAANyIGHQE2NzY1NCY3MhYVFAcGIxEzETZnFCsiJisd\
ByM5S1JEKCN6JhO5Dzg7LxsmJjIjSUxSAkz+vDQAAAIAAP6GAMUBegADAAwAABc3NQcRFTcRIzUH\
ETcckJCpGawBZS2WLQFJ6DT9wOIzAkMBAAACAAD+mAD/AWgAAwAfAAA3FTc1AyM1BzU3NQc1NzUz\
FTc1MxU3FQcVNxUHFSM1B1NaWh41NTU1HlodNTU1NR1aRqcbp/43ow9cD6cPWg+onxyrow9cD6cP\
Wg+onxwAAAABABT/hAELAHoAHgAAFzUmJwcwFSM1MzcnMCM1MxUWFzcwNTMVIwYHFzAzFcMoDDNI\
OTMzOUghEjRIOSETNDl8OyYNMztKMjJIOSIRMzlHIhE0SAAEAAD/agFsAbAADgAcACsAOgAANw4B\
HQEyNzY3NjU0JyYjNzIWFRQHBgcGIxEzETYXDgEdATI3Njc2NTQnJiM3MhYVFAcGBw4BIxEzETZO\
AREQCgQMHx4OHiswGR8WNxcrGAkr6REdEB4dCwYLEA8WHyoLGSgWNxYeG30VFB4ZDTQrKcYQHicy\
AT/9uiUbNDkgEiE5JgEdEcYpLzATExwWFSY2JBYcPi8bJQJG/sEyAAAAAAEAAP7AAKkBQAATAAAT\
MxU3FQcVNxUHFSM1BzU3NQc1N0QeR0dHRx5EREREAUCiDlwOfw5aD6iiDlwOfw5aDwADAAD+mAE6\
AWgAIwAnACsAADc1MxU3FQcVNxUHFSM1BxUjNQ8BIzUHNTc1BzU3NTMVNzUzFQM1BxU3FTc16R4z\
MzMzHj0ePAEeMzMzMx49Hh49Wz3AqJ4PXA+fD1oPtaoSrKQQqJ4PXA+fD1oPtaoSrKT+/Z4Rn7ie\
EZ8AAf/9AAABPwD0ABgAADcGIyImNTQ/ATYvASY1NDYzMjEXBRYVFAcSAgMHCQbPDg7NCAsHAQIB\
Hw4OAQEQCAoDSQcGTwMLChIBawYODQUAAAABAAAAAABQAFAACQAANTQ2MhYUBiMiJhciFxcREBgo\
ERcXIhcYAAAAAQAAAAABQAAoAAMAADE1IRUBQCgoAAAAAQAAAAAAZAEYAAMAADMDMwMoKGQoARj+\
6AAAAAEAAAAAARgBNQAFAAAxGwEjJweMjEFYWgE1/svGxgAAAgAAAAACWAFKAA4AGQAAMTQ2MzIe\
AhUjLgEiBgchIiY0NjMyFhUUBrN5OWtVMw8LouCiCwEcFyUlFxkjI5iyLFGATW6Ghm4kMCQkGBkj\
AAABAAAAAAC2AS0AFwAAEzIWFxYVFAcOASMnJjU0NjU0Iy4BNTQ2VhsbEBoyGUQQBgFHFBsoLQEt\
DBEdMD08HS0DAQIIaxMPASYcHjEAAQAA/wYAggD6AAMAADUzESOCgvr+DAAAAQAAAAAAggD6AAMA\
ADUzFSOCgvr6AAAAAQAA/4MBLAAAAAMAADEhFSEBLP7UfQAAAQAAAAABLAB9AAMAADUhFSEBLP7U\
fX0AAQAA/n4A6wGHABMAABMXBxcmIyIGFRQXJjU0NjMyFyc3Kb1nbDI0HyY4eDQlIiKHZAGH5dnP\
LiQdNTRLTSMtFby0AAABAAD/DQEAAMAAFgAANw4CIyImNTQ2MhYVFAcyNjc2MhcDJ6sDGRoTKzcm\
OCkXIjMhAhUDljA8AQcEKSgfIB4ZHRshLAIC/m8QAAAAAQAA/gwBSADAACQAABcGIyImNTQ2MzIW\
FRQHMj8BBiMiJjU0NjMyFhUUBzI3NjIXAyerKCErNycbHCkXQQs8NhgrNycbHCkXSC4CFQPFLcQM\
KCggIB8ZHRsiygwpKB8gHhkdG00CAv1uDAAAAQAA/gwBjwHAADYAADcGIyImNTQ2MzIWFRQHMj8B\
Ig4BIyImNTQ2MzIWFRQHMjc2MhcBJxMGIyImNTQ2MzIWFRQHMjf2KB8rNycbHCkXPws6ASAcEys3\
ER4OHh8MBAoQERkdKwkYKzUvHxnRER0QHh0LBgsQDxYfKgsZKBY3Fh4bfQEeEMYpKzQNGR4UFSY5\
IRIgOTRAAkb+wTImAR0RxikvMBMTHBYVJjYkFhw+LxslAkb+wTIAAAIAMv9kAQ0BsAAKABYAADci\
BhUUFxYXNTQmJzIXETMRIicmNTQ2qxcgLi4aKjM6IyM6Uk85eiYbMEVCA8sSHiY0AUT9tFJQRSMy\
AAAE//X/agFsAbAADgAdACwAOwAANyMiBwYVFBcWFxYzNTQmJzIXETMRIiYnJicmNTQ2Fw4BHQEy\
NzY3NjU0JyYjNzIWFRQHBgcOASMRMxE2VwEREAoEDB8eDh4rMBkfFjcXKxgJK+kRHRAeHQsGCxAP\
Fh8qCxkoFjcWHht9FRQeGQ00KynGEB4nMgE//bolGzQ5IBIhOSYBHRHGKS8wExMcFhUmNiQWHD4v\
GyUCRv7BMgAAAAABAAD+wACpAUAAEwAAEzMVNxUHFTcVBxUjNQc1NzUHNTdEHkdHR0ceRERERAFA\
og5cDn8OWg+oog5cDn8OWg8AAwAA/pgBOgFoACMAJwArAAA3NTMVNxUHFTcVBxUjNQcVIzUPASM1\
BzU3NQc1NzUzFTc1MxUDNQcVNxU3NekeMzMzMx49HjwBHjMzMzMePR4ePVs9wKieD1wPnw9aD7Wq\
EqykEKieD1wPnw9aD7WqEqyk/v2eEZ+4nhGfAAH//QAAAT8A9AAYAAA3BiMiJjU0PwE2LwEmNTQ2\
MzIxFwUWFRQHEgIDBwkGzw4OzQgLBwECAR8ODgEBEAgKA0kHBk8DCwoSAWsGDg0FAAAAAQAAAAAA\
UABQAAkAADU0NjIWFAYjIiYXIhcXERAYKBEXFyIXGAAAAAEAAAAAAUAAKAADAAAxNSEVAUAoKAAA\
AAEAAAAAAGQBGAADAAAzAzMDKChkKAEY/ugAAAABAAAAAAEYATUABQAAMRsBIycHjIxBWFoBNf7L\
xsYAAAIAAAAAAlgBSgAOABkAADE0NjMyHgIVIy4BIgYHISImNDYzMhYVFAazeTlrVTMPC6LgogsB\
HBclJRcZIyOYsixRgE1uhoZuJDAkJBgZIwAAAQAAAAAAtgEtABcAABMyFhcWFRQHDgEjJyY1NDY1\
NCMuATU0NlYbGxAaMhlEEAYBRxQbKC0BLQwRHTA9PB0tAwECCGsTDwEmHB4xAAEAAP8GAIIA+gAD\
AAA1MxEjgoL6/gwAAAEAAAAAAIIA+gADAAA1MxUjgoL6+gAAAAEAAP+DASwAAAADAAAxIRUhASz+\
1H0AAAEAAAAAASwAfQADAAA1IRUhASz+1H19AAEAAP5+AOsBhwATAAATFwcXJiMiBhUUFyY1NDYz\
MhcnNym9Z2wyNB8mOHg0JSIih2QBh+XZzy4kHTU0S00jLRW8tAAAAQAA/w0BAADAABYAADcOAiMi\
JjU0NjIWFRQHMjY3NjIXAyerAxkaEys3JjgpFyIzIQIVA5YwPAEHBCkoHyAeGR0bISwCAv5vEAAA\
JxscKRdILgEWA/70LVUoISs3JxscKRdBCzwMKCggIB8ZHRsiywkEKSgfIB4ZHRtNAgL8bgwBJAwo\
KCAgHxkdGyIAAAAAAQAA/QwB2gHAAEUAABMGIyImNTQ2MzIWFRQHMj8BBiMiJjU0NjMyFhUUBzI/\
AAEAAP4MAUgAwAAkAAAXBiMiJjU0NjMyFhUUBzI/AQYjIiY1NDYzMhYVFAcyNzYyFwMnqyghKzcn\
AQYjIiY1NDYzMhYVFAcyPwEiDgEjIiY1NDYyFhUUBzI3NjIXASerKCErNycbHCkXQQs6KCErNycb\
HCkXQQs6KB8rNycbHCkXPws6ASAcEys3JjgpF0guAhUD/qkt/jwMKCggIB8ZHRsiygwoKCAgHxkd\
GyLKDCgoICAfGR0bIssJBCkoHyAeGR0bTQIC+24MAAAAAQAA/QwCGQKuAFYAACUGIyImNTQ2MzIW\
FRQHMj8BBiMiJjU0NjMyFhUUBzI/ASIOASMiJjU0NjMyFhUUBzI3NjIXAScTBiMiJjU0NjMyFhUU\
BzI/AQYjIiY1NDYzMhYVFAcyNwE/KCErNycbHCkXQQs2KB8rNycbHCkXPws0ASAcEys3JxscKRdI\
LgEWA/5qLVUoISs3JxscKRdBCzooISs3JxscKRdBCzQMKCggIB8ZHRsixAwoKCAgHxkdGyLHCQQp\
KB8gHhkdG00CAvqADAEkDCgoICAfGR0bIsgMKCggIB8ZHRsiAAEAAP8aA1IA5gALAAA1MxUhNTMR\
GxwpF0ELPDYYKzcnGxwpF0guAhUDxS3EDCgoICAfGR0bIsoMKSgfIB4ZHRtNAgL9bgwAAAEAAP4M\
AY8BwAA2AAA3BiMiJjU0NjMyFhUUBzI/ASIOASMiJjU0NjMyFhUUBzI3NjIXAScTBiMiJjU0NjMy\
FhUUBzI39igfKzcnGxwpFz8LOgEgHBMrNycbHCkXSC4BFgP+9C1VKCErNycbHCkXQQs8DCgoICAf\
GR0bIssJBCkoHyAeGR0bTQIC/G4MASQMKCggIB8ZHRsiAAAAAAEAAP0MAdoBwABFAAATBiMiJjU0\
NjMyFhUUBzI/AQYjIiY1NDYzMhYVFAcyPwEGIyImNTQ2MzIWFRQHMj8BIg4BIyImNTQ2MhYVFAcy\
NzYyFwEnqyghKzcnGxwpF0ELOighKzcnGxwpF0ELOigfKzcnGxwpFz8LOgEgHBMrNyY4KRdILgIV\
A/6pLf48DCgoICAfGR0bIsoMKCggIB8ZHRsiygwoKCAgHxkdGyLLCQQpKB8gHhkdG00CAvtuDAAA\
AAEAAP0MAhkCrgBWAAAlBiMiJjU0NjMyFhUUBzI/AQYjIiY1NDYzMhYVFAcyPwEiDgEjIiY1NDYz\
MhYVFAcyNzYyFwEnEwYjIiY1NDYzMhYVFAcyPwEGIyImNTQ2MzIWFRQHMjcBPyghKzcnGxwpF0EL\
NigfKzcnGxwpFz8LNAEgHBMrNycbHCkXSC4BFgP+ai1VKCErNycbHCkXQQs6KCErNycbHCkXQQs0\
DCgoICAfGR0bIsQMKCggIB8ZHRsixwkEKSgfIB4ZHRtNAgL6gAwBJAwoKCAgHxkdGyLIDCgoICAf\
IzUhFSMZAyAZGfzgGeZ9ff40fX0AAAAAA//w/wYCJgD6AAcADwATAAA2IiY0NjIWFAAiJjQ2MhYU\
BQEzAVAyIyMyIwGIMiMjMiP90gG4fv5HSyMyIyMy/s8jMiMjMloB9P4MAAT/4f8GAwcA+gAHAA8A\
GR0bIgABAAD/GgNSAOYACwAANTMVITUzESM1IRUjGQMgGRn84BnmfX3+NH19AAAAAAP/8P8GAiYA\
+gAHAA8AEwAANiImNDYyFhQAIiY0NjIWFAUBMwFQMiMjMiMBiDIjIzIj/dIBuH7+R0sjMiMjMv7P\
EwAXAAA2IiY0NjIWFAAiJjQ2MhYUBQEzATMBMwFBMiMjMiMCdzIjIzIj/OMBuHv+R3kBuHv+R0sj\
MiMjMv7PIzIjIzJaAfT+DAH0/gwAAv+0/4gBfAEYABkASAAANxYzMjY3PgU1NCYnJiMiBgcGFRQe\
ARciJwczMhQrASI0OwETPgI1NC4CIyIOAwcGJjc2NzYzMhYXPgEzMhYVFAbFAwQSMg4CAwMDAQEH\
IzIjIzJaAfT+DAAE/+H/BgMHAPoABwAPABMAFwAANiImNDYyFhQAIiY0NjIWFAUBMwEzATMBQTIj\
IzIjAncyIyMyI/zjAbh7/kd5Abh7/kdLIzIjIzL+zyMyIyMyWgH0/gwB9P4MAAL/tP+IAXwBGAAR\
ADsAADcWMzI2NzY1NCcmIyIGBwYVFBciJwczMhQrASI0OwETNjU0IyIOAwcGJjc2NzYzMhYXPgEz\
CAMDEzQLDwQHCBYZLTQLC+ELC0toAQMCAQIFAwgMDwsZCgUbBTEPGCQjJAcdJiMeLWsoATUkBQsM\
DAwMBhAXAwEzHCYlDBQNKiB6Hh4BHQIIDgYDBQUCBxQSLBAIDwlYEBkTGh4PNDBHbQAB/9v/9gG+\
MhYVFAbFAgUSMg4NDwIEEjULDycqGS00CwvhCwtLaAYLCAwPCxkKBRsFMQ8YJCMkBx0mIx4tWSgB\
NSQgJiUFATMcJyQpLiB6Hh4BHRIMDwcUEiwQCA8JWBAZExoeDzQwSWsAAAH/2//2Ab4BGABTAAA3\
ARgAVwAANwYHBisBIj8BNiYjIgYHBiY3PgMzMhc2MzIXPgEzMh4DFRQPAQYVFDMyNz4FNzYWBw4C\
IyImNTQ2PwI0IyIPAQYHJwYmPwE2NTQmIyIHUAcIBAQ1DQ1CBAYIDRYkBRUEFBAiHxI3CyQkLQkL\
KRMKEg8LBwUvBAgCAwULBwwDDQEGFQYSFy0fFRkCAzQBFRsIQQgPJg0IBEMBDAkbCBURAwEapw0P\
BgcGKwEiPwE2JiMiBgcGJjc+AzMyFzYzMhc+ATMyFhUUDwEGFRQzMjc+BTc2FgcOAiMiJjU0PwE2\
NTQjIg8BBgcnBiY/ATY1NCMiB1AHCAQENQ0NQgQGCA0WJAUVBBQQIh8SNwskJC0JCykTGSQFLwQI\
AQQFCwcMAw0BBhUGEhctHxUZBTQBFRsIQQgPJg0IBEMBFRsIFREDARqnDQ8aOQgMCSUaMBIoKCgQ\
GjkIDAklGjASKCgoEBgGCw8TCg0NfQsJDgIDCQYPBRIBCQ0LHx8aFRQGDQeIBg4UqxYBAQEPCKsD\
AwYIFAAB/37/YAFeAbgASAAAByImNTQ2MzIWFRQHBhUUMzI+BzcjIiY1ND4DOwE+ATMyFhUUBiMi\
JjU0NzY0IyIOBwczMhYUBisBDgEyIDAXExIXEgoZCxAPCw0KDg0UCjUJCgEDBAYDQRRpNCAwFxMS\
GCMaCw99CwkOAgMJBg8FEgEJDQsfHxoWEw0NiAIEDhSrFgEBAQ8IqwIEDhQAAAAB/37/YAFeAbgA\
QQAAByImNTQ2MzIWFRQHBhUUMzI+BzcjIjU0OwE+ATMyFhUUBiMiJjU0NzY0IyIOBwczMhQrAQ4B\
MiAwFxMSFxIKGQsQDwsNCg4NFAo1ExFBFGk0IDAXExIXEgoZBwwKBwgFBgMGATYTEz8hdqAmIBoi\
FA8OCwcNDgYREyUkPDdVJxUTS18mIBoiFA8OCwYcBQsKFA0aDR4GKMXBAAAB/9sAAAETARMAKgAA\
Nz4BJiMiBg8BBgcGKwEiPwE2JiMiBgcGJjc+ATMyFhc2MzIWFRQjIiY1NNAFAwMFFCQJPwcIBAQ1\
DQ1CBAYIDhciBRUEITgiGx0EHyQaICsPG+cDBwQsGJwRAwEaow0PGjUIDAk8QBoNJx4ZNxYNFAAA\
FxIKGQcMCgcIBQYDBgE2CQoKCT8hdqAmIBoiFA8OCwcNDgYREyUkPDdVJwwJAwUFBAJLXyYgGiIU\
Dw4LBhwFCwoUDRoNHgYMEAzFwQAAAAEAAAAAANoBGAA0AAAzIiY1NDYzMhYVFAcWMzI2NTQuAicm\
NTQ2MzIWFRQGIyIuATU0NyYjIgYVFB4CFx4BFRRQHjISDA4XDAYYFiEJCxgGPDctIjYWEAgQCwUQ\
Dg8ZERIeBRsXKxsQFg0LGAwSFhILDwcNBCYqIy0kGBAYCQ8KCAgUEQ0JEwwRAxIfFVoAAAH/5P//\
AOcBEAA/AAAnBi4BPwEuASMqASMiDgEHBicmNz4BNxYzMj4BMzIXFhQPAQ4BFRQeARcWNicuATU0\
NjMyFRQHBgciJiMuASMiCgYLAQWwCBcRBQwHBREUBQ8EAwgMCwEwIhkgEQcFCAsHngEBECwXCAwD\
AyQUDCYkERIDBQIXNAkSAwQIDQbBBAMbIQQNCwkSHjACAwMEAQERB6cDAwIFAQkLBA8JCAgUCxM3\
AAEAAAAAANwBGAAxAAAzIiY1NDYyFhUUBxYzMjY1NC4CJyY1NDYzMhYVFAYjIiY1NDcmIyIGFRQe\
AhcWFRRQHjIVGhcQDBMWIQkLGAY6Ny0iNhYQDRYHDxEPGRESHgUwKxsQFhAMEhAQFhILDwcNBCUw\
IygkGBAYFA4DDhMRDQkTDBEDICtVAAAB/+T//ADnAQ8APAAAJwYuAT8BIgYjIiYOAQcGJyY3PgE3\
HgEzMjYzMhcWFA8BBhUUMzI2MzIXFjc2JyI1NDYzMhUUBisBLgEjIgoGCwEFsAUfDAMUDB8FDwQD\
CAwLAQ4vFSIrBAgFCweeAgIBDQYaIw0IBQckEg0mKB4dGCUIDgMECA0GwQYGAjIFDQsJEh4wAgEG\
CwEBEQenBgIDAxoKDg0DHQ4VNx4vAxgABf+0/4gFSwEYABsANQDZAPABCwAAJRYzMj4BNz4GNTQm\
JyYjIgYHDgIVFAUWMzI2Nz4FNTQmJyYjIgYHBhUUHgEXIicHMzIUKwEiNDsBEz4CNTQuAiMiDgMH\
KhUJAQEDDQAF/7T/iAVLARgAGwA1ANkA8AELAAAlFjMyPgE3PgY1NCYnJiMiBgcOAhUUBRYzMjY3\
PgU1NCYnJiMiBgcGFRQeARciJwczMhQrASI0OwETPgI1NC4CIyIOAwcGJjc2NzYzMhYXPgEzMhc+\
ATMyFhc+ATMyFz4BMzIWFz4BMzIXPgEzMhYXPgEzMhYVFAYjIicHMzIUKwEiNDsBEz4CNTQmIyIG\
BxUUBiMiJwczMhQrASI0OwETPgI1NCYjIgYHFRQGIyInBzMyFCsBIjQ7ARM0PgM0NTQmIyIGBxUU\
BiUWMzI2Nz4ENTQmJyYjIgYHBhUUBRYzMjY3PgY1NCYnJiMiDgEHBhUUFgIKAwQMHx4JAgMCAgIB\
BiY3Njc2MzIWFz4BMzIXPgEzMhYXPgEzMhc+ATMyFhc+ATMyFz4BMzIWFz4BMzIWFRQGIyInBzMy\
FCsBIjQ7ARM+AjU0JiMiBgcVFAYjIicHMzIUKwEiNDsBEz4CNTQmIyIGBxUUBiMiJwczMhQrASI0\
OwETND4DNDU0JiMiBgcVFAYlFjMyNjc+BDU0JicmIyIGBwYVFAUWMzI2Nz4GNTQmJyYjIg4BBwYV\
FBYCCgMEDB8eCQIDAgICAQEHCAMDEzQLBQcD/ssDBBIyDgIDAwMBAQcIAwMTNAsPBAcIFhktNAsL\
4QsLS2gBAwIBAgUDCAwPCxkKBRsFMQ8YJCMkBx0mIy8TEiIbIyQHHSYjLxMSIhsjJAcdJiMvExIi\
GyMkBx0mIx4ta0kWGS00CwvhCwtLaAEDAgUGDhQUa0kWGS00CwvhCwtLaAEDAgUGDhQUa0kWGS00\
CwvhCwtLaAECAQIFBg4UFGsDgwMEEjIOAgQDAwEHCAMDEzQLD/7LAwQSMg4CAwICAgEBBwgDAwwh\
HgcPCSgBGCoXBAoJCwoKCwUQFwMBMxwNGxgLKAcBNSQFCwwMDAwGEBcDATMcJiUMFA0qIHoeHgEd\
AggOBgMFBQIHFBIsEAgPCVgQGRMaHg8yHBYTGh4PMhwWExoeDzIcFhMaHg80MEdtIHoeHgEdAggO\
BgcIGCMBR20geh4eAR0CCA4GBwgYIwFHbSB6Hh4BHQEDBQUGBwMHCBgjAUdtKAE1JAYNDw4PBxAX\
AwEzHCYlKAcBNSQECgkLCgoLBRAXAwEYJRImJRIaAAAE/7T/iAQGARgAfQCYALQAzgAAISInBzMy\
FCsBIjQ7ARM+AjU0JiMiBgcVFAYjIicHMzIUKwEiNDsBEzQ+AzQ1NCYjIgYHFRQGIyInBzMyFCsB\
IjQ7ARM+AjU0LgIjIg4DBwYmNzY3NjMyFhc+ATMyFz4BMzIWFz4BMzIXPgEzMhYXPgEzMhYVFAYn\
FjMyNjc+BjU0JicmIyIOAQcGFRQWBRYzMj4BNz4GNTQmJyYjIgYHDgIVFAUWMzI2Nz4FNTQmJyYj\
AQcIAwMTNAsFBwP+ywMEEjIOAgMDAwEBBwgDAxM0Cw8EBwgWGS00CwvhCwtLaAEDAgECBQMIDA8L\
GQoFGwUxDxgkIyQHHSYjLxMSIhsjJAcdJiMvExIiGyMkBx0mIy8TEiIbIyQHHSYjHi1rSRYZLTQL\
IgYHBhUUHgEDUhYZLTQLC+ELC0toAQMCBQYOFBRrSRYZLTQLC+ELC0toAQIBAgUGDhQUa0kWGS00\
CwvhCwtLaAEDAgECBQMIDA8LGQoFGwUxDxgkIyQHHSYjLxMSIhsjJAcdJiMvExIiGyMkBx0mIx4t\
C+ELC0toAQMCBQYOFBRrSRYZLTQLC+ELC0toAQMCBQYOFBRrSRYZLTQLC+ELC0toAQIBAgUGDhQU\
awODAwQSMg4CBAMDAQcIAwMTNAsP/ssDBBIyDgIDAgICAQEHCAMDDCEeBw8JKAEYKhcECgkLCgoL\
a0wDBBIyDgIDAgICAQEHCAMDDCEeBw8J/sIDBAwfHgkCAwICAgEBBwgDAxM0CwUHA/7LAwQSMg4C\
BRAXAwEzHA0bGAsoBwE1JAULDAwMDAYQFwMBMxwmJQwUDSogeh4eAR0CCA4GAwUFAgcUEiwQCA8J\
WBAZExoeDzIcFhMaHg8yHBYTGh4PMhwWExoeDzQwR20geh4eAR0CCA4GBwgYIwFHbSB6Hh4BHQII\
DgYHCBgjAUdtIHoeHgEdAQMFBQYHAwcIGCMBR20oATUkBg0PDg8HEBcDATMcJiUoBwE1JAQKCQsK\
CgsFEBcDARglEiYlEhoAAAT/tP+IBAYBGAB9AJgAtADOAAAhIicHMzIUKwEiNDsBEz4CNTQmIyIG\
AwMDAQEHCAMDEzQLDwQHIHoeHgEdAggOBgcIGCMBR20geh4eAR0BAwUFBgcDBwgYIwFHbSB6Hh4B\
HQIIDgYDBQUCBxQSLBAIDwlYEBkTGh4PMhwWExoeDzIcFhMaHg80MEdtKAE1JAQKCQsKCgsFEBcD\
ARglEiYlEhoDARgqFwQKCQsKCgsFEBcDATMcDRsYCygHATUkBQsMDAwMBhAXAwEzHCYlDBQNAAP/\
BxUUBiMiJwczMhQrASI0OwETND4DNDU0JiMiBgcVFAYjIicHMzIUKwEiNDsBEz4CNTQuAiMiDgMH\
BiY3Njc2MzIWFz4BMzIXPgEzMhYXPgEzMhc+ATMyFhc+ATMyFhUUBicWMzI2Nz4GNTQmJyYjIg4B\
BwYVFBYFFjMyPgE3PgY1NCYnJiMiBgcOAhUUBRYzMjY3PgU1NCYnJiMiBgcGFRQeAQNSFhktNAsL\
tP+IAsEBGABXAHEAjQAAMyInBzMyFCsBIjQ7ARM+AjU0LgIjIg4DBwYmNzY3NjMyFhc+ATMyFz4B\
MzIWFz4BMzIWFRQGIyInBzMyFCsBIjQ7ARM0PgM0NTQmIyIGBxUUBicWMzI2Nz4FNTQmJyYjIgYH\
BhUUHgEFFjMyPgE3PgY1NCYnJiMiBgcOAhUUyBYZLTQLC+ELC0toAQMCAQIFAwgMDwsZCgUbBTEP\
4QsLS2gBAwIFBg4UFGtJFhktNAsL4QsLS2gBAgECBQYOFBRrSRYZLTQLC+ELC0toAQMCAQIFAwgM\
DwsZCgUbBTEPGCQjJAcdJiMvExIiGyMkBx0mIy8TEiIbIyQHHSYjHi1rTAMEEjIOAgMCAgIBAQcI\
AwMMIR4HDwn+wgMEDB8eCQIDAgICAQEHCAMDEzQLBQcD/ssDBBIyDgIDAwMBAQcIAwMTNAsPBAcg\
GCQjJAcdJiMvExIiGyMkBx0mIx4ta0kWGS00CwvhCwtLaAECAQIFBg4UFGtMAwQSMg4CAwMDAQEH\
CAMDEzQLDwQHAUoDBAwfHgkCAwICAgEBBwgDAxM0CwUHAyB6Hh4BHQIIDgYDBQUCBxQSLBAIDwlY\
eh4eAR0CCA4GBwgYIwFHbSB6Hh4BHQEDBQUGBwMHCBgjAUdtIHoeHgEdAggOBgMFBQIHFBIsEAgP\
CVgQGRMaHg8yHBYTGh4PMhwWExoeDzQwR20oATUkBAoJCwoKCwUQFwMBGCUSJiUSGgMBGCoXBAoJ\
CwoKCwUQFwMBMxwNGxgLKAcBNSQFCwwMDAwGEBcDATMcJiUMFA0AA/+0/4gCwQEYAFcAcQCNAAAz\
IicHMzIUKwEiNDsBEz4CNTQuAiMiDgMHBiY3Njc2MzIWFz4BMzIXPgEzMhYXPgEzMhYVFAYjIicH\
MzIUKwEiNDsBEzQ+AzQ1NCYjIgYHFRQGJxYzMjY3PgU1NCYnJiMiBgcGFRQeAQUWMzI+ATc+BjU0\
EBkTGh4PMhwWExoeDzQwR20geh4eAR0BAwUFBgcDBwgYIwFHbSgBNSQFCwwMDAwGEBcDATMcJiUM\
FA0CARgqFwQKCQsKCgsFEBcDATMcDRsYCygAAAL/2/+IAysBGAB6AI8AACU2NzYzMhYXPgEzMhYV\
FAYjIicHMzIUKwEiNDsBEz4DNTQmIyIOAwcOASMiJjU0Nj8CNCMiDwEGBycGJj8BNjU0JiMiDwEG\
JicmIyIGBw4CFRTIFhktNAsL4QsLS2gBAwIBAgUDCAwPCxkKBRsFMQ8YJCMkBx0mIy8TEiIbIyQH\
HSYjHi1rSRYZLTQLC+ELC0toAQIBAgUGDhQUa0wDBBIyDgIDAwMBAQcIAwMTNAsPBAcBSgMEDB8e\
BwYrASI/ATYmIyIGBwYmNz4DMzIXNjMyFz4BMzIeAxUUDwEGFRQzMjc+ARcWMzI2Nz4BNTQmJyYj\
IgYHBhUUFgGtMRQYIiMkBx0mIx4ta0kWGS00CwvhCwtLaAEBAwEFBgcPEg4YCBo8LRUZAgM0ARUb\
CEEIDyYNCARDAQwJGwhEBwgEBDUNDUIEBggNFiQFFQQUECIfEjcLJCQtCQspEwoSDwsHBS8ECAID\
DyXRAwQSMg4GBwcIAwMTNAsPCXdsGB0TGh4PNDBHbSB6Hh4BHQIFCQkFBwgNHxs1EDY4FRQGDQeI\
CQIDAgICAQEHCAMDEzQLBQcDIHoeHgEdAggOBgMFBQIHFBIsEAgPCVgQGRMaHg8yHBYTGh4PNDBH\
bSB6Hh4BHQEDBQUGBwMHCBgjAUdtKAE1JAULDAwMDAYQFwMBMxwmJQwUDQIBGCoXBAoJCwoKCwUQ\
FwMBMxwNGxgLKAAAAv/b/4gDKwEYAHoAjwAAJTY3NjMyFhc+ATMyFhUUBiMiJwczMhQrASI0OwET\
Bg4UqxYBAQEPCKsDAwYIFKwRAwEapw0PGjkIDAklGjASKCgoEBgGCw8TCg0NfQsJDgIINTkBNSQP\
JhEQFwMBMxwmJRIaAAL/2/9gAxkBuABXAJ8AADcGBwYrASI/ATYmIyIGBwYmNz4DMzIXNjMyFz4B\
MzIeAxUUDwEGFRQzMjc+BTc2FgcOAiMiJjU0Nj8CNCMiDwEGBycGJj8BNjU0JiMiBxMiJjU0NjMy\
FhUUBwYVFDMyPgc3IyImNTQ2OwE+ATMyFhUUBiMiJjU0NzY0IyIOBwczMhYVFA4BKwEOAVAHCAQE\
PgM1NCYjIg4DBw4BIyImNTQ2PwI0IyIPAQYHJwYmPwE2NTQmIyIPAQYHBisBIj8BNiYjIgYHBiY3\
PgMzMhc2MzIXPgEzMh4DFRQPAQYVFDMyNz4BFxYzMjY3PgE1NCYnJiMiBgcGFRQWAa0xFBgiIyQH\
HSYjHi1rSRYZLTQLC+ELC0toAQEDAQUGBw8SDhgIGjwtFRkCAzQBFRsIQQgPJg0IBEMBDAkbCEQH\
CAQENQ0NQgQGCA0WJAUVBBQQIh8SNwskJC0JCykTChIPCwcFLwQIAgMPJdEDBBIyDgYHBwgDAxM0\
NQ0NQgQGCA0WJAUVBBQQIh8SNwskJC0JCykTChIPCwcFLwQIAgMFCwcMAw0BBhUGEhctHxUZAgM0\
Cw8Jd2wYHRMaHg80MEdtIHoeHgEdAgUJCQUHCA0fGzUQNjgVFAYNB4gGDhSrFgEBAQ8IqwMDBggU\
rBEDARqnDQ8aOQgMCSUaMBIoKCgQGAYLDxMKDQ19CwkOAgg1OQE1JA8mERAXAwEzHCYlEhoAAv/b\
/2ADGQG4AFcAnwAANwYHBisBIj8BNiYjIgYHBiY3PgMzMhc2MzIXPgEzMh4DFRQPAQYVFDMyNz4F\
NzYWBw4CIyImNTQ2PwI0IyIPAQYHJwYmPwE2NTQmIyIHEyImNTQ2MzIWFRQHBhUUMzI+BzcjIiY1\
NDY7AT4BMzIWFRQGIyImNTQ3NjQjIg4HBzMyFhUUDgErAQ4BUAcIBAQ1DQ1CBAYIDRYkBRUEFBAi\
HxI3CyQkLQkLKRMKEg8LBwUvBAgCAwULBwwDDQEGFQYSFy0fFRkCAzQBFRsIQQgPJg0IBEMBDAkb\
CPUgMBcTEhcSChkLEA8LDQoODRQKNQkKCQhBFGk0IDAXExIXEgoZBwwKBwgFBgMGATYJCwUJBj8h\
ARUbCEEIDyYNCARDAQwJGwj1IDAXExIXEgoZCxAPCw0KDg0UCjUJCgkIQRRpNCAwFxMSFxIKGQcM\
CgcIBQYDBgE2CQsFCQY/IXYVEQMBGqcNDxo5CAwJJRowEigoKBAYBgsPEwoNDX0LCQ4CAwkGDwUS\
AQkNCx8fGhUUBg0HiAYOFKsWAQEBDwirAwMGCBT+nyYgGiIUDw4LBw0OBhETJSQ8N1UnDAkIC0tf\
JiAaIhQPDgsGHAULChQNGg0eBgwIBQkGxcEAAAAAAf9+/2ACaQG4AHsAACUjDgEjIiY1NDYzMhYV\
FAcGFRQzMj4HNyMiJjU0PgM7AT4BMzIWFRQGIyImNTQ3NjQjIgcGBxc+ATMyFhUUBiMiJjU0NzY0\
IyIOBwczMhYUBisBDgEjIiY1NDYzMhYVFAcGFRQzMj4HAV+bIXZfIDAXExIXEgoZCxAPCw0KDg0U\
CjUJCgEDBAYDQRRpNCAwFxMSFxIKGSYXAwGbFGk0IDAXExIXEgoZBwwKBwgFBgMGATYJCwsJPyF2\
dhURAwEapw0PGjkIDAklGjASKCgoEBgGCw8TCg0NfQsJDgIDCQYPBRIBCQ0LHx8aFRQGDQeIBg4U\
qxYBAQEPCKsDAwYIFP6fJiAaIhQPDgsHDQ4GERMlJDw3VScMCQgLS18mIBoiFA8OCwYcBQsKFA0a\
XyAwFxMSFxIKGQsQDwsNCg8NFObFwSYgGiIUDw4LBw0OBhETJSQ8N1UnDAkDBQUEAktfJiAaIhQP\
DR4GDAgFCQbFwQAAAAAB/37/YAJpAbgAewAAJSMOASMiJjU0NjMyFhUUBwYVFDMyPgc3IyImNTQ+\
AzsBPgEzMhYVFAYjIiY1NDc2NCMiBwYHFz4BMzIWFRQGIyImNTQ3NjQjIg4HBzMyFhQGKwEOASMi\
JjU0NjMyFhUUBwYVFDMyPgcBX5shdl8gMBcTEhcSChkLEA8LDQoODRQKNQkKAQMEBgNBFGk0IDAX\
ExIXEgoZJhcDAZsUaTQgMBcTEhcSChkHDAoHCAUGAwYBNgkLCwk/IXZfIDAXExIXEgoZCxAPCw0K\
Dw0U5sXBJiAaIhQPDgsHDQ4GERMlJDw3VScMCQMFBQQCS18mIBoiFA8OCwYccwwGAUtfJiAaIhQP\
DgsGHHMMBgFLXyYgGiIUDw4LBhwFCwoUDRoNHgYMEAzFwSYgGiIUDw4LBw0OBhETJSQ8N1UAAAAA\
Af9+/2ADdAG4ALMAABMzPgEzMhYVFAYjIiY1NDc2NCMiBwYHFz4BMzIWFRQGIyImNTQ3NjQjIg4H\
BzMyFhUUDgErAQ4BIyImNTQ2MzIWFRQHBhUUMzI+BzcjDgEjIiY1NDYzMhYVFAcGFRQzMj4HNyMO\
ASMiJjU0NjMyFhUUBwYVFDMyPgc3IyImNTQ+AzsBPgEzMhYVFAYjIiY1NDc2NCMiDgIHBsycFGk0\
IDAXExIXEgoZJhcDAZsUaTQgMBcTEhcSChkHDAoHCAUGAwYBNgkKBAkGPyF2XyAwFxMSFxIKGQsQ\
DwsNCg8NFAqbIXZfIDAXExIXEgoZCxAPCw0KDg0VCpshdl8gMBcTEhcSChkLEA8LDQoODRQKNQkK\
AQMEBgNBFGk0IDAXExIXEgoZDhUOCAYCAQ5LXyYgGiIUDw4LBhxzDAYBS18mIBoiFA8OCwYcBQsK\
FA0aDR4GDAgFCQbFwSYgGiIUDw4LBw0OBhETJSQ8N1UnxcEmIBoiFA8OCwcNDgYREyUkOzhVJ8XB\
JiAaIhQPDgsHDQ4GERMlJDw3VScMCQMFBQQCS18mIBoiFA8OCwYcEyciGwoAAf9+/2AEgAG4AOcA\
DgsGHAULChQNGg0eBgwQDMXBJiAaIhQPDgsHDQ4GERMlJDw3VQAAAAAB/37/YAN0AbgAswAAEzM+\
ATMyFhUUBiMiJjU0NzY0IyIHBgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIWFRQOASsBDgEjIiY1\
NDYzMhYVFAcGFRQzMj4HNyMOASMiJjU0NjMyFhUUBwYVFDMyPgc3Iw4BIyImNTQ2MzIWFRQHBhUU\
MzI+BzcjIiY1ND4DOwE+ATMyFhUUBiMiJjU0NzY0IyIOAgcGzJwUaTQgMBcTEhcSChkmFwMBmxRp\
NCAwFxMSFxIKGQcMCgcIBQYDBgE2CQoECQY/IXZfIDAXExIXEgoZCxAPCw0KDw0UCpshdl8gMBcT\
EhcSChkLEA8LDQoODRUKmyF2XyAwFxMSFxIKGQsQDwsNCg4NFAo1CQoBAwQGA0EUaTQgMBcTEhcS\
AAEXPgEzMhYVFAYjIiY1NDc2NCMiBwYHFz4BMzIWFRQGIyImNTQ3NjQjIg4HBzMyFhQGKwEOASMi\
JjU0NjMyFhUUBwYVFDMyPgc3Iw4BIyImNTQ2MzIWFRQHBhUUMzI+BzcjDgEjIiY1NDYzMhYVFAcG\
FRQzMj4HNyMOASMiJjU0NjMyFhUUBwYVFDMyPgc3IyImNTQ+AzsBPgEzMhYVFAYjIiY1NDc2NCMi\
BwYHFz4BMzIWFRQGIyImNTQ3NjQjIg4HAdicFGk0IDAXExIXEgoZJhcDAZsUaTQgMBcTEhcSChkH\
DAoHCAUGAwYBNgkKCgk/IXZfIDAXExIXEgoZCxAPCw0KDw0UCpshdl8gMBcTEhcSChkLEA8LDQoO\
DRQKmyF2XyAwFxMSFxIKGQsQDwsNCg8NFAqbIXZfIDAXExIXEgoZCxAPCw0KDg0UCjUJCgEDBAYD\
ChkOFQ4IBgIBDktfJiAaIhQPDgsGHHMMBgFLXyYgGiIUDw4LBhwFCwoUDRoNHgYMCAUJBsXBJiAa\
IhQPDgsHDQ4GERMlJDw3VSfFwSYgGiIUDw4LBw0OBhETJSQ7OFUnxcEmIBoiFA8OCwcNDgYREyUk\
QRRpNCAwFxMSFxIKGSYXAwGbFGk0IDAXExIXEgoZBwwKBwgFBgMGAQ8BS18mIBoiFA8OCwYccwwG\
AUtfJiAaIhQPDgsGHAULChQNGg0eBgwQDMXBJiAaIhQPDgsHDQ4GERMlJDw3VSfFwSYgGiIUDw4L\
PDdVJwwJAwUFBAJLXyYgGiIUDw4LBhwTJyIbCgAB/37/YASAAbgA5wAAARc+ATMyFhUUBiMiJjU0\
NzY0IyIHBgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIWFAYrAQ4BIyImNTQ2MzIWFRQHBhUUMzI+\
BzcjDgEjIiY1NDYzMhYVFAcGFRQzMj4HNyMOASMiJjU0NjMyFhUUBwYVFDMyPgc3Iw4BIyImNTQ2\
MzIWFRQHBhUUMzI+BzcjIiY1ND4DOwE+ATMyFhUUBiMiJjU0NzY0IyIHBgcXPgEzMhYVFAYjIiY1\
NDc2NCMiDgcB2JwUaTQgMBcTEhcSChkmFwMBmxRpNCAwFxMSFxIKGQcMCgcIBQYDBgE2CQoKCT8h\
dl8gMBcTEhcSChkLEA8LDQoPDRQKmyF2XyAwFxMSFxIKGQsQDwsNCg4NFAqbIXZfIDAXExIXEgoZ\
CxAPCw0KDw0UCpshdl8gMBcTEhcSChkLEA8LDQoODRQKNQkKAQMEBgNBFGk0IDAXExIXEgoZJhcD\
AZsUaTQgMBcTEhcSChkHDAoHCAUGAwYBDwFLXyYgGiIUDw4LBhxzDAYBS18mIBoiFA8OCwYcBQsK\
FA0aDR4GDBAMxcEmIBoiFA8OCwcNDgYREyUkPDdVJ8XBJiAaIhQPDgsHDQ4GERMlIzw3VifFwSYg\
GiIUDw4LBw0OBhETJSQ8N1UnxcEmIBoiFA8OCwcNDgYREyUkPDdVJwwJAwUFBAJLXyYgGiIUDw4L\
BhxzDAYBS18mIBoiFA8OCwYcBQoLEw4ZDh0AAwAA/2AC3wG4ADcAewDFAAAzIiY1NDYzMhYVFAcW\
MzI2NTQuAycuAjU0NjMyFhUUBiMiLgE1NDcmIyIGFRQeAhceARUUJQYuAT8BLgIjIgYjIg4BBwYn\
Jjc+ATceATMyNjMyFhcWFA8BDgEVFB4BFxY+AScuATU0NjMyFRQHBiMiIy4CIyIGBSImNTQ2MzIW\
FRQHDgEeARUUFjI+BzcjIi4BNTQ2OwE+ATMyFhUUBiMiJjU0NzY0IyIOBwczMhYUBisBDgFQHjIS\
DA4XDAYYFiEDDAUXAxQYFDctIjYWEAgQCwUQDg8ZERIeBRsXASYGCwEFrgQJCgcGHAYFEBIFEAUD\
BwsMARglDiAoBwQLBwsHmgICGioNBQkFAgMkFAwmJhITAwMSIxgIChL+vSAwFxMSFxIEAQECBhYQ\
DwsNCg4NFAo1BgkECQhBFGk0IDAXExIXEgoZBwwKBwgFBgMGATYJCgoJPyF2KxsQFg0LGAwSFhIJ\
Bw0OBhETJSM8N1YnxcEmIBoiFA8OCwcNDgYREyUkPDdVJ8XBJiAaIhQPDgsHDQ4GERMlJDw3VScM\
CQMFBQQCS18mIBoiFA8OCwYccwwGAUtfJiAaIhQPDgsGHAUKCxMOGQ4dAAMAAP9gAt8BuAA3AHsA\
xQAAMyImNTQ2MzIWFRQHFjMyNjU0LgMnLgI1NDYzMhYVFAYjIi4BNTQ3JiMiBhUUHgIXHgEVFCUG\
LgE/AS4CIyIGIyIOAQcGJyY3PgE3HgEzMjYzMhYXFhQPAQ4BFRQeARcWPgEnLgE1NDYzMhUUBwYj\
IiMuAiMiBgUiJjU0NjMyFhUUBw4BHgEVFBYyPgc3IyIuATU0NjsBPgEzMhYVFAYjIiY1NDc2NCMi\
DgcHMzIWFAYrAQ4BUB4yEgwOFwwGGBYhAwwFFwMUGBQ3LSI2FhAIEAsFEA4PGRESHgUbFwEmBgsB\
Ba4ECQoHBhwGBRASBRAFAwcLDAEYJQ4gKAcECwcLB5oCAhoqDQUJBQIDJBQMJiYSEwMDEiMYCAoS\
/r0gMBcTEhcSBAEBAgYWEA8LDQoODRQKNQYJBAkIQRRpNCAwFxMSFxIKGQcMCgcIBQYDBgE2CQoK\
CT8hdisbEBYNCxgMEhYSCQwNBA4CDRIeDyMtJBgQGAkPCggIFBENCRMMEQMSHxVaAwQIDQa/AgIB\
DA0EDgINEh4PIy0kGBAYCQ8KCAgUEQ0JEwwRAxIfFVoDBAgNBr8CAgEBGB8EDQsIEx4uBAIBCAEB\
AREHowMFAgcDBwsDBA0GCAgUCxM3KxQJAQgGB6smIBoiFA8OCwIFBAYDCAYGERMlJDw3VScGCQYI\
C0tfJiAaIhQPDgsGHAULChQNGg0eBgwQDMXBAAAAAgAF//sB/AGaAAkALAAAAQ8BBhUUMzI2NwcO\
ASMiJjU0PwEjNTM/AQc3MhU2MzIWFRQGIiY1NDcGDwEjATZ/OQIUGEQSDCkyHyIiAzdpcxZZJpgZ\
IDUYHRggFgk3EEJIAQkHuAgDFRcPKBkUJBoLC7MgSi54Ci0pHBUSGxEOEhMPJ9gAAQAA//0BtQDU\
ARgfBA0LCBMeLgQCAQgBAQERB6MDBQIHAwcLAwQNBggIFAsTNysUCQEIBgerJiAaIhQPDgsCBQQG\
AwgGBhETJSQ8N1UnBgkGCAtLXyYgGiIUDw4LBhwFCwoUDRoNHgYMEAzFwQAAAAIABf/7AfwBmgAJ\
ACwAAAEPAQYVFDMyNjcHDgEjIiY1ND8BIzUzPwEHNzIVNjMyFhUUBiImNTQ3Bg8BIwE2fzkCFBhE\
EgwpMh8iIgM3aXMWWSaYGSA1GB0YIBYJNxBCSAEJB7gIAxUXDygZFCQaCwuzIEoueAotKRwVEhsR\
DhITDyfYAAEAAP/9AbUA1AAvAAAlMjU0JwYiJjU+ATMyFhUUBwYjIi8BJiMiFRQXNjMyFhUUBgci\
AC8AACUyNTQnBiImNT4BMzIWFRQHBiMiLwEmIyIVFBc2MzIWFRQGByInJjU0NzYzFh8BFgFwLRoQ\
HBQBFwkkLSkXHiYeohoRLhkQDw0VFA0dGBwoFiIqF6IcMTkgExAWDQ4WMzUzJBUVehI6IBIQFw4P\
EgIaIi0zJBMCEHoTAAEAAP/NAbUBAwA2AAAXIiY1NDc2MxYfATUzFRcWMzI2NTQnBiMiNT4BMzIW\
FRQHBiMiLwEVIzUnJiMiBhUUFzYzMhUUTSAtKBYgKBcuHlwiDxUcFhQSHAEPCSAtKRccJB4uHlwg\
DxYcFRQTHANDJjMkEwIQJGmARxcpHCcQDBsPFUImMyQVFSRsg0cWKB4nDwwdIAABAA0AAAJFAOAA\
CwAANyc3FzcXNxcHJwcnIhWOZXhqTRaSaXRpLhmZfHx8VBehfHx8AAAAAQAN/8sCRQERABMAACUH\
JyY1NDc2MxYfARYBcC0aEBwUARcJJC0pFx4mHqIaES4ZEA8NFRQNHRgcKBYiKheiHDE5IBMQFg0O\
FjM1MyQVFXoSOiASEBcODxICGiItMyQTAhB6EwABAAD/zQG1AQMANgAAFyImNTQ3NjMWHwE1MxUX\
FjMyNjU0JwYjIjU+ATMyFhUUBwYjIi8BFSM1JyYjIgYVFBc2MzIVFE0gLSgWICgXLh5cIg8VHBYU\
EhwBDwkgLSkXHCQeLh5cIA8WHBUUExwDQyYzJBMCECRpgEcXKRwnEAwbDxVCJjMkFRUkbINHFige\
Jw8MHSAAAQANAAACRQDgAAsAADcnNxc3FzcXBycHJyIVjmV4ak0Wkml0aS4ZmXx8fFQXoXx8fAAA\
AAEADf/LAkUBEQATAAAlBycHJzcXNzUzFzcXNxcHJwcVIwEWQGlLFY5lFhsBRmpPFJJpGRtGRnxO\
JwcnNxc3NTMXNxc3FwcnBxUjARZAaUsVjmUWGwFGak8UkmkZG0ZGfE4ZmXwXlntKfFUYoXwblgAA\
AQAAAAABGAEYAAsAADM1IzUzNTMVMxUjFXt7eyJ7e3sie3siewAAAAEAAAAAATYBcgAKAAAxNT4E\
NzMUBiQxTDQ0Dx7APAcPKz1vSY7aAAAB//8AAAEtAKAAHQAANz4CMzIeARcWMzI3NhYHDgIjIi4B\
JyYjIgcGJgEJECkcGCYmDwkKHhgEDgIIESkcGCQlEgcIHR0FDUYZISAhMQoGJAYHBxkiHyExCgQj\
BggAAAABAAAAAAEsASwABwAAMREhESM1IxUBLCPmASz+1LS0AAEAAAAAAPoBwgAGAAAzAzMbATMD\
aWkoVVUoaQHC/pgBaP4+AAIAAAAAAMgAyAAHAA8AADYyNjQmIgYUFiImNDYyFhRFPiwsPix0Ujs7\
UjsZLD4sLD5FO1I7O1IAAf84AAAAyADIAAsAACM0NjIWFSM0JiIGFch2pHYeYJRgUnZ2UkpgYEoA\
AAACAAAAAAC0ASwABwAVAAA2MjY0JiIGFBc1LgE1NDYyFhUUBgcVSx4bGx4bGB0rN0Y3Kx14NTY1\
NTatZAg1Jyg8PCgnNQhkAAACAAAAAADIASwADwAfAAA3LgE1NDYyFhUUBgcdASM1Nz4BNTQmIgYV\
FBYXPQEzFVQkMDtSOzAkICAZIiw+LCIZIGYFOCUpOzspJTgFAWVlGQYqGh8sLB8aKgYBSUkAAAAE\
AAD//AP0An8AhwCRAJ0ApwAANz4BNTQuAScuATU0PgI/Ag4BFRQzMjcXDgEjIiY1ND4CMzIWFRQG\
IyImJzceATMyNTQuAicHBhUUHgIVFAYPAR4CMzoBMzI3JjU0NzYzMhYVFAcGBx4BMzI2NTQ2Ny4C\
PQEeARUUBiMiJwYjIi4BJw4BIiYnJiMiDgEHBiMiNTQ2BSImNDYzMhYUBiU+ATU0JyYjIgYVFAU2\
NTQmJw4BFRSYNkUBAgIDUgMEBQECPWJqIB4dGhUpJR4tHz5wSH1yMi4cNhIYERQUMwwcQC0bCR0i\
HR8QEA4pHwwDCAIQIyEcIkwbIwodPBMgGxYxQ10ggV66tFFCQSMkPCEzFg4sKCAbHBoSChQZBloh\
CjADXAsTEwsMEhH+DzMkBAUVHC4BPFoXEj8wWhg/IAQHBwQIdCIFDg4MBASlBGMsIzwNQi8mGiFH\
RCxRNzA1MCsOHhEyChoiGgJCJBseNiEsFSZPFBQKKRoeNSg1JS4rICETOTAXEioZUl8ZLFk0BgE8\
sWtGXzk6FRQSJhkWIC0LFQQ6Bw04TxIYEhIYEoAvLxsHDhI5Kx50BYEiSRUdWEQ/AAAAAAIADAAK\
AdMBzwAKAI8AACU0JiMiBhQWMzI2Jw4BIyImNDYzMhYXNjU0JyYjIiY0NjMyFx4BFxYzMjU0Jy4B\
NTQ2MzIWFRQGBxQzMjc+ATc2MzIWFRQGIyIGBwYVFDMyNjMyFhQGIyImIyIGFRQXHgEXFhUUBiMi\
Jy4BJyYjIhUUFhUUBiMiJjQ2NTQjIgcOAQcGIyImNTQ3NjMyNzY1NAEWGA8QFRYPEBeJGSYOGRsa\
GQ0qGCUMFBgdHBkXEhALAxQLEhQBAiceFBIbIwEWEA0RAQ0MGhMeGxIbFQ0QIRsrDhwbHRkPJxQX\
EgsUOA0OGRcTFAwBExEKEiogExIdJhcNDhIDERIMFBoNDBYjEg3uEBQTIhYVCgElGSobJQIDFg4K\
GZl8F5Z7SnxVGKF8G5YAAAEAAAAAARgBGAALAAAzNSM1MzUzFTMVIxV7e3sie3t7Int7InsAAAAB\
AAAAAAE2AXIACgAAMTU+BDczFAYkMUw0NA8ewDwHDys9b0mO2gAAAf//AAABLQCgAB0AADc+AjMy\
HgEXFjMyNzYWBw4CIyIuAScmIyIHBiYBCRApHBgmJg8JCh4YBA4CCBEpHBgkJRIHCB0dBQ1GGSEg\
ITEKBiQGBwcZIh8hMQoEIwYIAAAAAQAAAAABLAEsAAcAADERIREjNSMVASwj5gEs/tS0tAABAAAA\
AAD6AcIABgAAMwMzGwEzA2lpKFVVKGkBwv6YAWj+PgACAAAAAADIAMgABwAPAAA2MjY0JiIGFBYi\
JjQ2MhYURT4sLD4sdFI7O1I7GSw+LCw+RTtSOztSAAH/OAAAAMgAyAALAAAjNDYyFhUjNCYiBhXI\
dqR2HmCUYFJ2dlJKYGBKAAAAAgAAAAAAtAEsAAcAFQAANjI2NCYiBhQXNS4BNTQ2MhYVFAYHFUse\
GxseGxgdKzdGNysdeDU2NTU2rWQINScoPDwoJzUIZAAAAgAAAAAAyAEsAA8AHwAANy4BNTQ2MhYV\
FAYHHQEjNTc+ATU0JiIGFRQWFz0BMxVUJDA7UjswJCAgGSIsPiwiGSBmBTglKTs7KSU4BQFlZRkG\
KRsfLCwfGykGAUlJAAAABAAA//wD9AJ/AIcAkQCdAKcAADc+ATU0LgEnLgE1ND4CPwIOARUUMzI3\
Fw4BIyImNTQ+AjMyFhUUBiMiJic3HgEzMjU0LgInBwYVFB4CFRQGDwEeAjM6ATMyNyY1NDc2MzIW\
FRQHBgceATMyNjU0NjcuAj0BHgEVFAYjIicGIyIuAScOASImJyYjIg4BBwYjIjU0NgUiJjQ2MzIW\
FAYlPgE1NCcmIyIGFRQFNjU0JicOARUUmDZFAQICA1IDBAUBAj1iaiAeHRoVKSUeLR8+cEh9cjIu\
HDYSGBEUFDMMHEAtGwkdIh0fEBAOKR8MAwgCECMhHCJMGyMKHTwTIBsWMUNdIIFeurRRQkEjJDwh\
MxYOLCggGxwaEgoUGQZaIQowA1wLExMLDBIR/g8zJAQFFRwuATxaFxI/MFoYPyAEBwcECHQiBQ4O\
DAQEpQRjLCM8DUIvJhohR0QsUTcwNTArDh4RMgoaIhoCQiQbHjYhLBUmTxQUCikaHjUoNSUuKyAh\
EzkwFxIqGVJfGSxZNAYBPLFrRl85OhUUEiYZFiAtCxUEOgcNOE8SGBISGBKALy8bBw4SOSsedAWB\
IkkVHVhEPwAAAAACAAwACgHTAc8ACgCPAAAlNCYjIgYUFjMyNicOASMiJjQ2MzIWFzY1NCcmIyIm\
NDYzMhceARcWMzI1NCcuATU0NjMyFhUUBgcUMzI3PgE3NjMyFhUUBiMiBgcGFRQzMjYzMhYUBiMi\
JiMiBhUUFx4BFxYVFAYjIicuAScmIyIVFBYVFAYjIiY0NjU0IyIHDgEHBiMiJjU0NzYzMjc2NTQB\
FhgPEBUWDxAXiRkmDhkbGhkNKhglDBQYHRwZFxIQCwMUCxIUAQInHhQSGyMBFhANEQENDBoTHhsS\
GxUNECEbKw4cGx0ZDycUFxILFDgNDhkXExQMARMRChIqIBMSHSYXDQ4SAxESDBQaDQwWIxIN7hAU\
EyIWFQoBJRkqGyUCAxYOChMcKB0MCz4WDRgNCBcnDxcaGxYRIxkqEBM4Dw4bFBEjBgsODxYnHSga\
JggKFgoSAwsNGRMbDgs5FQ8fHjATFBgaJDEXJA4SPQwHFhQYDg0SDQwaAAAAAwAA/wYB9AD6AAcA\
DwAXAAA2FBYyNjQmIgI0NjIWFAYiNiImNDYyFhQteKp4eKqlktCSktCBMiMjMiNVqnh4qnj+y9CS\
ExwoHQwLPhYNGA0IFycPFxobFhEjGSoQEzgPDhsUESMGCw4PFicdKBomCAoWChIDCw0ZExsOCzkV\
Dx8eMBMUGBokMRckDhI9DAcWFBgODRINDBoAAAADAAD/BgH0APoABwAPABcAADYUFjI2NCYiAjQ2\
MhYUBiI2IiY0NjIWFC14qnh4qqWS0JKS0IEyIyMyI1WqeHiqeP7L0JKS0JK+IzIjIzIAAAIAAP8G\
AfQA+gAHAA8AADYUFjI2NCYiAjQ2MhYUBiIteKp4eKqlktCSktBVqnh4qnj+y9CSktCSAAAAAAMA\
AP6iAfQBXgARABcAHQAAEzMVHgEVFAYHFSM1LgE1NDY3GQEOARQWFz4BNCYn5C1gg4NgLWCEg2FN\
amp6TWlpTQFeZAqOYmGPCmRlCI9iY44I/jsBmAh1nnUICXSedAkAAAACAAD/BgHSAPoAIQApAAAl\
FhUUBwYjIicmIyIGFBYzMjc2MzIXFhUUBwYjIiY0NjMyAiImNDYyFhQBzwILBQYNCjN3VXh4VXgy\
BhIHAwwDQJVokpJolHsyIyMyI4IGBA8GAw5feKp4Xw0CBwwGBniS0JL+yiMyIyMyAAAAAQAA/wYB\
0gD6ACEAACUWFRQHBiMiJyYjIgYUFjMyNzYzMhcWFRQHBiMiJjQ2MzIBzwILBQYNCjN3VXh4VXgy\
BhIHAwwDQJVokpJolIIGBA8GAw5feKp4Xw0CBwwGBniS0JIAAAIAAP6iAdIBXgAkACoAACUWFxYV\
FAcGIyInJicRNjc2MzIXFhUUBwYHFSM1LgE1NDY3NTMDEQ4BFBYBEYQ6AgsFBg0KLWZlLgYSBwMM\
AzyCLWCEg2EtLU5pafkKbQYEDwYDDlQK/mgHVw0CBwwGBnAHZWUIj2Jjjghl/dYBmAh1nnUAAAEA\
AP/EAHgAPAAHAAAWIiY0NjIWFFUyIyMyIzwjMiMjMgAAAAEAAP6iAC0BXgADAAATESMRLS0BXv1E\
ArwAAAACAAD9EgFKAL4AAwAPAAA3FSE1JTMVITUzESMRIRUjHgEO/tQeAQ4eHv7yHkaMjHgyMvxU\
AmIyAAEAAAAAAlYBcgALAAA1Nxc3FzcXAScHJweJVFZSryL+/FRWUzVBuXNzcekW/qR0dHBHAAAB\
/+oAvQEQATcAGQAAET4BMzIWMjc2MzIVFAcOASMiJiIHBiMiNTQYIRwUVSgUAwcMFhghHBRVKBQE\
BwsBDhgRPRQDCQwWGBE9FAQKDAAAAAAADgCuAAEAAAAAAAAAfgD+AAEAAAAAAAEABwGNAAEAAAAA\
AAIABwGlAAEAAAAAAAMAJAH3AAEAAAAAAAQABwIsAAEAAAAAAAUACQJIAAEAAAAAAAYABwJiAAMA\
AQQJAAAA/AAAAAMAAQQJAAEADgF9AAMAAQQJAAIADgGVAAMAAQQJAAMASAGtAAMAAQQJAAQADgIc\
AAMAAQQJAAUAEgI0AAMAAQQJAAYADgJSAEMAbwBwAHkAcgBpAGcAaAB0ACAAXAAyADUAMQAgADIA\
MAAxADgAIABKAGUAYQBuAC0ARgByAGEAbgBjAG8AaQBzACAATQBvAGkAbgBlAC4AIABUAGgAaQBz\
ACAAZgBvAG4AdAAgAGkAcwAgAGwAaQBjAGUAbgBzAGUAZAAgAHUAbgBkAGUAcgAgAHQAaABlACAA\
UwBJAEwAIABPAHAAZQBuACAARgBvAG4AdAAgAEwAaQBjAGUAbgBzAGUAIABcACgAaAB0AHQAcAA6\
AC8ALwBzAGMAcgBpAHAAdABzAC4AcwBpAGwALgBvAHIAZwAvAE8ARgBMAFwAKQAuAABDb3B5cmln\
aHQgXDI1MSAyMDE4IEplYW4tRnJhbmNvaXMgTW9pbmUuIFRoaXMgZm9udCBpcyBsaWNlbnNlZCB1\
bmRlciB0aGUgU0lMIE9wZW4gRm9udCBMaWNlbnNlIFwoaHR0cDovL3NjcmlwdHMuc2lsLm9yZy9P\
RkxcKS4AAGEAYgBjADIAcwB2AGcAAGFiYzJzdmcAAFIAZQBnAHUAbABhAHIAAFJlZ3VsYXIAAEYA\
bwBuAHQARgBvAHIAZwBlACAAMgAuADAAIAA6ACAAYQBiAGMAMgBzAHYAZwAgADoAIAAyADcALQAx\
ADEALQAyADAAMQA4AABGb250Rm9yZ2UgMi4wIDogYWJjMnN2ZyA6IDI3LTExLTIwMTgAAGEAYgBj\
ADIAcwB2AGcAAGFiYzJzdmcAAFYAZQByAHMAaQBvAG4AIAAgAABWZXJzaW9uICAAAGEAYgBjADIA\
cwB2AGcAAGFiYzJzdmcAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgwAAAAEAAgEC\
AAMBAwEEAQUBBgEHAQgBCQEKAQsBDAENAQ4BDwEQAREBEgETARQBFQEWARcBGAEZARoBGwEcAR0B\
HgEfASABIQEiASMBJAElASYBJwEoASkBKgErASwBLQEuAS8BMAExATIBMwE0ATUBNgE3ATgBOQE6\
ATsBPAE9AT4BPwFAAUEBQgFDAUQBRQFGAUcBSAFJAUoBSwFMAU0BTgFPAVABUQFSAVMBVAFVAVYB\
VwFYAVkBWgFbAVwBXQFeAV8BYAFhAWIBYwFkAWUBZgFnAWgBaQFqAWsBbAFtAW4BbwFwAXEBcgFz\
AXQBdQF2AXcBeAF5AXoBewF8AX0BfgF/AYAGLm5vZGVmB3VuaUUwMDAHdW5pRTAyMgd1bmlFMDIz\
B3VuaUUwMjQHdW5pRTAzMAd1bmlFMDM4B3VuaUUwMzkHdW5pRTA0Mwd1bmlFMDQ1B3VuaUUwNDYH\
dW5pRTA0Nwd1bmlFMDQ4B3VuaUUwNTAHdW5pRTA1Qwd1bmlFMDYyB3VuaUUwNjkHdW5pRTA3QQd1\
bmlFMDdCB3VuaUUwN0MHdW5pRTA3RAd1bmlFMDgwB3VuaUUwODEHdW5pRTA4Mgd1bmlFMDgzB3Vu\
aUUwODQHdW5pRTA4NQd1bmlFMDg2B3VuaUUwODcHdW5pRTA4OAd1bmlFMDg5B3VuaUUwOEEHdW5p\
RTA4Qgd1bmlFMDhDB3VuaUUwOTQHdW5pRTA5NQd1bmlFMEEwB3VuaUUwQTEHdW5pRTBBMgd1bmlF\
MEEzB3VuaUUwQTQHdW5pRTBBOQd1bmlFMEIzB3VuaUUxMDEHdW5pRTFCOQd1bmlFMUJCB3VuaUUx\
RTcHdW5pRTI0MAd1bmlFMjQxB3VuaUUyNDIHdW5pRTI0Mwd1bmlFMjQ0B3VuaUUyNDUHdW5pRTI0\
Ngd1bmlFMjQ3B3VuaUUyNDgHdW5pRTI0OQd1bmlFMjYwB3VuaUUyNjEHdW5pRTI2Mgd1bmlFMjYz\
B3VuaUUyNjQHdW5pRTI4MAd1bmlFMjgxB3VuaUUyODIHdW5pRTI4Mwd1bmlFNEEwB3VuaUU0QTIH\
dW5pRTRBNAd1bmlFNEE4B3VuaUU0QUMHdW5pRTRDMAd1bmlFNENFB3VuaUU0RTEHdW5pRTRFMgd1\
bmlFNEUzB3VuaUU0RTQHdW5pRTRFNQd1bmlFNEU2B3VuaUU0RTcHdW5pRTRFOAd1bmlFNEU5B3Vu\
aUU0RUEHdW5pRTRFRQd1bmlFNTAwB3VuaUU1MDEHdW5pRTUyMAd1bmlFNTIxB3VuaUU1MjIHdW5p\
ktCSviMyIyMyAAACAAD/BgH0APoABwAPAAA2FBYyNjQmIgI0NjIWFAYiLXiqeHiqpZLQkpLQVap4\
eKp4/svQkpLQkgAAAAADAAD+ogH0AV4AEQAXAB0AABMzFR4BFRQGBxUjNS4BNTQ2NxkBDgEUFhc+\
ATQmJ+QtYIODYC1ghINhTmlpe01paU0BXmQKjmJhjwpkZQiPYmOOCP47AZgIdZ51CAh2nHYIAAAA\
AgAA/wYB0gD6ACEAKQAAJRYVFAcGIyInJiMiBhQWMzI3NjMyFxYVFAcGIyImNDYzMgIiJjQ2MhYU\
Ac8CCwUGDQozd1V4eFV4MgYSBwMMA0CVaJKSaJR7MiMjMiOCBgQPBgMOX3iqeF8NAgcMBgZ4ktCS\
/sojMiMjMgAAAAEAAP8GAdIA+gAhAAAlFhUUBwYjIicmIyIGFBYzMjc2MzIXFhUUBwYjIiY0NjMy\
Ac8CCwUGDQozd1V4eFV4MgYSBwMMA0CVaJKSaJSCBgQPBgMOX3iqeF8NAgcMBgZ4ktCSAAACAAD+\
ogHSAV4AJAAqAAAlFhcWFRQHBiMiJyYnETY3NjMyFxYVFAcGBxUjNS4BNTQ2NzUzAxEOARQWARGE\
OgILBQYNCi1mZS4GEgcDDAM8gi1ghINhLS1OaWn5Cm0GBA8GAw5UCv5oB1cNAgcMBgZwB2VlCI9i\
Y44IZf3WAZgIdZ51AAABAAD/xAB4ADwABwAAFiImNDYyFhRVMiMjMiM8IzIjIzIAAAABAAD+ogAt\
AV4AAwAAExEjES0tAV79RAK8AAAAAgAA/RIBSgC+AAMADwAANxUhNSUzFSE1MxEjESEVIx4BDv7U\
HgEOHh7+8h5GjIx4MjL8VAJiMgABAAAAAAJWAXIACwAANTcXNxc3FwEnBycHiVRWUq8i/vxUVlM1\
Qblzc3HpFv6kdHRwRwAAAf/oAL0BEgE3ABcAACY+ATMyFjI3NjMyFRQOASMiJiIHBiMiNRgwIRwU\
VSgUAwcOMCEcFFUoFAQHDfYwET0UAwcMMBE9FAQIAAAAAAIAAP+JAcIAiwALABMAAAU0JiMiBhUU\
FjMyPgEUBiImNDYyAVFaNiA0XTchL3F9yH19yBQ0TycfNEwjdGxLS2xLAAAAAgAA/2wBSAKoAA0A\
HAAAJSYjIgYVFBcWMzI2NTQTMxEUBiMiJyY1NDYzMhcBJA0lPJcGCyY8lwIdfkhPJA9+SEAkRBdh\
KwoJF2ErCQJu/W5DZ0MeHUNnLQAAAAABAAD/eQFAAqgADQAAATMRFAYjIiY1NDYzMhcBIh55WTI8\
elgxHwKo/YBGaTgnRWocAAAAAQAA/3kCCgKoABoAAAERFAYjIiY1NDYzMhcRNTMeBBUUBzY1NAFA\
eVkyPHpYMR8eBi45OCUyEgG5/m9GaTgnRWocAU7vNVxHTnVJSHJBSe8AAAAAAgAA/3kCCwKoAB8A\
KAAAJTURMx4EFRQHFhUUBzY1NCYjERQGIyImNTQ2MzITHgEXNDY1NCYBIh4JMDg2IxUWIgV2OHlZ\
Mjx6WDE9DoUbAXZr5gFXJEc7QlgzJTIwJC5BGSFfnv7WRmk4J0VqAXY7ojUDDgNfnwAAAQAA/84A\
ZAAyAAcAABYiJjQ2MhYURyodHSodMh0qHR0qAAAAAAAOAK4AAQAAAAAAAACDAQgAAQAAAAAAAQAH\
AZwAAQAAAAAAAgAHAbQAAQAAAAAAAwAgAf4AAQAAAAAABAAHAi8AAQAAAAAABQAJAksAAQAAAAAA\
BgAHAmUAAwABBAkAAAEGAAAAAwABBAkAAQAOAYwAAwABBAkAAgAOAaQAAwABBAkAAwBAAbwAAwAB\
BAkABAAOAh8AAwABBAkABQASAjcAAwABBAkABgAOAlUAQwBvAHAAeQByAGkAZwBoAHQAIABcADIA\
NQAxACAAMgAwADEAOAAtADIAMAAxADkAIABKAGUAYQBuAC0ARgByAGEAbgBjAG8AaQBzACAATQBv\
AGkAbgBlAC4AIABUAGgAaQBzACAAZgBvAG4AdAAgAGkAcwAgAGwAaQBjAGUAbgBzAGUAZAAgAHUA\
bgBkAGUAcgAgAHQAaABlACAAUwBJAEwAIABPAHAAZQBuACAARgBvAG4AdAAgAEwAaQBjAGUAbgBz\
AGUAIABcACgAaAB0AHQAcAA6AC8ALwBzAGMAcgBpAHAAdABzAC4AcwBpAGwALgBvAHIAZwAvAE8A\
RgBMAFwAKQAuAABDb3B5cmlnaHQgXDI1MSAyMDE4LTIwMTkgSmVhbi1GcmFuY29pcyBNb2luZS4g\
VGhpcyBmb250IGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBTSUwgT3BlbiBGb250IExpY2Vuc2UgXCho\
dHRwOi8vc2NyaXB0cy5zaWwub3JnL09GTFwpLgAAYQBiAGMAMgBzAHYAZwAAYWJjMnN2ZwAAUgBl\
AGcAdQBsAGEAcgAAUmVndWxhcgAARgBvAG4AdABGAG8AcgBnAGUAIAA6ACAAYQBiAGMAMgBzAHYA\
ZwAgADoAIAAyADgALQAxADAALQAyADAAMQA5AABGb250Rm9yZ2UgOiBhYmMyc3ZnIDogMjgtMTAt\
MjAxOQAAYQBiAGMAMgBzAHYAZwAAYWJjMnN2ZwAAVgBlAHIAcwBpAG8AbgAgACAAAFZlcnNpb24g\
IAAAYQBiAGMAMgBzAHYAZwAAYWJjMnN2ZwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAkgAAAAEAAgECAAMBAwEEAQUBBgEHAQgBCQEKAQsBDAENAQ4BDwEQAREBEgETARQBFQEWARcB\
GAEZARoBGwEcAR0BHgEfASABIQEiASMBJAElASYBJwEoASkBKgErASwBLQEuAS8BMAExATIBMwE0\
ATUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAUEBQgFDAUQBRQFGAUcBSAFJAUoBSwFMAU0BTgFPAVAB\
UQFSAVMBVAFVAVYBVwFYAVkBWgFbAVwBXQFeAV8BYAFhAWIBYwFkAWUBZgFnAWgBaQFqAWsBbAFt\
AW4BbwFwAXEBcgFzAXQBdQF2AXcBeAF5AXoBewF8AX0BfgF/AYABgQGCAYMBhAGFAYYBhwGIAYkB\
igGLAYwBjQGOAY8GLm5vZGVmB3VuaUUwMDAHdW5pRTAyMgd1bmlFMDIzB3VuaUUwMjQHdW5pRTAz\
MAd1bmlFMDM4B3VuaUUwMzkHdW5pRTA0Mwd1bmlFMDQ1B3VuaUUwNDYHdW5pRTA0Nwd1bmlFMDQ4\
B3VuaUUwNTAHdW5pRTA1Qwd1bmlFMDYyB3VuaUUwNjkHdW5pRTA3QQd1bmlFMDdCB3VuaUUwN0MH\
dW5pRTA3RAd1bmlFMDgwB3VuaUUwODEHdW5pRTA4Mgd1bmlFMDgzB3VuaUUwODQHdW5pRTA4NQd1\
bmlFMDg2B3VuaUUwODcHdW5pRTA4OAd1bmlFMDg5B3VuaUUwOEEHdW5pRTA4Qgd1bmlFMDhDB3Vu\
aUUwOTQHdW5pRTA5NQd1bmlFMEEwB3VuaUUwQTEHdW5pRTBBMgd1bmlFMEEzB3VuaUUwQTQHdW5p\
RTBBOQd1bmlFMEIzB3VuaUUxMDEHdW5pRTFCOQd1bmlFMUJCB3VuaUUxRTcHdW5pRTFGMgd1bmlF\
MUY0B3VuaUUxRjcHdW5pRTFGOQd1bmlFMUZDB3VuaUUxRkUHdW5pRTFGRgd1bmlFMjAwB3VuaUUy\
NDAHdW5pRTI0MQd1bmlFMjQyB3VuaUUyNDMHdW5pRTI0NAd1bmlFMjQ1B3VuaUUyNDYHdW5pRTI0\
Nwd1bmlFMjQ4B3VuaUUyNDkHdW5pRTI2MAd1bmlFMjYxB3VuaUUyNjIHdW5pRTI2Mwd1bmlFMjY0\
B3VuaUUyODAHdW5pRTI4MQd1bmlFMjgyB3VuaUUyODMHdW5pRTRBMAd1bmlFNEEyB3VuaUU0QTQH\
dW5pRTRBOAd1bmlFNEFDB3VuaUU0QzAHdW5pRTRDRQd1bmlFNEUxB3VuaUU0RTIHdW5pRTRFMwd1\
bmlFNEU0B3VuaUU0RTUHdW5pRTRFNgd1bmlFNEU3B3VuaUU0RTgHdW5pRTRFOQd1bmlFNEVBB3Vu\
aUU0RUUHdW5pRTUwMAd1bmlFNTAxB3VuaUU1MjAHdW5pRTUyMQd1bmlFNTIyB3VuaUU1MjMHdW5p\
RTUyNAd1bmlFNTI1B3VuaUU1MjkHdW5pRTUyQQd1bmlFNTJCB3VuaUU1MkMHdW5pRTUyRAd1bmlF\
NTJGB3VuaUU1MzAHdW5pRTUzMQd1bmlFNTM5B3VuaUU1NjYHdW5pRTU2Nwd1bmlFNTY5B3VuaUU1\
NkMHdW5pRTU2RAd1bmlFNTgyB3VuaUU1RDAHdW5pRTVFMgd1bmlFNjEwB3VuaUU2MTIHdW5pRTYx\
NAd1bmlFNjE4B3VuaUU2MjQHdW5pRTYzMAd1bmlFNjUwB3VuaUU2NTUHdW5pRTkxMAd1bmlFOTEx\
B3VuaUU5MTIHdW5pRTkxNAd1bmlFOTE1B3VuaUU5MTgHdW5pRTkyMAd1bmlFOTI1B3VuaUU5NUQH\
dW5pRUEwMgd1bmlFQUE0AAAAAAH//wACAAEAAAAAAAAADAAUAAQAAAACAAAAAQAAAAEAAAAAAAEA\
AAAA1+feHQAAAADRlyIXAAAAANgjLVI=\
")'
dW5pRUEwMgd1bmlFQUE0B3VuaUVDQTIHdW5pRUNBMwd1bmlFQ0E1B3VuaUVDQTcHdW5pRUNBOQd1\
bmlFQ0I3AAAAAAAAAf//AAIAAQAAAAAAAAAMABQABAAAAAIAAAABAAAAAQAAAAAAAQAAAADZahND\
AAAAANGXIhcAAAAA2dy6gQ==\
") format("truetype")'
Changes to font/abc2svg.sfd.
1

2
3
4
5
6

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

23
24
25
26
27
28
29

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

22
23
24
25
26
27
28
29
-
+




-
+















-
+







SplineFontDB: 3.0
SplineFontDB: 3.2
FontName: abc2svg
FullName: abc2svg
FamilyName: abc2svg
Weight: Regular
Copyright: Copyright \\251 2018 Jean-Francois Moine. This font is licensed under the SIL Open Font License \\(http://scripts.sil.org/OFL\\).
Copyright: Copyright \\251 2018-2019 Jean-Francois Moine. This font is licensed under the SIL Open Font License \\(http://scripts.sil.org/OFL\\).
Version: 
ItalicAngle: 0
UnderlinePosition: 0
UnderlineWidth: 0
Ascent: 819
Descent: 205
InvalidEm: 0
LayerCount: 2
Layer: 0 1 "Back" 1
Layer: 1 0 "Fore" 0
XUID: [1021 296 1430826605 13506888]
OS2Version: 0
OS2_WeightWidthSlopeOnly: 0
OS2_UseTypoMetrics: 0
CreationTime: 1433498007
ModificationTime: 1543339218
ModificationTime: 1572276737
PfmFamily: 17
TTFWeight: 400
TTFWidth: 5
LineGap: 92
VLineGap: 92
Panose: 2 0 5 3 0 0 0 0 0 0
OS2TypoAscent: 0
︙
46
47
48
49
50
51
52
53

54
55
56
57

58
59
60
61
62
63
64
46
47
48
49
50
51
52

53
54
55
56

57
58
59
60
61
62
63
64







-
+



-
+







Encoding: Custom
Compacted: 1
UnicodeInterp: none
NameList: Adobe Glyph List
DisplaySize: -96
AntiAlias: 1
FitToEm: 1
WinInfo: 48 8 2
WinInfo: 128 8 2
BeginPrivate: 0
EndPrivate
TeXData: 1 0 0 346030 173015 115343 0 1048576 115343 783286 444596 497025 792723 393216 433062 380633 303038 157286 324010 404750 52429 2506097 1059062 262144
BeginChars: 128 128
BeginChars: 143 143

StartChar: .nodef
Encoding: 0 0 0
Width: 400
Flags: M
LayerCount: 2
Fore
︙
208
209
210
211
212
213
214
215

216
217
218
219
220
221
222
223
224
208
209
210
211
212
213
214

215


216
217
218
219
220
221
222







-
+
-
-







EndSplineSet
Validated: 1
EndChar

StartChar: uniE043
Encoding: 9 57411 9
Width: 100
Flags: W
Flags: MW
HStem: 320 100<8.4375 91.5625> 570 100<8.4375 91.5625>
VStem: 0 100<328.438 411.562 578.438 661.562>
LayerCount: 2
Fore
SplineSet
50 570 m 3
 22 570 0 592 0 620 c 3
 0 648 22 670 50 670 c 3
 78 670 100 648 100 620 c 3
︙
390
391
392
393
394
395
396
397

398
399
400
401
402
403
404
405
406
407
388
389
390
391
392
393
394

395



396
397
398
399
400
401
402







-
+
-
-
-







EndSplineSet
Validated: 1
EndChar

StartChar: uniE048
Encoding: 13 57416 13
Width: 862
Flags: W
Flags: MW
HStem: -67 43<422.573 472 512 561.427> 176 40<122 261.794 360 472 512 624 722.206 862> 417 42<420.937 472 512 558.146>
VStem: 260 100<86.3906 176 216 307.453> 472 40<-204 -64.8472 -24 176 216 416 456.872 596> 624 100<86.3906 176 216 306.031>
CounterMasks: 1 1c
LayerCount: 2
Fore
SplineSet
472 596 m 1
 512 596 l 1
 512 459 l 1
 625 448 715 344 724 216 c 1
︙
442
443
444
445
446
447
448
449
450
451
452




453
454
455
456
457


458
459
460
461
462
463
464


465
466
467


468
469
470
471
472
473
474
475
476


477
478
479
480


481
482
483
484
485
486
487
488
489
490
491


492
493
494
495
496
497
498
499
500

501
502
503
504
505
506
507
508
509
437
438
439
440
441
442
443




444
445
446
447
448
449
450


451
452
453
454
455
456
457


458
459
460


461
462
463
464
465
466
467
468
469


470
471
472
473


474
475
476
477
478
479
480
481
482
483
484


485
486
487
488
489
490
491
492
493
494

495


496
497
498
499
500
501
502







-
-
-
-
+
+
+
+



-
-
+
+





-
-
+
+

-
-
+
+







-
-
+
+


-
-
+
+









-
-
+
+








-
+
-
-







Encoding: 14 57424 14
Width: 779
Flags: HMW
LayerCount: 2
Fore
SplineSet
364 851 m 0
 358.834831965 811.556898639 356.976973124 782.035724861 356.976973124 751.255004513 c 0
 356.976973124 716.483564406 359.347803652 680.10480549 362 626 c 1
 443 681 551 791 551 896 c 4
 551 942 546 1011 497 1023 c 5
 359 812 357 782 357 751 c 0
 357 716 359 680 362 626 c 1
 443 681 551 791 551 896 c 0
 551 942 546 1011 497 1023 c 1
 417 1013 376 939 364 851 c 0
449 -250 m 9
 472 -486 l 0
 472.010742188 -487.030273438 472.015625 -488.056640625 472.015625 -489.081054688 c 0
 472.015625 -586.490234375 422.876582453 -657 316 -657 c 0
 472 -487 472 -488 472 -489 c 0
 472 -586 423 -657 316 -657 c 0
 204 -657 139 -593 139 -509 c 0
 139 -452 184 -392 253 -392 c 0
 315 -392 365 -436 365 -495 c 0
 365 -554 317 -594 255 -595 c 1
 275 -606 298 -620 324 -620 c 0
 376.540978621 -620 435.012695312 -591.5 435.012695312 -489.685546875 c 0
 435.012695312 -488.795898438 435.008789062 -487.900390625 435 -487 c 0
 377 -620 435 -592 435 -490 c 0
 435 -489 435 -488 435 -487 c 0
 412 -254 l 17
 398.364449186 -255.357092735 384.770660292 -256.025113783 371.270180398 -256.025113783 c 0
 175.860782244 -256.025113783 5.68434188608e-14 -116.071979914 0 100 c 0
 398 -255 385 -256 371 -256 c 0
 176 -256 0 -116 0 100 c 0
 0 188 25 277 58 335 c 8
 131 463 275 562 326 600 c 1
 317 704 313 787 313 852 c 0
 313 1052 406 1152 494 1160 c 1
 570 1115 592 1005 592 873 c 0
 592 688 538 598 379 473 c 1
 399 266 l 0
 409.453125 267.208007812 419.672851562 267.79296875 429.6484375 267.79296875 c 0
 584.774414062 267.79296875 679 144.540865361 679 -3 c 0
 409 267 420 268 430 268 c 0
 585 268 679 145 679 -3 c 0
 679 -124 618 -220 449 -250 c 9
601 -23 m 0
 601 51.748046875 536.096679688 141.236328125 423.16015625 141.236328125 c 0
 419.477539062 141.236328125 415.756835938 140.158203125 412 140 c 1
 601 52 536 141 423 141 c 0
 419 141 416 140 412 140 c 1
 445 -215 l 1
 575 -189 601 -121 601 -23 c 0
375 138 m 1
 306 124 251 75 251 8 c 0
 251 -35 281 -84 319 -108 c 1
 246 -91 195 -56 195 46 c 0
 195 176 270 236 361 261 c 1
 344 450 l 1
 164 306 93 226 92 82 c 1
 94.8109172159 -111.016315493 218.774420218 -221.508405886 375.873991765 -221.508405886 c 0
 386.441654234 -221.508405886 397.159253713 -221.008441515 408 -220 c 0
 95 -111 219 -222 376 -222 c 0
 387 -222 397 -221 408 -220 c 0
 375 138 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE05C
Encoding: 15 57436 15
Width: 722
Flags: W
Flags: MW
HStem: -516 36<404.33 535.95> -88 35<438.435 550.878> 50 35<444.881 548.647> 475 37<397.951 540.179>
VStem: 0 123<-514 510> 186 28<-514 -6 15 510> 290 157<-427.748 -329.281 326.281 424.154> 614 108<-378.538 -189.443 184.402 376.99>
LayerCount: 2
Fore
SplineSet
478 -480 m 0
 567 -480 614 -375 614 -286 c 0
 614 -257 610 -227 601 -196 c 0
 584 -125 550 -88 502 -88 c 0
︙
561
562
563
564
565
566
567
568

569
570
571
572
573
574

575
576
577
578
579
580
581
582






583
584
585
586



587
588
589
590
591


592
593

594
595
596


597
598

599
600

601
602
603
604
605
606
607
554
555
556
557
558
559
560

561


562
563
564

565
566
567






568
569
570
571
572
573
574



575
576
577
578
579
580


581
582
583

584
585


586
587
588

589
590

591
592
593
594
595
596
597
598







-
+
-
-



-
+


-
-
-
-
-
-
+
+
+
+
+
+

-
-
-
+
+
+



-
-
+
+

-
+

-
-
+
+

-
+

-
+







EndSplineSet
Validated: 1
EndChar

StartChar: uniE062
Encoding: 16 57442 16
Width: 703
Flags: W
Flags: HMW
HStem: -170 101<618.042 698.676> -113 224<91.5691 210.802> 72 102<613.779 693.281> 215 40<163.491 326.905>
VStem: 19 241<-54.3228 59.215> 422 121<-211.326 98.8623> 610 97<-162.037 -77.8382 83.7117 162.856>
LayerCount: 2
Fore
SplineSet
19 29 m 0x5e
19 29 m 0
 19 161 125 255 261 255 c 0
 378 255 440 220 496 146 c 0
 528.135801023 103.534834363 543 31 543 -30 c 0
 543 -193 509 -303 392 -412 c 24
 250 -545 118 -593 -70 -603 c 1
 72 -565 193 -517 296 -414 c 0
 362.272166103 -347.727833897 383 -307 408 -201 c 0
 417.095703125 -162.434570312 422 -69 422 -36 c 0
 528 104 543 31 543 -30 c 0
 543 -193 505 -313 388 -422 c 24
 246 -555 118 -593 -70 -603 c 1
 72 -565 189 -512 292 -409 c 0
 358 -343 387 -287 412 -181 c 0
 421 -142 422 -69 422 -36 c 0
 422 48 418 100 376 150 c 0
 339.06264499 193.973041679 314 215 245 215 c 0
 168 215 91 170 67 62 c 1
 97 100 116 111 153 111 c 4
 339 194 314 207 245 207 c 0
 168 207 97 154 67 58 c 1
 97 96 116 111 153 111 c 0
 214 111 260 62 260 -1 c 0
 260 -33 248 -61 221 -83 c 1
 198 -104 166 -113 139 -113 c 0
 71 -113 19 -52 19 29 c 0x5e
661 -170 m 0x9e
 71 -113 19 -52 19 29 c 0
661 -170 m 0
 632 -170 610 -150 610 -119 c 0
 610 -90 629 -69 656 -69 c 4
 610 -90 629 -69 656 -69 c 0
 683 -69 707 -90 707 -119 c 0
 707 -148 687 -170 661 -170 c 0x9e
655 72 m 0x3e
 707 -148 687 -170 661 -170 c 0
655 72 m 0
 625 72 605 94 605 127 c 0
 605 156 626 174 654 174 c 4
 605 156 626 174 654 174 c 0
 681 174 702 153 702 127 c 0
 702 94 682 72 655 72 c 0x3e
 702 94 682 72 655 72 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE069
Encoding: 17 57449 17
Width: 470
︙
623
624
625
626
627
628
629
630

631
632
633
634
635
636

637
638
639
640

641
642
643
644
645
646

647
648
649
650
651
652
653
654
655
656
657
658

659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674

675
676
677
678
679
680

681
682
683
684
685
686
687
688
689
690
691
692

693
694
695
696
697
698
699
700
701
614
615
616
617
618
619
620

621


622
623
624

625
626
627
628

629
630
631
632
633
634

635
636
637
638
639
640
641
642
643
644
645
646

647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662

663
664
665
666
667
668

669
670
671
672
673
674
675
676
677
678
679
680

681


682
683
684
685
686
687
688







-
+
-
-



-
+



-
+





-
+











-
+















-
+





-
+











-
+
-
-







EndSplineSet
Validated: 1
EndChar

StartChar: uniE07A
Encoding: 18 57466 18
Width: 779
Flags: W
Flags: MW
HStem: -502 30<205.501 304.477> -204 31<214.246 323 357 391.941> 113 95<243.207 289> 118 99<329.167 431.634>
VStem: 0 76<-48.8921 170.465> 111 181<-436.162 -326.772> 154 43<-16.0423 68.9973> 249 38<502.74 719.447> 338 32<-437.341 -205.711> 442 32<651.371 785.751> 502 41<-92.0505 45.5518>
LayerCount: 2
Fore
SplineSet
329 117 m 1xd8e0
329 117 m 1
 329 117 348 -79 354 -170 c 1
 464 -145 502 -83 502 -22 c 0
 502 46 442 118 350 118 c 0
 343 118 336 118 329 117 c 1xd8e0
 343 118 336 118 329 117 c 1
323 -170 m 2
 323 -170 299 97 298 113 c 1
 243 102 197 67 197 13 c 0
 197 -13 207 -40 228 -66 c 1
 188 -44 154 0 154 58 c 0
 154 137 216 188 289 208 c 1xeae0
 154 137 216 188 289 208 c 1
 284 266 280 308 275 360 c 1
 131 245 77 181 76 66 c 1
 82 -68 133 -173 320 -173 c 0
 323 -173 323 -172 323 -170 c 2
357 -199 m 1
 363 -271 369 -330 370 -341 c 0
 371 -351 370 -359 370 -368 c 0
 370 -370 370 -373 370 -375 c 0
 370 -462 329 -502 248 -502 c 0
 178 -502 111 -448 111 -381 c 0
 111 -335 147 -294 202 -294 c 0
 252 -294 292 -338 292 -385 c 0xdce0
 252 -294 292 -338 292 -385 c 0
 292 -432 254 -459 204 -460 c 1
 220 -469 238 -472 259 -472 c 0
 303 -472 338 -442 338 -368 c 0
 338 -361 338 -353 337 -344 c 2
 324 -203 l 1
 315 -204 303 -204 294 -204 c 0
 108 -204 0 -105 0 80 c 0
 0 150 20 221 46 267 c 1
 67 308 132 372 166 399 c 0
 185 415 222 451 263 481 c 1
 257 564 249 630 249 682 c 0
 249 842 328 916 402 938 c 1
 470 864 474 804 474 698 c 0
 474 550 431 478 304 378 c 1
 309 324 311 278 318 215 c 1
 327 216 335 217 344 217 c 0xd9e0
 327 216 335 217 344 217 c 0
 398 217 453 197 493 150 c 1
 534 106 543 55 543 -2 c 0
 543 -76 514 -135 444 -169 c 1
 414 -184 391 -196 357 -199 c 1
290 658 m 0
 288 638 287 615 287 594 c 0xc9e0
 288 638 287 615 287 594 c 0
 287 552 291 513 293 501 c 1
 358 545 442 639 442 712 c 0
 442 749 433 790 401 820 c 1
 331 792 299 728 290 658 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07B
Encoding: 19 57467 19
Width: 782
Flags: W
Flags: MW
HStem: -413 29<322.95 428.387> -70 28<351.623 440.673> 40 28<355.737 438.873> 380 30<318.164 432.756>
VStem: 0 98<-411 408> 149 22<-411 -5 12 408> 232 126<-344.637 -259.418 257.61 342.509> 491 87<-311.532 -144.136 138.096 308.532>
LayerCount: 2
Fore
SplineSet
382 -384 m 0
 453 -384 491 -300 491 -229 c 0
 491 -206 488 -182 481 -157 c 0
 467 -100 440 -70 402 -70 c 0
︙
753
754
755
756
757
758
759
760

761
762
763
764
765
766

767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783


784
785
786
787
788


789
790
791
792

793
794
795
796
797
798
799
740
741
742
743
744
745
746

747


748
749
750

751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766


767
768
769
770
771


772
773
774
775
776

777
778
779
780
781
782
783
784







-
+
-
-



-
+















-
-
+
+



-
-
+
+



-
+







EndSplineSet
Validated: 1
EndChar

StartChar: uniE07C
Encoding: 20 57468 20
Width: 807
Flags: W
Flags: MW
HStem: -136 81<491.982 561.648> -102 188<67.2812 172.801> 58 81<488.633 557.386> 170 34<129.66 260.591>
VStem: 15 195<-48.9031 47.6335> 340 94<-168.512 89.0947> 488 78<-131.953 -59.6448 63.9747 133.311>
LayerCount: 2
Fore
SplineSet
15 33 m 0x5e
15 33 m 0
 15 139 104 204 209 204 c 0
 299 204 358 176 399 117 c 0
 423 82 434 25 434 -24 c 0
 434 -154 394 -258 314 -330 c 1
 233 -401 94 -474 -56 -482 c 1
 58 -452 160 -406 230 -331 c 1
 278 -281 313 -202 331 -117 c 0
 337 -86 340 -55 340 -29 c 0
 340 38 325 90 295 124 c 1
 262 160 236 170 194 170 c 0
 133 170 68 125 54 50 c 1
 78 80 86 86 116 86 c 0
 165 86 210 38 210 -12 c 0
 210 -38 200 -60 178 -78 c 1
 160 -95 138 -102 116 -102 c 0
 54 -102 15 -32 15 33 c 0x5e
529 -136 m 0x9e
 54 -102 15 -32 15 33 c 0
529 -136 m 0
 506 -136 488 -120 488 -95 c 0
 488 -72 502 -55 525 -55 c 0
 547 -55 566 -72 566 -95 c 0
 566 -118 550 -136 529 -136 c 0x9e
524 58 m 0x3e
 566 -118 550 -136 529 -136 c 0
524 58 m 0
 500 58 484 76 484 102 c 0
 484 125 499 139 523 139 c 0
 545 139 562 123 562 102 c 0
 562 76 546 58 524 58 c 0x3e
 562 76 546 58 524 58 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07D
Encoding: 21 57469 21
Width: 200
︙
1118
1119
1120
1121
1122
1123
1124
1125

1126
1127
1128
1129
1130
1131
1132
1133
1134
1135


1136
1137
1138
1139
1140
1141
1142
1103
1104
1105
1106
1107
1108
1109

1110


1111
1112
1113
1114
1115
1116


1117
1118
1119
1120
1121
1122
1123
1124
1125







-
+
-
-






-
-
+
+







EndSplineSet
Validated: 1
EndChar

StartChar: uniE08A
Encoding: 32 57482 32
Width: 409
Flags: W
Flags: HMW
HStem: -246 29<187.663 292.861> 30 134<264.058 320.72> 228 21<196.786 281.147>
VStem: 9 110<-126.934 138.101> 325 54<100.5 183.933> 392 17<-84.7973 -75.9219>
LayerCount: 2
Fore
SplineSet
303 164 m 1
 311 163 315 162 321 161 c 1
 324 165 325 170 325 174 c 0
 325 202 273 228 234 228 c 1
 173 226 119 170 119 18 c 0
 325 202 273 224 234 224 c 5
 173 222 119 170 119 18 c 0
 119 -58 126 -133 158 -175 c 0
 181 -204 207 -217 239 -217 c 0
 265 -217 294 -207 322 -183 c 0
 350 -159 369 -119 392 -71 c 1
 392 -74 410 -77 409 -80 c 0
 376 -183 333 -244 211 -246 c 0
 161 -246 111 -226 73 -189 c 0
︙
1152
1153
1154
1155
1156
1157
1158
1159

1160
1161
1162
1163
1164
1165
1166
1167
1168
1135
1136
1137
1138
1139
1140
1141

1142


1143
1144
1145
1146
1147
1148
1149







-
+
-
-







EndSplineSet
Validated: 1
EndChar

StartChar: uniE08B
Encoding: 33 57483 33
Width: 408
Flags: W
Flags: MW
HStem: -246 29<215.419 292.933> 30 134<264.058 320.72> 228 21<215.371 280.515>
VStem: 9 110<-126.934 136.6> 180 35<-350 -246 -197 209 248 350> 325 54<100.5 183.933> 392 17<-84.661 -75.9219>
LayerCount: 2
Fore
SplineSet
303 164 m 1
 311 163 315 162 321 161 c 1
 324 165 325 170 325 174 c 0
 325 202 273 228 234 228 c 0
︙
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341





1342
1343
1344
1345
1346
1347
1348
1349

1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368














1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384





1385
1386
1387
1388
1389
1390
1391
1311
1312
1313
1314
1315
1316
1317





1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329

1330


1331
1332
1333














1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358





1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370







-
-
-
-
-
+
+
+
+
+







-
+
-
-



-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
-
-
-
-
+
+
+
+
+







Fore
SplineSet
337 -30 m 4
 337 40 264 101 193 101 c 4
 154 101 109 75 109 31 c 4
 109 -40 186 -97 257 -97 c 4
 298 -97 337 -75 337 -30 c 4
450 0 m 0
 450 -70 361 -129 225 -129 c 0
 89 -129 0 -70 0 0 c 0
 0 70 89 129 225 129 c 0
 361 129 450 70 450 0 c 0
450 0 m 4
 450 -70 361 -129 225 -129 c 4
 89 -129 0 -70 0 0 c 4
 0 70 89 129 225 129 c 4
 361 129 450 70 450 0 c 4
EndSplineSet
Validated: 1
EndChar

StartChar: uniE0A3
Encoding: 40 57507 40
Width: 329
Flags: W
Flags: MW
HStem: -148 57<54.5533 171.763> 91 57<156.237 273.528>
VStem: 0 31<-79.2669 -12.3025> 297 31<12.3025 73.4219>
LayerCount: 2
Fore
SplineSet
292 68 m 0
 283 84 264 91 242 91 c 0
 164 91 31 10 31 -49 c 0
 31 -56 33 -62 37 -68 c 0
 45 -84 64 -91 86 -91 c 0
 164 -91 297 -10 297 49 c 0
 297 56 295 62 292 68 c 0
313 81 m 0
 323 61 328 41 328 22 c 0
 328 -70 223 -148 130 -148 c 0
 83 -148 40 -128 15 -81 c 0
 5 -61 0 -41 0 -22 c 0
 0 70 105 148 198 148 c 0
 245 148 288 128 313 81 c 0
292 68 m 4
 283 84 264 91 242 91 c 4
 164 91 31 10 31 -49 c 4
 31 -56 33 -62 37 -68 c 4
 45 -84 64 -91 86 -91 c 4
 164 -91 297 -10 297 49 c 4
 297 56 295 62 292 68 c 4
313 81 m 4
 323 61 328 41 328 22 c 4
 328 -70 223 -148 130 -148 c 4
 83 -148 40 -128 15 -81 c 4
 5 -61 0 -41 0 -22 c 4
 0 70 105 148 198 148 c 4
 245 148 288 128 313 81 c 4
EndSplineSet
Validated: 1
EndChar

StartChar: uniE0A4
Encoding: 41 57508 41
Width: 320
Flags: HMW
LayerCount: 2
Fore
SplineSet
320 40 m 0
 320 -51 231 -135 110 -135 c 0
 40 -135 0 -90 0 -40 c 0
 0 50 90 135 210 135 c 0
 280 135 320 90 320 40 c 0
320 40 m 4
 320 -51 231 -135 110 -135 c 4
 40 -135 0 -90 0 -40 c 4
 0 50 90 135 210 135 c 4
 280 135 320 90 320 40 c 4
EndSplineSet
Validated: 1
EndChar

StartChar: uniE0A9
Encoding: 42 57513 42
Width: 330
︙
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517











































































































































































1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530








1531
1532
1533
1534
1535
1536

1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555

1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574














1575
1576
1577
1578
1579
1580

1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594






1595
1596
1597
1598


1599
1600
1601
1602
1603
1604
1605

1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627


1628
1629
1630
1631
1632


1633
1634
1635
1636
1637
1638

1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653








1654
1655
1656
1657
1658


1659
1660
1661
1662


1663
1664
1665
1666
1667
1668
1669

1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687










1688
1689
1690
1691
1692


1693
1694
1695
1696


1697
1698
1699
1700


1701
1702
1703
1704
1705
1706

1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724










1725
1726
1727
1728
1729




1730
1731
1732


1733
1734
1735
1736


1737
1738
1739
1740
1741
1742
1743

1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763












1764
1765
1766
1767
1768


1769
1770
1771
1772


1773
1774
1775
1776


1777
1778
1779
1780


1781
1782
1783
1784
1785
1786

1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806












1807
1808
1809
1810
1811




1812
1813
1814


1815
1816
1817
1818


1819
1820
1821
1822


1823
1824
1825
1826
1827
1828
1829

1830
1831
1832
1833
1834
1835
1836
1487
1488
1489
1490
1491
1492
1493
1494


1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670








1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683

1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702

1703
1704
1705
1706
1707
1708














1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727

1728
1729
1730
1731
1732
1733
1734
1735
1736






1737
1738
1739
1740
1741
1742
1743
1744


1745
1746
1747
1748
1749
1750
1751
1752

1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773


1774
1775
1776
1777
1778


1779
1780
1781
1782
1783
1784
1785

1786
1787
1788
1789
1790
1791
1792
1793








1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804


1805
1806
1807
1808


1809
1810
1811
1812
1813
1814
1815
1816

1817
1818
1819
1820
1821
1822
1823
1824
1825










1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838


1839
1840
1841
1842


1843
1844
1845
1846


1847
1848
1849
1850
1851
1852
1853

1854
1855
1856
1857
1858
1859
1860
1861
1862










1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873




1874
1875
1876
1877
1878


1879
1880
1881
1882


1883
1884
1885
1886
1887
1888
1889
1890

1891
1892
1893
1894
1895
1896
1897
1898
1899












1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914


1915
1916
1917
1918


1919
1920
1921
1922


1923
1924
1925
1926


1927
1928
1929
1930
1931
1932
1933

1934
1935
1936
1937
1938
1939
1940
1941
1942












1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955




1956
1957
1958
1959
1960


1961
1962
1963
1964


1965
1966
1967
1968


1969
1970
1971
1972
1973
1974
1975
1976

1977
1978
1979
1980
1981
1982
1983
1984








-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+





-
+


















-
+





-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
+








-
-
-
-
-
-
+
+
+
+
+
+


-
-
+
+






-
+




















-
-
+
+



-
-
+
+





-
+







-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+



-
-
+
+


-
-
+
+






-
+








-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+



-
-
+
+


-
-
+
+


-
-
+
+





-
+








-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+

-
-
+
+


-
-
+
+






-
+








-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+



-
-
+
+


-
-
+
+


-
-
+
+


-
-
+
+





-
+








-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
+
+
+
+

-
-
+
+


-
-
+
+


-
-
+
+






-
+







 22 -50 0 -28 0 0 c 3
 0 28 22 50 50 50 c 3
 78 50 100 28 100 0 c 3
 100 -28 78 -50 50 -50 c 3
EndSplineSet
Validated: 1
EndChar

StartChar: uniE240
Encoding: 48 57920 48
StartChar: uniE1F2
Encoding: 48 57842 48
Width: 320
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 680 m 1
 320 680 l 1
 320 40 l 2
 320 -51 231 -135 110 -135 c 0
 40 -135 0 -90 0 -40 c 0
 0 50 90 135 210 135 c 0
 244 135 271 124 290 107 c 1
 290 560 l 1
 0 560 l 1
 0 680 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE1F4
Encoding: 49 57844 49
Width: 320
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 680 m 1
 320 680 l 1
 320 40 l 2
 320 -51 231 -135 110 -135 c 0
 40 -135 0 -90 0 -40 c 0
 0 50 90 135 210 135 c 0
 244 135 271 124 290 107 c 1
 290 380 l 1
 0 380 l 1
 0 500 l 1
 290 500 l 1
 290 560 l 1
 0 560 l 1
 0 680 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE1F7
Encoding: 50 57847 50
Width: 320
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 680 m 25
 320 680 l 29
 320 560 l 25
 0 560 l 25
 0 680 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE1F9
Encoding: 51 57849 51
Width: 320
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 500 m 25
 320 500 l 25
 320 380 l 25
 0 380 l 29
 0 500 l 25
0 680 m 25
 320 680 l 25
 320 560 l 25
 0 560 l 25
 0 680 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE1FC
Encoding: 52 57852 52
Width: 100
Flags: HMW
LayerCount: 2
Fore
SplineSet
50 -38 m 7
 22 -38 0 -16 0 12 c 7
 0 40 22 62 50 62 c 7
 78 62 100 40 100 12 c 7
 100 -16 78 -38 50 -38 c 7
EndSplineSet
Validated: 1
EndChar

StartChar: uniE1FE
Encoding: 53 57854 53
Width: 310
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 960 m 29
 310 960 l 29
 310 930 l 29
 30 930 l 29
 30 770 l 29
 0 770 l 29
 0 960 l 29
EndSplineSet
Validated: 1
EndChar

StartChar: uniE1FF
Encoding: 54 57855 54
Width: 230
Flags: HMW
LayerCount: 2
Fore
SplineSet
80 810 m 0
 27 810 0 833 0 864 c 0
 0 882 11 900 30 900 c 0
 51 900 63 888 63 872 c 0
 63 842 40 846 40 840 c 0
 40 831 59 827 73 827 c 0
 100 827 130 857 130 900 c 0
 130 918 120 940 98 940 c 0
 88 940 90 940 80 940 c 0
 70 940 60 943 60 954 c 24
 60 965 69 970 80 970 c 0
 95 970 105 970 120 970 c 0
 151 970 180 1001 180 1028 c 0
 180 1043 172 1060 152 1060 c 0
 141 1060 131 1056 131 1047 c 0
 131 1040 133 1043 133 1028 c 0
 133 1014 124 1000 100 1000 c 0
 84 1000 70 1013 70 1029 c 0
 70 1052 86 1080 150 1080 c 0
 197 1080 230 1055 230 1020 c 0
 230 966 160 949 160 940 c 0
 160 933 180 916 180 890 c 0
 180 847 149 810 80 810 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE200
Encoding: 55 57856 55
Width: 310
Flags: HMW
LayerCount: 2
Fore
SplineSet
310 960 m 25
 310 770 l 25
 280 770 l 25
 280 930 l 25
 0 930 l 25
 0 960 l 25
 310 960 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE240
Encoding: 56 57920 56
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 -239 m 9
 0 0 l 1
 30 0 l 1
 51 -195 315 -321 315 -584 c 0
 315 -643 302 -710 269 -785 c 1
 282 -738 287 -691 287 -647 c 0
 287 -452 175 -294 30 -239 c 1
 0 -239 l 9
0 -239 m 13
 0 0 l 5
 30 0 l 5
 51 -195 315 -321 315 -584 c 4
 315 -643 302 -710 269 -785 c 5
 282 -738 287 -691 287 -647 c 4
 287 -452 175 -294 30 -239 c 5
 0 -239 l 13
EndSplineSet
Validated: 1
EndChar

StartChar: uniE241
Encoding: 49 57921 49
Encoding: 57 57921 57
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 0 m 1
 0 239 l 1
 30 239 l 1
 178 280 288 445 288 642 c 0
 288 688 282 736 269 785 c 1
 302 711 315 647 315 588 c 0
 315 328 51 195 30 0 c 0
 0 0 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE242
Encoding: 50 57922 50
Encoding: 58 57922 58
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 -343 m 9
 0 0 l 1
 30 0 l 1
 62.1962890625 -133.135742188 315.4921875 -235.983398438 315.4921875 -417.918945312 c 0
 315.4921875 -445.063476562 309.853515625 -473.969726562 297 -505 c 1
 308.7109375 -531.232421875 315.594726562 -559.000976562 315.594726562 -588.922851562 c 0
 315.594726562 -622.875976562 306.73046875 -659.6015625 286 -700 c 1
 289.740234375 -679.993164062 291.475585938 -660.758789062 291.475585938 -642.3125 c 0
 291.475585938 -448.700195312 100.301757812 -342 30 -342 c 1
 0 -343 l 9
30 -171 m 1
 56 -278 226 -366 291 -492 c 1
 291.465820312 -485.108398438 291.692382812 -478.3203125 291.692382812 -471.635742188 c 0
 291.692382812 -277.536132812 100.569335938 -171 30 -171 c 1
0 -343 m 13
 0 0 l 5
 30 0 l 5
 62 -133 315 -236 315 -418 c 4
 315 -445 310 -474 297 -505 c 5
 309 -531 316 -559 316 -589 c 4
 316 -623 307 -660 286 -700 c 5
 290 -680 291 -660 291 -642 c 4
 291 -448 100 -342 30 -342 c 5
 0 -343 l 13
30 -171 m 5
 56 -278 226 -366 291 -492 c 5
 291 -485 292 -479 292 -472 c 4
 292 -278 101 -171 30 -171 c 5
EndSplineSet
Validated: 1
EndChar

StartChar: uniE243
Encoding: 51 57923 51
Encoding: 59 57923 59
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 -1 m 9
 0 342 l 1
 30 342 l 1
 100.301757812 342 291.475585938 448.700195312 291.475585938 642.3125 c 0
 291.475585938 660.758789062 289.740234375 679.993164062 286 700 c 1
 306.73046875 659.6015625 315.594726562 622.875976562 315.594726562 588.922851562 c 0
 315.594726562 559.000976562 308.7109375 531.232421875 297 505 c 1
 309.853515625 473.969726562 315.4921875 445.063476562 315.4921875 417.918945312 c 0
 315.4921875 235.983398438 62.1962890625 133.135742188 30 0 c 1
 100 342 291 448 291 642 c 0
 291 660 290 680 286 700 c 1
 307 660 316 623 316 589 c 0
 316 559 309 531 297 505 c 1
 310 474 315 445 315 418 c 0
 315 236 62 133 30 0 c 1
 0 -1 l 9
30 171 m 1
 100.569335938 171 291.692382812 277.536132812 291.692382812 471.635742188 c 0
 291.692382812 478.3203125 291.465820312 484.108398438 291 491 c 1
 101 171 292 278 292 472 c 0
 292 479 291 484 291 491 c 1
 226 365 56 278 30 171 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE244
Encoding: 52 57924 52
Encoding: 60 57924 60
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 -368 m 9
 0 145 l 1
 30 145 l 1
 62 12 315 -91 315 -273 c 0
 315 -300 310 -329 297 -360 c 1
 309 -386 315 -414 315 -444 c 0
 315 -471 310 -500 297 -531 c 1
 309 -557 316 -585 316 -615 c 0
 316 -649 307 -686 286 -726 c 1
 290 -706 291 -687 291 -668 c 0
 291 -475 100 -368 30 -368 c 1
 0 -368 l 9
30 -199 m 1
 56 -307 226 -392 291 -518 c 1
 291 -511 292 -504 292 -498 c 0
 292 -305.781467412 104.491731968 -198.972460053 31.9860569235 -198.972460053 c 0
 31.3140795885 -198.972460053 30.6519803414 -198.981634357 30 -199 c 1
 292 -306 105 -199 32 -199 c 0
 31 -199 31 -199 30 -199 c 1
30 -28 m 1
 56 -135 226 -221 291 -347 c 1
 291 -340 292 -333 292 -327 c 0
 292 -134.781467412 104.491731968 -27.9724600525 31.9860569235 -27.9724600525 c 0
 31.3140795885 -27.9724600525 30.6519803414 -27.9816343566 30 -28 c 1
 292 -135 105 -28 32 -28 c 0
 31 -28 31 -28 30 -28 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE245
Encoding: 53 57925 53
Encoding: 61 57925 61
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 343 m 17
 30 343 l 1
 100.301757812 343 291.475585938 449.700195312 291.475585938 643.3125 c 0
 291.475585938 661.758789062 289.740234375 680.993164062 286 701 c 1
 306.73046875 660.6015625 315.594726562 623.875976562 315.594726562 589.922851562 c 0
 315.594726562 560.000976562 308.7109375 532.232421875 297 506 c 1
 309.81640625 475.145507812 315.422851562 446.318359375 315.422851562 419.19921875 c 0
 315.422851562 389.1875 308.556640625 361.265625 297 335 c 1
 309.853515625 303.969726562 315.4921875 275.063476562 315.4921875 247.918945312 c 0
 315.4921875 65.9833984375 62.1962890625 -36.8642578125 30 -170 c 1
 100 343 291 449 291 643 c 0
 291 661 290 681 286 701 c 1
 307 661 316 624 316 590 c 0
 316 560 309 532 297 506 c 1
 310 475 315 446 315 419 c 0
 315 389 309 361 297 335 c 1
 310 304 315 275 315 248 c 0
 315 66 62 -37 30 -170 c 1
 0 -170 l 1
 0 343 l 17
30 0 m 1
 100.569335938 0 291.692382812 107.536132812 291.692382812 301.635742188 c 0
 291.692382812 308.3203125 291.465820312 315.108398438 291 322 c 1
 101 0 292 108 292 302 c 0
 292 309 291 315 291 322 c 1
 226 196 56 109 30 0 c 1
30 171 m 1
 100.569335938 171 291.692382812 278.536132812 291.692382812 472.635742188 c 0
 291.692382812 479.3203125 291.465820312 486.108398438 291 493 c 1
 101 171 292 279 292 473 c 0
 292 480 291 486 291 493 c 1
 226 367 56 280 30 171 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE246
Encoding: 54 57926 54
Encoding: 62 57926 62
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 -344 m 9
 0 340 l 1
 30 340 l 1
 62.1962890625 206.864257812 315.4921875 104.016601562 315.4921875 -77.9189453125 c 0
 315.4921875 -105.063476562 309.853515625 -133.969726562 297 -165 c 1
 308.556640625 -191.265625 315.422851562 -219.1875 315.422851562 -249.19921875 c 0
 315.422851562 -276.318359375 309.81640625 -305.145507812 297 -336 c 1
 308.556640625 -362.265625 315.422851562 -390.1875 315.422851562 -420.19921875 c 0
 315.422851562 -447.318359375 309.81640625 -476.145507812 297 -507 c 1
 308.7109375 -533.232421875 315.594726562 -561.000976562 315.594726562 -590.922851562 c 0
 315.594726562 -624.875976562 306.73046875 -661.6015625 286 -702 c 1
 289.740234375 -681.993164062 291.475585938 -662.758789062 291.475585938 -644.3125 c 0
 291.475585938 -450.700195312 100.301757812 -344 30 -344 c 1
 62 207 315 104 315 -78 c 0
 315 -105 310 -134 297 -165 c 1
 309 -191 315 -219 315 -249 c 0
 315 -276 310 -305 297 -336 c 1
 309 -362 315 -390 315 -420 c 0
 315 -447 310 -476 297 -507 c 1
 309 -533 316 -561 316 -591 c 0
 316 -625 307 -662 286 -702 c 1
 290 -682 291 -662 291 -644 c 0
 291 -450 100 -344 30 -344 c 1
 0 -344 l 9
30 -173 m 1
 56 -281 226 -368 291 -494 c 1
 291.465820312 -487.108398438 291.692382812 -480.3203125 291.692382812 -473.635742188 c 0
 291.692382812 -279.536132812 100.569335938 -173 30 -173 c 1
 291 -487 292 -481 292 -474 c 0
 292 -280 101 -173 30 -173 c 1
30 -2 m 1
 56 -110 226 -197 291 -323 c 1
 291.465820312 -316.108398438 291.692382812 -309.3203125 291.692382812 -302.635742188 c 0
 291.692382812 -108.536132812 100.569335938 -2 30 -2 c 1
 291 -316 292 -310 292 -303 c 0
 292 -109 101 -2 30 -2 c 1
30 169 m 1
 56 62 226 -26 291 -152 c 1
 291.465820312 -145.108398438 291.692382812 -138.3203125 291.692382812 -131.635742188 c 0
 291.692382812 62.4638671875 100.569335938 169 30 169 c 1
 291 -145 292 -139 292 -132 c 0
 292 62 101 169 30 169 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE247
Encoding: 55 57927 55
Encoding: 63 57927 63
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 -370 m 9
 0 314 l 1
 30 314 l 1
 100.301757812 314 291.475585938 420.700195312 291.475585938 614.3125 c 0
 291.475585938 632.758789062 289.740234375 651.993164062 286 672 c 1
 306.73046875 631.6015625 315.594726562 594.875976562 315.594726562 560.922851562 c 0
 315.594726562 531.000976562 308.7109375 503.232421875 297 477 c 1
 309.81640625 446.145507812 315.422851562 417.318359375 315.422851562 390.19921875 c 0
 315.422851562 360.1875 308.556640625 332.265625 297 306 c 1
 309.81640625 275.145507812 315.422851562 246.318359375 315.422851562 219.19921875 c 0
 315.422851562 189.1875 308.556640625 161.265625 297 135 c 1
 309.853515625 103.969726562 315.4921875 75.0634765625 315.4921875 47.9189453125 c 0
 315.4921875 -134.016601562 62.1962890625 -236.864257812 30 -370 c 1
 100 314 291 421 291 614 c 0
 291 633 290 652 286 672 c 1
 307 632 316 595 316 561 c 0
 316 531 309 503 297 477 c 1
 310 446 315 417 315 390 c 0
 315 360 309 332 297 306 c 1
 310 275 315 246 315 219 c 0
 315 189 309 161 297 135 c 1
 310 104 315 75 315 48 c 0
 315 -134 62 -237 30 -370 c 1
 0 -370 l 9
30 -200 m 5
 100.569335938 -200 291.692382812 -92.4638671875 291.692382812 101.635742188 c 0
 291.692382812 108.3203125 291.465820312 115.108398438 291 122 c 5
 226 -4 56 -91 30 -200 c 5
30 -200 m 1
 101 -200 292 -92 292 102 c 0
 292 108 291 115 291 122 c 1
 226 -4 56 -91 30 -200 c 1
30 -29 m 1
 100.569335938 -29 291.692382812 78.5361328125 291.692382812 272.635742188 c 0
 291.692382812 279.3203125 291.465820312 286.108398438 291 293 c 1
 101 -29 292 79 292 273 c 0
 292 279 291 286 291 293 c 1
 226 167 56 81 30 -29 c 1
30 142 m 1
 100.569335938 142 291.692382812 249.536132812 291.692382812 443.635742188 c 0
 291.692382812 450.3203125 291.465820312 457.108398438 291 464 c 1
 101 142 292 250 292 444 c 0
 292 450 291 457 291 464 c 1
 226 338 56 251 30 142 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE248
Encoding: 56 57928 56
Encoding: 64 57928 64
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 -325 m 9
 0 530 l 1
 30 530 l 1
 62.1962890625 396.864257812 315.4921875 294.016601562 315.4921875 112.081054688 c 0
 315.4921875 84.9365234375 309.853515625 56.0302734375 297 25 c 1
 308.556640625 -1.265625 315.422851562 -29.1875 315.422851562 -59.19921875 c 0
 315.422851562 -86.318359375 309.81640625 -115.145507812 297 -146 c 1
 308.556640625 -172.265625 315.422851562 -200.1875 315.422851562 -230.19921875 c 0
 315.422851562 -257.318359375 309.81640625 -286.145507812 297 -317 c 1
 308.556640625 -343.265625 315.422851562 -371.1875 315.422851562 -401.19921875 c 0
 315.422851562 -428.318359375 309.81640625 -457.145507812 297 -488 c 1
 308.7109375 -514.232421875 315.594726562 -542.000976562 315.594726562 -571.922851562 c 0
 315.594726562 -605.875976562 306.73046875 -642.6015625 286 -683 c 1
 289.740234375 -662.993164062 291.475585938 -643.758789062 291.475585938 -625.3125 c 0
 291.475585938 -431.700195312 100.301757812 -325 30 -325 c 1
 62 397 315 294 315 112 c 0
 315 85 310 56 297 25 c 1
 309 -1 315 -29 315 -59 c 0
 315 -86 310 -115 297 -146 c 1
 309 -172 315 -200 315 -230 c 0
 315 -257 310 -286 297 -317 c 1
 309 -343 315 -371 315 -401 c 0
 315 -428 310 -457 297 -488 c 1
 309 -514 316 -542 316 -572 c 0
 316 -606 307 -643 286 -683 c 1
 290 -663 291 -643 291 -625 c 0
 291 -431 100 -325 30 -325 c 1
 0 -325 l 9
30 -154 m 1
 56 -262 226 -349 291 -475 c 1
 291.465820312 -468.108398438 291.692382812 -461.3203125 291.692382812 -454.635742188 c 0
 291.692382812 -260.536132812 100.569335938 -154 30 -154 c 1
 291 -468 292 -462 292 -455 c 0
 292 -261 101 -154 30 -154 c 1
30 17 m 1
 56 -91 226 -178 291 -304 c 1
 291.465820312 -297.108398438 291.692382812 -290.3203125 291.692382812 -283.635742188 c 0
 291.692382812 -89.5361328125 100.569335938 17 30 17 c 1
 291 -297 292 -291 292 -284 c 0
 292 -90 101 17 30 17 c 1
30 188 m 1
 56 80 226 -7 291 -133 c 1
 291.465820312 -126.108398438 291.692382812 -119.3203125 291.692382812 -112.635742188 c 0
 291.692382812 81.4638671875 100.569335938 188 30 188 c 1
 291 -126 292 -120 292 -113 c 0
 292 81 101 188 30 188 c 1
30 359 m 1
 56 252 226 164 291 38 c 1
 291.465820312 44.8916015625 291.692382812 51.6796875 291.692382812 58.3642578125 c 0
 291.692382812 252.463867188 100.569335938 359 30 359 c 1
 291 45 292 51 292 58 c 0
 292 252 101 359 30 359 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE249
Encoding: 57 57929 57
Encoding: 65 57929 65
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 -580 m 9
 0 275 l 1
 30 275 l 1
 100.301757812 275 291.475585938 381.700195312 291.475585938 575.3125 c 0
 291.475585938 593.758789062 289.740234375 612.993164062 286 633 c 1
 306.73046875 592.6015625 315.594726562 555.875976562 315.594726562 521.922851562 c 0
 315.594726562 492.000976562 308.7109375 464.232421875 297 438 c 1
 309.81640625 407.145507812 315.422851562 378.318359375 315.422851562 351.19921875 c 0
 315.422851562 321.1875 308.556640625 293.265625 297 267 c 1
 309.81640625 236.145507812 315.422851562 207.318359375 315.422851562 180.19921875 c 0
 315.422851562 150.1875 308.556640625 122.265625 297 96 c 1
 309.81640625 65.1455078125 315.422851562 36.318359375 315.422851562 9.19921875 c 0
 315.422851562 -20.8125 308.556640625 -48.734375 297 -75 c 1
 309.853515625 -106.030273438 315.4921875 -134.936523438 315.4921875 -162.081054688 c 0
 315.4921875 -344.016601562 62.1962890625 -446.864257812 30 -580 c 1
 100 275 291 382 291 575 c 0
 291 594 290 613 286 633 c 1
 307 593 316 556 316 522 c 0
 316 492 309 464 297 438 c 1
 310 407 315 378 315 351 c 0
 315 321 309 293 297 267 c 1
 310 236 315 207 315 180 c 0
 315 150 309 122 297 96 c 1
 310 65 315 36 315 9 c 0
 315 -21 309 -49 297 -75 c 1
 310 -106 315 -135 315 -162 c 0
 315 -344 62 -447 30 -580 c 1
 0 -580 l 9
30 -410 m 5
 100.569335938 -410 291.692382812 -302.463867188 291.692382812 -108.364257812 c 0
 291.692382812 -101.6796875 291.465820312 -94.8916015625 291 -88 c 5
 226 -214 56 -301 30 -410 c 5
30 -410 m 1
 101 -410 292 -302 292 -108 c 0
 292 -102 291 -95 291 -88 c 1
 226 -214 56 -301 30 -410 c 1
30 -239 m 1
 100.569335938 -239 291.692382812 -131.463867188 291.692382812 62.6357421875 c 0
 291.692382812 69.3203125 291.465820312 76.1083984375 291 83 c 1
 101 -239 292 -131 292 63 c 0
 292 69 291 76 291 83 c 1
 226 -43 56 -129 30 -239 c 1
30 -68 m 1
 100.569335938 -68 291.692382812 39.5361328125 291.692382812 233.635742188 c 0
 291.692382812 240.3203125 291.465820312 247.108398438 291 254 c 1
 101 -68 292 40 292 234 c 0
 292 240 291 247 291 254 c 1
 226 128 56 41 30 -68 c 1
30 103 m 1
 100.569335938 103 291.692382812 210.536132812 291.692382812 404.635742188 c 0
 291.692382812 411.3203125 291.465820312 418.108398438 291 425 c 1
 101 103 292 211 292 405 c 0
 292 411 291 418 291 425 c 1
 226 299 56 213 30 103 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE260
Encoding: 58 57952 58
Encoding: 66 57952 66
Width: 269
Flags: HMW
LayerCount: 2
Fore
SplineSet
103 122 m 0
 77 122 40 90 40 65 c 2
︙
1847
1848
1849
1850
1851
1852
1853
1854

1855
1856

1857
1858
1859
1860
1861
1862
1863
1864
1995
1996
1997
1998
1999
2000
2001

2002
2003

2004

2005
2006
2007
2008
2009
2010
2011







-
+

-
+
-







 40 108 l 1
 63 142 93 160 133 160 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE261
Encoding: 59 57953 59
Encoding: 67 57953 67
Width: 200
Flags: W
Flags: MW
VStem: 0 28<-101 49 146 376> 172 25<-378 -152 -56 94>
LayerCount: 2
Fore
SplineSet
28 -101 m 1
 172 -56 l 1
 172 94 l 1
 28 49 l 1
︙
1874
1875
1876
1877
1878
1879
1880
1881

1882
1883
1884
1885
1886
1887
1888
2021
2022
2023
2024
2025
2026
2027

2028
2029
2030
2031
2032
2033
2034
2035







-
+







 1 377 l 1
 28 378 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE262
Encoding: 60 57954 60
Encoding: 68 57954 68
Width: 255
Flags: HMW
LayerCount: 2
Fore
SplineSet
83 70 m 21
 83 -97 l 5
︙
1919
1920
1921
1922
1923
1924
1925
1926

1927
1928

1929
1930
1931
1932
1933
1934
1935
1936
1937
2066
2067
2068
2069
2070
2071
2072

2073
2074

2075


2076
2077
2078
2079
2080
2081
2082







-
+

-
+
-
-







 83 -189 l 5
 83 -360 l 5
EndSplineSet
Validated: 1
EndChar

StartChar: uniE263
Encoding: 61 57955 61
Encoding: 69 57955 69
Width: 267
Flags: W
Flags: MW
HStem: -124 74<20 77 210 267> 50 72<20 77 210 267>
VStem: 20 72<-124 -65 65 122> 195 72<-124 -65 65 122>
LayerCount: 2
Fore
SplineSet
195 -124 m 9
 195 -65 l 25
 195 -65 155 -27 143 -14 c 9
 92 -65 l 25
︙
1957
1958
1959
1960
1961
1962
1963
1964

1965
1966
1967
1968
1969
1970
1971
2102
2103
2104
2105
2106
2107
2108

2109
2110
2111
2112
2113
2114
2115
2116







-
+







 267 -124 l 1
 195 -124 l 9
EndSplineSet
Validated: 1
EndChar

StartChar: uniE264
Encoding: 62 57956 62
Encoding: 70 57956 70
Width: 366
Flags: HMW
LayerCount: 2
Fore
SplineSet
78 125 m 1
 55 124 31 100 31 78 c 2
︙
2004
2005
2006
2007
2008
2009
2010
2011

2012
2013
2014
2015
2016
2017
2018
2149
2150
2151
2152
2153
2154
2155

2156
2157
2158
2159
2160
2161
2162
2163







-
+







 219 113 l 1
 235 143 256 163 291 163 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE280
Encoding: 63 57984 63
Encoding: 71 57984 71
Width: 269
Flags: HMW
LayerCount: 2
Fore
SplineSet
171 122 m 0
 140 122 116 92 116 57 c 0
︙
2029
2030
2031
2032
2033
2034
2035
2036

2037
2038
2039
2040
2041
2042
2043
2174
2175
2176
2177
2178
2179
2180

2181
2182
2183
2184
2185
2186
2187
2188







-
+







 76 -20 50 28 50 75 c 0
 50 124 97 160 141 160 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE281
Encoding: 64 57985 64
Encoding: 72 57985 72
Width: 366
Flags: HMW
LayerCount: 2
Fore
SplineSet
87 125 m 1
 86 125 l 2
︙
2076
2077
2078
2079
2080
2081
2082
2083

2084
2085
2086
2087
2088
2089
2090
2221
2222
2223
2224
2225
2226
2227

2228
2229
2230
2231
2232
2233
2234
2235







-
+







 219 113 l 1
 235 143 256 163 291 163 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE282
Encoding: 65 57986 65
Encoding: 73 57986 73
Width: 169
Flags: HMW
LayerCount: 2
Fore
SplineSet
68 320 m 8
 98 320 l 1
︙
2108
2109
2110
2111
2112
2113
2114
2115

2116
2117
2118
2119
2120
2121
2122
2253
2254
2255
2256
2257
2258
2259

2260
2261
2262
2263
2264
2265
2266
2267







-
+







 68 152 l 1
 68 320 l 8
EndSplineSet
Validated: 1
EndChar

StartChar: uniE283
Encoding: 66 57987 66
Encoding: 74 57987 74
Width: 314
Flags: HMW
LayerCount: 2
Fore
SplineSet
233 192 m 17
 233 360 l 1
︙
2166
2167
2168
2169
2170
2171
2172
2173

2174
2175
2176
2177
2178
2179
2180
2311
2312
2313
2314
2315
2316
2317

2318
2319
2320
2321
2322
2323
2324
2325







-
+







 233 101 l 1
 172 83 l 9
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4A0
Encoding: 67 58528 67
Encoding: 75 58528 75
Width: 320
Flags: W
HStem: 0 244<5 15>
VStem: -3 322
LayerCount: 2
Fore
SplineSet
︙
2193
2194
2195
2196
2197
2198
2199
2200

2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216

2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233


2234
2235
2236
2237
2238

2239
2240
2241
2242

2243
2244
2245
2246
2247
2248

2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268

2269
2270
2271
2272
2273
2274
2275
2338
2339
2340
2341
2342
2343
2344

2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360

2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376


2377
2378
2379
2380
2381
2382

2383
2384
2385
2386

2387
2388
2389
2390
2391
2392

2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412

2413
2414
2415
2416
2417
2418
2419
2420







-
+















-
+















-
-
+
+




-
+



-
+





-
+



















-
+







 319 109 314 101 305 98 c 1
 18 1 l 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4A2
Encoding: 68 58530 68
Encoding: 76 58530 76
Width: 80
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 40 m 24
 0 63 17 80 40 80 c 24
 62 80 80 63 80 40 c 24
 80 18 63 0 40 0 c 24
 18 0 0 18 0 40 c 24
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4A4
Encoding: 69 58532 69
Encoding: 77 58532 77
Width: 320
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 0 m 25
 0 40 l 25
 320 40 l 25
 320 0 l 25
 0 0 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4A8
Encoding: 70 58536 70
Width: 320
Encoding: 78 58536 78
Width: 100
Flags: HMW
LayerCount: 2
Fore
SplineSet
40 0 m 29
40 0 m 25
 0 280 l 25
 100 280 l 25
 60 0 l 25
 40 0 l 29
 40 0 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4AC
Encoding: 71 58540 71
Encoding: 79 58540 79
Width: 280
Flags: W
HStem: 0 309
VStem: 0 280
LayerCount: 2
Fore
SplineSet
0 0 m 1
 140 309 l 25
 280 0 l 1
 215 0 l 1
 127 198 l 25
 37 0 l 1
 0 0 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4C0
Encoding: 72 58560 72
Encoding: 80 58560 80
Width: 600
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 0 m 1
 0 214 150 330 300 330 c 3
︙
2284
2285
2286
2287
2288
2289
2290
2291

2292
2293
2294
2295
2296
2297
2298
2429
2430
2431
2432
2433
2434
2435

2436
2437
2438
2439
2440
2441
2442
2443







-
+







 332 120 359 92 359 60 c 3
 359 27 333 0 299 0 c 3
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4CE
Encoding: 73 58574 73
Encoding: 81 58574 81
Width: 182
Flags: HMW
LayerCount: 2
Fore
SplineSet
86 301 m 0
 123 301 136 294 156 272 c 0
︙
2306
2307
2308
2309
2310
2311
2312
2313

2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329

2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345

2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361

2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377

2378
2379
2380
2381
2382
2383
2384
2451
2452
2453
2454
2455
2456
2457

2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473

2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489

2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505

2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521

2522
2523
2524
2525
2526
2527
2528
2529







-
+















-
+















-
+















-
+















-
+







 32 156 -0 184 -0 222 c 0
 -0 258 27 301 86 301 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E1
Encoding: 74 58593 74
Encoding: 82 58593 82
Width: 130
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 250 m 1
 130 250 l 1
 130 -250 l 5
 0 -250 l 1
 0 250 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E2
Encoding: 75 58594 75
Encoding: 83 58594 83
Width: 130
Flags: MW
LayerCount: 2
Fore
SplineSet
0 250 m 1
 130 250 l 1
 130 0 l 1
 0 0 l 1
 0 250 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E3
Encoding: 76 58595 76
Encoding: 84 58595 84
Width: 300
Flags: MW
LayerCount: 2
Fore
SplineSet
0 0 m 1
 300 0 l 1
 300 -125 l 1
 0 -125 l 1
 0 0 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E4
Encoding: 77 58596 77
Encoding: 85 58596 85
Width: 300
Flags: MW
LayerCount: 2
Fore
SplineSet
0 125 m 1
 300 125 l 1
 300 0 l 1
 0 0 l 1
 0 125 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E5
Encoding: 78 58597 78
Encoding: 86 58597 86
Width: 238
Flags: MW
LayerCount: 2
Fore
SplineSet
41 391 m 1
 230 162 l 1
︙
2394
2395
2396
2397
2398
2399
2400
2401

2402
2403
2404
2405
2406
2407
2408
2539
2540
2541
2542
2543
2544
2545

2546
2547
2548
2549
2550
2551
2552
2553







-
+







 122 193 l 1
 41 391 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E6
Encoding: 79 58598 79
Encoding: 87 58598 87
Width: 255
Flags: W
HStem: 48 33<112 170.038>
VStem: 0 135<81.9955 177.625>
LayerCount: 2
Fore
SplineSet
︙
2418
2419
2420
2421
2422
2423
2424
2425

2426
2427

2428
2429
2430
2431
2432
2433

2434
2435
2436
2437

2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450

2451
2452
2453
2454
2455
2456

2457
2458

2459
2460
2461
2462
2463
2464

2465
2466
2467
2468



2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488

2489
2490
2491
2492
2493
2494

2495
2496

2497
2498
2499
2500
2501
2502

2503
2504
2505
2506

2507
2508
2509
2510
2511
2512
2513

2514
2515
2516
2517
2518
2519
2520



2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533

2534
2535
2536
2537
2538
2539

2540
2541

2542
2543
2544
2545
2546
2547

2548
2549
2550
2551

2552
2553
2554
2555
2556
2557
2558

2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575

2576
2577
2578
2579
2580
2581
2582
2583
2584
2585

2586
2587
2588
2589
2590
2591

2592
2593
2594
2595
2596
2597
2598
2563
2564
2565
2566
2567
2568
2569

2570
2571

2572


2573
2574
2575

2576
2577
2578
2579

2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592

2593
2594
2595
2596
2597
2598

2599
2600

2601


2602
2603
2604

2605
2606



2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628

2629
2630
2631
2632
2633
2634

2635
2636

2637


2638
2639
2640

2641
2642
2643
2644

2645
2646
2647
2648
2649
2650
2651

2652
2653
2654
2655
2656



2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671

2672
2673
2674
2675
2676
2677

2678
2679

2680


2681
2682
2683

2684
2685
2686
2687

2688
2689
2690
2691
2692
2693
2694

2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711

2712
2713
2714
2715
2716
2717
2718
2719
2720
2721

2722
2723
2724
2725
2726
2727

2728
2729
2730
2731
2732
2733
2734
2735







-
+

-
+
-
-



-
+



-
+












-
+





-
+

-
+
-
-



-
+

-
-
-
+
+
+



















-
+





-
+

-
+
-
-



-
+



-
+






-
+




-
-
-
+
+
+












-
+





-
+

-
+
-
-



-
+



-
+






-
+
















-
+









-
+





-
+







 58 -227 l 1
 171 60 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E7
Encoding: 80 58599 80
Encoding: 88 58599 88
Width: 329
Flags: W
Flags: MW
HStem: -208 32<112 170.069> 48 33<184 242.918>
VStem: 0 135<-174.731 -78.875> 72 135<81.9955 177.625>
LayerCount: 2
Fore
SplineSet
171 -196 m 1xd0
171 -196 m 1
 151 -202 140 -208 98 -208 c 0
 45 -208 0 -184 0 -128 c 0
 0 -83 33 -64 66 -64 c 0
 101 -64 135 -85 135 -120 c 0xe0
 101 -64 135 -85 135 -120 c 0
 135 -136 130 -155 112 -176 c 1
 158 -176 182 -160 188 -142 c 1
 248 60 l 1
 230 56 212 48 170 48 c 0
 117 48 72 73 72 129 c 0
 72 174 105 192 138 192 c 0
 173 192 207 172 207 137 c 0
 207 121 202 102 184 81 c 1
 230 81 272 108 302 158 c 1
 304 160 324 160 328 158 c 1
 131 -500 l 1
 86 -488 l 1
 171 -196 l 1xd0
 171 -196 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E8
Encoding: 81 58600 81
Encoding: 89 58600 89
Width: 399
Flags: W
Flags: MW
HStem: -208 32<112 170.069> 48 32<189 245.07> 304 33<255 314.117>
VStem: 0 135<-174.731 -78.875> 77 135<81.2688 177.125> 143 135<337.996 433.625>
LayerCount: 2
Fore
SplineSet
246 60 m 1xf4
246 60 m 1
 226 54 217 48 175 48 c 0
 122 48 77 72 77 128 c 0xe8
 77 173 110 192 143 192 c 0xe4
 178 192 212 171 212 136 c 0xe8
 122 48 77 72 77 128 c 0
 77 173 110 192 143 192 c 0
 178 192 212 171 212 136 c 0
 212 120 207 101 189 80 c 1
 235 80 257 96 263 114 c 1
 321 317 l 1
 303 313 283 304 241 304 c 0
 188 304 143 329 143 385 c 0
 143 430 176 448 209 448 c 0
 244 448 278 428 278 393 c 0
 278 377 273 358 255 337 c 1
 301 337 343 364 373 414 c 1
 375 416 395 416 399 414 c 1
 131 -500 l 1
 86 -488 l 1
 171 -196 l 1
 151 -202 140 -208 98 -208 c 0
 45 -208 0 -184 0 -128 c 0
 0 -83 33 -64 66 -64 c 0
 101 -64 135 -85 135 -120 c 0
 135 -136 130 -155 112 -176 c 1
 158 -176 182 -160 188 -142 c 1
 246 60 l 1xf4
 246 60 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E9
Encoding: 82 58601 82
Encoding: 90 58601 90
Width: 472
Flags: W
Flags: MW
HStem: -464 32<112 170.069> -208 32<187 245.069> 48 32<264 320.07> 304 33<330 389.117>
VStem: 0 135<-430.731 -334.875> 75 135<-174.731 -78.875> 152 135<81.2688 177.125> 218 135<337.996 433.625>
LayerCount: 2
Fore
SplineSet
171 -452 m 1xf1
171 -452 m 1
 151 -458 140 -464 98 -464 c 0
 45 -464 0 -440 0 -384 c 0
 0 -339 33 -320 66 -320 c 0
 101 -320 135 -341 135 -376 c 0xf9
 101 -320 135 -341 135 -376 c 0
 135 -392 130 -411 112 -432 c 1
 158 -432 182 -416 188 -398 c 1
 246 -196 l 1
 226 -202 215 -208 173 -208 c 0
 120 -208 75 -184 75 -128 c 0
 75 -83 108 -64 141 -64 c 0
 176 -64 210 -85 210 -120 c 0xf4
 176 -64 210 -85 210 -120 c 0
 210 -136 205 -155 187 -176 c 1
 233 -176 257 -160 263 -142 c 1
 321 60 l 1
 301 54 292 48 250 48 c 0
 197 48 152 72 152 128 c 0xf2
 152 173 185 192 218 192 c 0xf1
 253 192 287 171 287 136 c 0xf2
 197 48 152 72 152 128 c 0
 152 173 185 192 218 192 c 0
 253 192 287 171 287 136 c 0
 287 120 282 101 264 80 c 1
 310 80 332 96 338 114 c 1
 396 317 l 1
 378 313 358 304 316 304 c 0
 263 304 218 329 218 385 c 0
 218 430 251 448 284 448 c 0
 319 448 353 428 353 393 c 0
 353 377 348 358 330 337 c 1
 376 337 418 364 448 414 c 1
 450 416 470 416 474 414 c 1
 131 -756 l 1
 86 -744 l 1
 171 -452 l 1xf1
 171 -452 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4EA
Encoding: 83 58602 83
Encoding: 91 58602 91
Width: 472
Flags: W
Flags: MW
HStem: -464 32<112 170.069> -210 32<187 245.069> 40 32<260 318.069> 290 32<333 389.07> 542 33<393 452.117>
VStem: 0 135<-430.731 -334.875> 75 135<-176.731 -80.875> 148 135<73.2688 169.125> 221 135<323.269 419.125> 281 135<575.996 671.625>
LayerCount: 2
Fore
SplineSet
319 52 m 1xfa
319 52 m 1
 299 46 288 40 246 40 c 0
 193 40 148 64 148 120 c 0
 148 165 181 184 214 184 c 0
 249 184 283 163 283 128 c 0xf9
 249 184 283 163 283 128 c 0
 283 112 278 93 260 72 c 1
 306 72 330 88 336 106 c 1
 390 302 l 1
 370 296 361 290 319 290 c 0
 266 290 221 314 221 370 c 0
 221 415 254 434 287 434 c 0
 322 434 356 413 356 378 c 0xf880
 322 434 356 413 356 378 c 0
 356 362 351 343 333 322 c 1
 379 322 401 338 407 356 c 1
 459 555 l 1
 441 551 421 542 379 542 c 0
 326 542 281 567 281 623 c 0
 281 668 314 686 347 686 c 0
 382 686 416 666 416 631 c 0
 416 615 411 596 393 575 c 1
 439 575 481 602 511 652 c 1
 513 654 533 654 537 652 c 1
 131 -756 l 1
 86 -744 l 1
 171 -452 l 1
 151 -458 140 -464 98 -464 c 0
 45 -464 0 -440 0 -384 c 0
 0 -339 33 -320 66 -320 c 0
 101 -320 135 -341 135 -376 c 0xfc40
 101 -320 135 -341 135 -376 c 0
 135 -392 130 -411 112 -432 c 1
 158 -432 182 -416 188 -398 c 1
 246 -198 l 1
 226 -204 215 -210 173 -210 c 0
 120 -210 75 -186 75 -130 c 0
 75 -85 108 -66 141 -66 c 0
 176 -66 210 -87 210 -122 c 0
 210 -138 205 -157 187 -178 c 1
 233 -178 257 -162 263 -144 c 1
 319 52 l 1xfa
 319 52 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4EE
Encoding: 84 58606 84
Encoding: 92 58606 92
Width: 851
Flags: W
HStem: -105 210<25 825>
VStem: 0 25<-230 -105 105 230> 825 25<-230 -105 105 230>
LayerCount: 2
Fore
SplineSet
︙
2610
2611
2612
2613
2614
2615
2616
2617

2618
2619

2620
2621
2622
2623
2624
2625
2626
2627
2628
2747
2748
2749
2750
2751
2752
2753

2754
2755

2756


2757
2758
2759
2760
2761
2762
2763







-
+

-
+
-
-







 0 -230 l 25
 0 230 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE500
Encoding: 85 58624 85
Encoding: 93 58624 93
Width: 563
Flags: W
Flags: MW
HStem: -195 120<435.745 528.255> 75 120<8.74536 101.255>
VStem: -5 120<88.7454 181.255> 422 120<-181.255 -88.7454>
LayerCount: 2
Fore
SplineSet
55 75 m 3
 22 75 -5 102 -5 135 c 3
 -5 169 22 195 55 195 c 3
 88 195 115 168 115 135 c 3
︙
2638
2639
2640
2641
2642
2643
2644
2645

2646
2647
2648
2649
2650
2651
2652
2773
2774
2775
2776
2777
2778
2779

2780
2781
2782
2783
2784
2785
2786
2787







-
+







 109 -250 l 1
 -16 -250 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE501
Encoding: 86 58625 86
Encoding: 94 58625 94
Width: 800
Flags: W
HStem: -195 120<659.745 752.255> 75 120<-6.25464 86.2546>
VStem: -20 120<88.7454 181.255> 646 120<-181.255 -88.7454>
LayerCount: 2
Fore
SplineSet
︙
2671
2672
2673
2674
2675
2676
2677
2678
2679


2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694










2695
2696
2697
2698
2699
2700
2701
2702
2703


2704
2705
2706
2707
2708
2709
2710

2711
2712
2713
2714
2715
2716

2717
2718

2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760





































2761
2762
2763
2764
2765
2766
2767


2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782


2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794


2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806

































2807
2808
2809
2810
2811
2812
2813
2814
2815
2816







2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828











2829
2830
2831
2832
2833
2834
2835


2836
2837
2838
2839
2840
2841
2842
2843
2844
2845



2846
2847
2848
2849
2850



2851
2852
2853

2854
2855
2856
2857
2858
2859
2860
2861
2862











2863
2864
2865
2866
2867
2868

2869
2870
2871
2872
2873
2874
2875
2806
2807
2808
2809
2810
2811
2812


2813
2814
2815
2816
2817
2818
2819










2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836


2837
2838
2839
2840
2841
2842
2843
2844

2845
2846
2847
2848
2849
2850

2851
2852

2853


2854
2855
2856





































2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898


2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913


2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925


2926
2927
2928
2929
2930
2931
2932
2933






2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969







2970
2971
2972
2973
2974
2975
2976
2977











2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993


2994
2995
2996
2997
2998
2999
3000
3001
3002



3003
3004
3005
3006
3007



3008
3009
3010
3011
3012

3013









3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029

3030
3031
3032
3033
3034
3035
3036
3037







-
-
+
+





-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+







-
-
+
+






-
+





-
+

-
+
-
-



-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-
-
+
+













-
-
+
+










-
-
+
+






-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
-
-
-
-
-
-
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+





-
-
+
+







-
-
-
+
+
+


-
-
-
+
+
+


-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+





-
+







 334 -250 l 5
 212 -250 l 5
EndSplineSet
Validated: 1
EndChar

StartChar: uniE520
Encoding: 87 58656 87
Width: 563
Encoding: 95 58656 95
Width: 379
Flags: HMW
LayerCount: 2
Fore
SplineSet
197 40 m 0
 199.179123831 39.0525548563 201.555731167 38.5988184777 204.0907001 38.5988184777 c 0
 228.311569742 38.5988184777 266.989634802 80.0214592618 286 128 c 0
 293.801273837 147.763227053 298.897664408 174.829638922 298.897664408 197.52305288 c 0
 298.897664408 218.46369363 294.558131706 235.680764302 284 240 c 0
 282.059714009 240.759242344 279.934395026 241.11995209 277.659464452 241.11995209 c 0
 252.96754404 241.11995209 210.650235832 198.625589581 196 162 c 0
 185.665333312 135.693575703 181.068143327 108.945807045 181.068143327 87.1471403297 c 0
 181.068143327 62.5417301443 186.925393958 44.2419393862 197 40 c 0
200 0 m 0
 183 0 173 6 153 32 c 1
 199 39 201 39 204 39 c 0
 228 39 267 80 286 128 c 0
 294 148 299 175 299 198 c 0
 299 219 295 236 284 240 c 0
 282 241 280 241 278 241 c 0
 253 241 211 199 196 162 c 0
 186 136 181 109 181 87 c 0
 181 62 187 44 197 40 c 0
220 0 m 4
 203 0 173 6 153 32 c 1
 108 -90 l 1
 160 -90 l 2
 175 -90 175 -120 160 -120 c 0
 -65 -120 l 0
 -79 -120 -80 -90 -65 -90 c 2
 10 -90 l 1
 114 195 l 0
 115.167116963 197.917792406 120.420719938 212.754502351 120.420719938 224.605732511 c 0
 120.420719938 233.06305846 117.745271893 240 109 240 c 0
 115 198 120 213 120 225 c 0
 120 233 118 240 109 240 c 0
 83 240 72 207 28 135 c 0
 21 124 -6 139 1 151 c 0
 56 249 69 280 125 280 c 0
 170 280 193 271 203 235 c 1
 244 278 263 280 305 280 c 0
 341 280 380 248 380 180 c 0
 380 87 299 0 200 0 c 0
 380 87 319 0 220 0 c 4
EndSplineSet
Validated: 1
EndChar

StartChar: uniE521
Encoding: 88 58657 88
Encoding: 96 58657 96
Width: 440
Flags: W
Flags: MW
HStem: -10 44.404<309.482 391.648> 0 21G<-1 65.5 153 199> 212.877 67.1232<170.507 229.008 295.16 358.649> 221 59<37.7036 99.8884>
VStem: 276.686 77.1955<12.819 101.414> 335.007 74.6509<146.188 236.074>
LayerCount: 2
Fore
SplineSet
80 21 m 0x64
 76 11 74 1 57 0 c 0
 4 0 l 0
 -6 0 0 18 4 26 c 1
 70 193 l 0
 77 211 69 221 60 221 c 0
 42 221 35 210 -11 138 c 0
 -18 127 -39 139 -33 151 c 0
 3 216 36 280 86 280 c 0x54
 128 280 145 266 152 240 c 1
 170 260 194 280 224 280 c 0
 254 280 273 263 278 240 c 1
 292 259 322 280 349 280 c 0
 382.297060451 280 409.658220776 253.618221661 409.658220776 218.530504648 c 0x24
 409.658220776 210.41331474 408.193901764 201.830198995 405 193 c 0
 358 68 l 0
 355.211869734 60.3326417674 353.881302664 53.2726015335 353.881302664 47.6241881024 c 0
 353.881302664 39.5840041338 356.577277218 34.4040367054 361.602949871 34.4040367054 c 0
 363.177144158 34.4040367054 364.979913018 34.9122608557 367 36 c 0
 389 48 404 72 419 93 c 0
 427 104 449 93 440 78 c 0
 411 27 388 -10 323 -10 c 0
 295.96461562 -10 276.685848474 4.31990873934 276.685848474 30.6552962356 c 0xa8
 276.685848474 38.4136929968 278.359032859 47.2149008096 282 57 c 0
 334 193 l 0
 334.687448544 195.062345631 335.007330587 196.961328373 335.007330587 198.698285174 c 0
 335.007330587 208.224115412 325.386390673 212.876768553 314.009865299 212.876768553 c 0
 300.302784706 212.876768553 284.047157224 206.122608782 279 193 c 0
 214 22 l 0
 211 14 207 0 191 -1 c 1
 153 0 l 16
 134 -2 132 12 136 22 c 0
 203 193 l 0
 203.698722351 195.018531237 204.023750859 196.886379473 204.023750859 198.6021409 c 0
 204.023750859 208.040661137 194.187819777 212.876768553 182.617370253 212.876768553 c 0
 169.199127766 212.876768553 153.448127054 206.372675496 148 193 c 0
 80 21 l 0x64
80 21 m 4
 76 11 74 1 57 0 c 4
 4 0 l 4
 -6 0 0 18 4 26 c 5
 70 193 l 4
 77 211 69 221 60 221 c 4
 42 221 35 210 -11 138 c 4
 -18 127 -39 139 -33 151 c 4
 3 216 36 280 86 280 c 4
 128 280 145 266 152 240 c 5
 170 260 194 280 224 280 c 4
 254 280 273 263 278 240 c 5
 292 259 322 280 349 280 c 4
 382 280 410 254 410 219 c 4
 410 211 408 202 405 193 c 4
 358 68 l 4
 355 60 354 54 354 48 c 4
 354 40 357 34 362 34 c 4
 364 34 365 35 367 36 c 4
 389 48 404 72 419 93 c 4
 427 104 449 93 440 78 c 4
 411 27 388 -10 323 -10 c 4
 296 -10 277 5 277 31 c 4
 277 39 278 47 282 57 c 4
 334 193 l 4
 335 195 335 197 335 199 c 4
 335 209 325 213 314 213 c 4
 300 213 284 206 279 193 c 4
 214 22 l 4
 211 14 207 0 191 -1 c 5
 153 0 l 20
 134 -2 132 12 136 22 c 4
 203 193 l 4
 204 195 204 197 204 199 c 4
 204 208 195 213 183 213 c 4
 170 213 153 206 148 193 c 4
 80 21 l 4
EndSplineSet
Validated: 1
EndChar

StartChar: uniE522
Encoding: 89 58658 89
Width: 351
Encoding: 97 58658 97
Width: 278
Flags: HMW
LayerCount: 2
Fore
SplineSet
-50 -160 m 0
 -90 -160 -130 -135 -130 -90 c 0
 -130 -55 -113 -30 -88 -30 c 0
 -63 -30 -47 -45 -47 -65 c 0
 -47 -75 -54 -83 -65 -90 c 0
 -75 -97 -75 -100 -75 -110 c 0
 -75 -120 -65 -124 -50 -124 c 0
 13 -124 16 -35 83 230 c 1
 64 230 65 230 30 230 c 0
 17.239470207 230 10.992185238 240.421191718 10.992185238 250.625180963 c 0
 10.992185238 260.412503619 16.7396874095 270 28 270 c 0
 17 230 11 241 11 251 c 0
 11 261 17 270 28 270 c 0
 59 270 61 270 93 270 c 1
 121 374 204 440 270 440 c 0
 310 440 350 415 350 370 c 0
 350 335 333 310 308 310 c 0
 283 310 267 325 267 345 c 0
 267 355 274 363 285 370 c 0
 296 377 295 380 295 390 c 0
 295 400 285 404 270 404 c 0
 228 404 218 332 205 270 c 1
 232 270 229 270 259 270 c 0
 272 270 278.5 260 278.5 250 c 0
 278.5 240 272 230 259 230 c 0
 272 270 278 260 278 250 c 0
 278 240 272 230 259 230 c 0
 233 230 229 230 196 230 c 1
 153 -26 80 -160 -50 -160 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE524
Encoding: 90 58660 90
Width: 563
Flags: W
HStem: 0 30<56.2969 128.192> 250 30<86.1231 145.206>
VStem: 0 67<48.3332 106.102> 30 50<188.851 246.418> 140 60<38.2418 104.228> 145.315 72.6848<183.703 229.735>
StartChar: uniE523
Encoding: 98 58659 98
Width: 275
Flags: HMW
LayerCount: 2
Fore
SplineSet
208 231 m 0
 218 237 218 245 208 245 c 0
 179 245 153 203 143 177 c 0
 80 21 l 0
 76 11 74 1 57 0 c 0
 4 0 l 0
 -6 0 0 18 4 26 c 1
 70 189 l 0
 77 207 69 217 60 217 c 0
 42 217 35 210 -11 138 c 0
 -18 127 -39 139 -33 151 c 0
 3 216 40 275 90 275 c 0
 132 275 147 246 150 236 c 1
 162 251 182 275 217 275 c 0
 252 275 275 254 275 220 c 0
 275 181 256 165 232 165 c 0
 212 165 190 182 190 200 c 0
 190 216 196 224 208 231 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE524
Encoding: 99 58660 99
Width: 220
Flags: HMW
LayerCount: 2
Fore
SplineSet
80 0 m 0xd8
 41 0 0 33 0 70 c 0xe0
 0 92 14 108 30 108 c 0xd0
 47 108 67 100 67 84 c 0
 67 69 62 55 55 48 c 1
 60 34 69 30 85 30 c 0
 113 30 140 45 140 70 c 0xe8
80 0 m 0
 41 0 0 33 0 70 c 0
 0 92 18 108 34 108 c 0
 51 108 70 96 70 80 c 0
 70 65 61 53 54 46 c 1
 62 35 69 30 85 30 c 0
 113 30 140 45 140 70 c 0
 140 100 116 103 90 120 c 0
 57 141 30 167 30 200 c 0
 30 247 70 280 130 280 c 0
 175 280 218 253 218 220 c 0
 218 199 202 180 180 180 c 0
 163.575188443 180 145.315176651 194.681601887 145.315176651 213.518742217 c 0xd4
 145.315176651 218.765893239 146.732008255 224.335480975 150 230 c 1
 142 240 133 250 120 250 c 0
 101 250 80 239 80 220 c 0
 80 193 127 175 150 160 c 0
 184 137 200 120 200 90 c 0
 200 26 156 0 80 0 c 0xd8
 57 141 32 172 32 205 c 0
 32 252 72 280 132 280 c 0
 177 280 220 253 220 220 c 0
 220 199 204 180 182 180 c 0
 166 180 147 195 147 214 c 0
 147 219 151 225 154 231 c 1
 146 241 135 250 122 250 c 0
 103 250 82 239 82 220 c 0
 82 193 129 175 152 160 c 0
 186 137 200 115 200 85 c 0
 200 26 156 0 80 0 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE525
Encoding: 91 58661 91
Width: 563
Encoding: 100 58661 100
Width: 232
Flags: HMW
LayerCount: 2
Fore
SplineSet
-10 3 m 0
 -22 -6 -34 16 -23 26 c 0
 153 219 l 0
 141.918058124 224.910369 126.469558534 226.454180286 105.444752759 226.454180286 c 0
 98.0113613903 226.454180286 89.8809317488 226.261203875 81 226 c 0
 70 226 47 173 34 162 c 0
 138 218 126 213 105 213 c 0
 98 213 86 219 76 218 c 0
 65 217 47 173 34 162 c 0
 20 150 6 154 20 187 c 0
 41 238 38 251 44 267 c 1
 79.2709584661 264.544426942 105.57190844 263.63368868 125.594979066 263.63368868 c 0
 183.112330324 263.63368868 188.827343421 271.148705903 206.551743542 271.148705903 c 0
 210.297411805 271.148705903 214.579415356 270.813087697 220 270 c 0
 79 265 89 260 126 260 c 0
 184 260 189 271 207 271 c 0
 211 271 215 271 220 270 c 0
 235 268 233 254 224 245 c 0
 66 78 l 0
 64.6486486486 74.6216216216 63.972972973 72.1015339664 63.972972973 70.1929599431 c 0
 65 75 64 72 64 70 c 0
 63.972972973 57.9780861943 91.6486486486 70.8108108108 147 44 c 0
 158 39 168 52 164 64 c 0
 157 84 125 65 125 100 c 0
 125 115 141 130 157 130 c 0
 177 130 195 117 195 75 c 0
 195 36.9571906054 164.866783384 2.35817739562 123.772791397 2.35817739562 c 0
 120.578422501 2.35817739562 117.317823606 2.5672403992 114 3 c 0
 69.8119048549 8.52351189314 48.9765181701 18.5669028431 29.7645164434 18.5669028431 c 0
 17.6725183082 18.5669028431 6.22361116537 14.5882936895 -10 3 c 0
 64 68 65 67 66 67 c 0
 69 67 76 70 86 70 c 0
 99 70 119 65 147 44 c 0
 166 29 179 64 166 64 c 0
 148 64 130 72 130 93 c 0
 130 111 143 128 161 128 c 0
 181 128 199 115 199 73 c 0
 199 35 170 -4 129 -4 c 0
 126 -4 103 -4 100 -4 c 0
 56 2 50 23 31 23 c 0
 19 23 6 15 -10 3 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE529
Encoding: 92 58665 92
Encoding: 101 58665 101
Width: 703
Flags: HMW
LayerCount: 2
Fore
SplineSet
522 40 m 0
 524.179123831 39.0525548563 526.555731167 38.5988184777 529.0907001 38.5988184777 c 0
︙
2977
2978
2979
2980
2981
2982
2983
2984

2985
2986
2987
2988
2989
2990
2991
3139
3140
3141
3142
3143
3144
3145

3146
3147
3148
3149
3150
3151
3152
3153







-
+







 835.665333312 135.693575703 831.068143327 108.945807045 831.068143327 87.1471403297 c 0
 831.068143327 62.5417301443 836.925393958 44.2419393862 847 40 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52A
Encoding: 93 58666 93
Encoding: 102 58666 102
Width: 563
Flags: W
HStem: -120 30<-74.2249 10 108 169.668 250.775 335 433 494.668 575.775 660 758 819.668> 0 38.5988<191.014 237.319 516.014 562.319 841.014 887.319> 240 40<80.1149 147.5 239.117 318 404.386 472.5 564.117 643 729.386 797.5 889.117 973>
VStem: 120.421 60.6474<87.1471 224.606> 298.898 81.1023<108.679 217.246> 445.421 60.6474<87.1471 224.606> 623.898 81.1023<108.679 217.246> 770.421 60.6474<87.1471 224.606> 948.898 81.1023<108.462 239.669>
LayerCount: 2
Fore
SplineSet
︙
3069
3070
3071
3072
3073
3074
3075
3076

3077
3078
3079
3080
3081
3082
3083
3231
3232
3233
3234
3235
3236
3237

3238
3239
3240
3241
3242
3243
3244
3245







-
+







 185.665333312 135.693575703 181.068143327 108.945807045 181.068143327 87.1471403297 c 0
 181.068143327 62.5417301443 186.925393958 44.2419393862 197 40 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52B
Encoding: 94 58667 94
Encoding: 103 58667 103
Width: 703
Flags: HMW
LayerCount: 2
Fore
SplineSet
200 0 m 0
 183 0 173 6 153 32 c 1
︙
3133
3134
3135
3136
3137
3138
3139
3140

3141
3142
3143
3144
3145
3146
3147
3295
3296
3297
3298
3299
3300
3301

3302
3303
3304
3305
3306
3307
3308
3309







-
+







 510.665333312 135.693575703 506.068143327 108.945807045 506.068143327 87.1471403297 c 0
 506.068143327 62.5417301443 511.925393958 44.2419393862 522 40 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52C
Encoding: 95 58668 95
Encoding: 104 58668 104
Width: 811
Flags: HMW
LayerCount: 2
Fore
SplineSet
429 119 m 0
 480 232 500 280 556 280 c 0
︙
3205
3206
3207
3208
3209
3210
3211
3212

3213
3214
3215
3216
3217
3218
3219
3367
3368
3369
3370
3371
3372
3373

3374
3375
3376
3377
3378
3379
3380
3381







-
+







 616.665333312 135.693575703 612.068143327 108.945807045 612.068143327 87.1471403297 c 0
 612.068143327 62.5417301443 617.925393958 44.2419393862 628 40 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52D
Encoding: 96 58669 96
Encoding: 105 58669 105
Width: 351
Flags: HMW
LayerCount: 2
Fore
SplineSet
80 21 m 0
 76 11 74 1 57 0 c 0
︙
3278
3279
3280
3281
3282
3283
3284
3285

3286
3287
3288
3289
3290
3291
3292
3440
3441
3442
3443
3444
3445
3446

3447
3448
3449
3450
3451
3452
3453
3454







-
+







 676 230 672 230 639 230 c 1
 596 -26 523 -160 393 -160 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52F
Encoding: 97 58671 97
Encoding: 106 58671 106
Width: 617
Flags: HMW
LayerCount: 2
Fore
SplineSet
351 230 m 9
 196 230 l 1
︙
3332
3333
3334
3335
3336
3337
3338
3339

3340
3341
3342
3343
3344
3345
3346
3494
3495
3496
3497
3498
3499
3500

3501
3502
3503
3504
3505
3506
3507
3508







-
+







 192 -120 202 -124 217 -124 c 0
 280 -124 284 -35 351 230 c 9
EndSplineSet
Validated: 1
EndChar

StartChar: uniE530
Encoding: 98 58672 98
Encoding: 107 58672 107
Width: 351
Flags: HMW
LayerCount: 2
Fore
SplineSet
204 270 m 9
 360 270 l 1
︙
3404
3405
3406
3407
3408
3409
3410
3411

3412
3413
3414
3415
3416
3417
3418
3566
3567
3568
3569
3570
3571
3572

3573
3574
3575
3576
3577
3578
3579
3580







-
+







 295 400 285 404 270 404 c 0
 228 404 217 332 204 270 c 9
EndSplineSet
Validated: 1
EndChar

StartChar: uniE531
Encoding: 99 58673 99
Encoding: 108 58673 108
Width: 617
Flags: HMW
LayerCount: 2
Fore
SplineSet
472 271 m 9
 628 270 l 1
︙
3494
3495
3496
3497
3498
3499
3500
3501

3502
3503
3504
3505
3506
3507
3508
3656
3657
3658
3659
3660
3661
3662

3663
3664
3665
3666
3667
3668
3669
3670







-
+







 562 400 552 404 537 404 c 0
 495 404 485 332 472 271 c 9
EndSplineSet
Validated: 1
EndChar

StartChar: uniE539
Encoding: 100 58681 100
Encoding: 109 58681 109
Width: 351
Flags: HMW
LayerCount: 2
Fore
SplineSet
80 0 m 0xd8
 41 0 0 33 0 70 c 0xe8
︙
3572
3573
3574
3575
3576
3577
3578
3579

3580
3581
3582
3583
3584
3585
3586
3734
3735
3736
3737
3738
3739
3740

3741
3742
3743
3744
3745
3746
3747
3748







-
+







 465 230 461 230 428 230 c 1
 385 -26 312 -160 182 -160 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE566
Encoding: 101 58726 101
Encoding: 110 58726 110
Width: 509
Flags: HMW
LayerCount: 2
Fore
SplineSet
310 265 m 1
 183 258 l 1
︙
3612
3613
3614
3615
3616
3617
3618
3619

3620
3621
3622
3623
3624
3625
3626
3774
3775
3776
3777
3778
3779
3780

3781
3782
3783
3784
3785
3786
3787
3788







-
+







 230 0 l 1
 242 40 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE567
Encoding: 102 58727 102
Encoding: 111 58727 111
Width: 437
Flags: W
HStem: -3 72<49.5596 110.739> 0 49<319.775 397.094> 141 71<327.059 386.441> 161 47<36.904 118.027>
VStem: 0 24<64.6579 145.71> 413 24<63.9568 143.617>
LayerCount: 2
Fore
SplineSet
︙
3649
3650
3651
3652
3653
3654
3655
3656

3657
3658
3659
3660
3661
3662
3663
3811
3812
3813
3814
3815
3816
3817

3818
3819
3820
3821
3822
3823
3824
3825







-
+







 323 68 l 1
 342 55 353 49 368 49 c 0x5c
EndSplineSet
Validated: 1
EndChar

StartChar: uniE569
Encoding: 103 58729 103
Encoding: 112 58729 112
Width: 437
Flags: HMW
LayerCount: 2
Fore
SplineSet
77 -3 m 0
 33 -3 0 54 0 102 c 0
︙
3691
3692
3693
3694
3695
3696
3697
3698

3699
3700
3701
3702
3703
3704
3705
3853
3854
3855
3856
3857
3858
3859

3860
3861
3862
3863
3864
3865
3866
3867







-
+







 92 61 102 51 102 32 c 0
 102 11 93 -1 77 -3 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE56C
Encoding: 104 58732 104
Encoding: 113 58732 113
Width: 580
Flags: W
HStem: 0 224
LayerCount: 2
Fore
SplineSet
34 46 m 13
︙
3716
3717
3718
3719
3720
3721
3722
3723

3724
3725
3726
3727
3728
3729
3730
3878
3879
3880
3881
3882
3883
3884

3885
3886
3887
3888
3889
3890
3891
3892







-
+







 109 124 l 5
 34 46 l 13
EndSplineSet
Validated: 1
EndChar

StartChar: uniE56D
Encoding: 105 58733 105
Encoding: 114 58733 114
Width: 580
Flags: W
HStem: -53 326<278 305> 0 21G<197.065 232.286 418.065 453.137>
VStem: 278 27<-53 70 150 273>
LayerCount: 2
Fore
SplineSet
︙
3750
3751
3752
3753
3754
3755
3756
3757

3758
3759
3760
3761
3762
3763
3764
3912
3913
3914
3915
3916
3917
3918

3919
3920
3921
3922
3923
3924
3925
3926







-
+







 278 -53 l 25
 278 70 l 1xa0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE582
Encoding: 106 58754 106
Encoding: 115 58754 115
Width: 280
Flags: HMW
LayerCount: 2
Fore
SplineSet
123 0 m 25
 123 123 l 25
︙
3774
3775
3776
3777
3778
3779
3780
3781

3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797

3798
3799
3800
3801
3802
3803
3804
3936
3937
3938
3939
3940
3941
3942

3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958

3959
3960
3961
3962
3963
3964
3965
3966







-
+















-
+







 157 0 l 25
 123 0 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE5D0
Encoding: 107 58832 107
Encoding: 116 58832 116
Width: 310
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 0 m 1
 0 60 l 1
 93 79 229 114 280 370 c 1
 310 370 l 1
 310 175 152 13 0 0 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE5E2
Encoding: 108 58850 108
Encoding: 117 58850 117
Width: 300
Flags: W
HStem: 0 62<198.921 262.211> 96 64<38.8054 112.592>
VStem: 283 16<88.1179 101.401>
LayerCount: 2
Fore
SplineSet
︙
3814
3815
3816
3817
3818
3819
3820
3821

3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841

3842
3843
3844
3845
3846
3847
3848
3849



3850
3851
3852


3853
3854

3855
3856
3857
3858
3859
3860

3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881

3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899

3900
3901
3902
3903
3904
3905
3906
3976
3977
3978
3979
3980
3981
3982

3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002

4003
4004
4005
4006
4007
4008



4009
4010
4011
4012


4013
4014
4015

4016
4017
4018
4019
4020
4021

4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042

4043
4044
4045
4046
4047
4048
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060

4061
4062
4063
4064
4065
4066
4067
4068







-
+



















-
+





-
-
-
+
+
+

-
-
+
+

-
+





-
+




















-
+

















-
+







 55 96 32 79 17 61 c 0
 10 53 -2 61 1 70 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE610
Encoding: 109 58896 109
Encoding: 118 58896 118
Width: 300
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 0 m 25
 0 300 l 25
 300 300 l 25
 300 0 l 25
 265 0 l 25
 265 180 l 25
 35 180 l 29
 35 0 l 29
 0 0 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE612
Encoding: 110 58898 110
Encoding: 119 58898 119
Width: 250
Flags: HMW
LayerCount: 2
Fore
SplineSet
105 0 m 25
 0 450 l 29
 40 450 l 29
105 0 m 29
 0 450 l 25
 40 450 l 25
 125 90 l 25
 210 450 l 29
 250 450 l 29
 210 450 l 25
 250 450 l 25
 145 0 l 25
 105 0 l 25
 105 0 l 29
EndSplineSet
Validated: 1
EndChar

StartChar: uniE614
Encoding: 111 58900 111
Encoding: 120 58900 120
Width: 200
Flags: HMW
LayerCount: 2
Fore
SplineSet
100 25 m 28
 141 25 175 59 175 100 c 28
 175 141 141 175 100 175 c 28
 59 175 25 141 25 100 c 28
 25 59 59 25 100 25 c 28
100 0 m 28
 45 0 0 45 0 100 c 28
 0 155 45 200 100 200 c 28
 155 200 200 155 200 100 c 28
 200 45 155 0 100 0 c 28
EndSplineSet
Validated: 1
EndChar

StartChar: uniE618
Encoding: 112 58904 112
Encoding: 121 58904 121
Width: 280
Flags: HMW
LayerCount: 2
Fore
SplineSet
-200 0 m 1
 -200 110 -110 200 0 200 c 24
 110 200 200 110 200 0 c 1
 170 0 l 1
 170 99 99 170 0 170 c 24
 -99 170 -170 99 -170 0 c 1
 -200 0 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE624
Encoding: 113 58916 113
Encoding: 122 58916 122
Width: 250
Flags: HMW
LayerCount: 2
Fore
SplineSet
90 120 m 0
 110 120 132 165 132 200 c 28
︙
3916
3917
3918
3919
3920
3921
3922
3923

3924
3925
3926
3927
3928
3929
3930

3931
3932
3933
3934


3935
3936
3937

3938
3939
3940


3941
3942
3943
3944


3945
3946
3947
3948


3949
3950
3951
3952
3953
3954

3955
3956
3957
3958
3959
3960
3961
4078
4079
4080
4081
4082
4083
4084

4085
4086
4087
4088
4089
4090
4091

4092
4093
4094


4095
4096
4097
4098

4099
4100


4101
4102
4103
4104


4105
4106
4107
4108


4109
4110
4111
4112
4113
4114
4115

4116
4117
4118
4119
4120
4121
4122
4123







-
+






-
+


-
-
+
+


-
+

-
-
+
+


-
-
+
+


-
-
+
+





-
+







 108 0 l 25
 72 0 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE630
Encoding: 114 58928 114
Encoding: 123 58928 123
Width: 200
Flags: HMW
LayerCount: 2
Fore
SplineSet
84 102 m 1
 36.534641833 109.00393025 -7.1054273576e-15 150.442560477 0 200 c 24
 37 109 -0 150 0 200 c 24
 0 255 45 300 100 300 c 24
 155 300 200 255 200 200 c 24
 200 150.442560477 163.465358167 109.00393025 116 102 c 1
 116 101.288034818 l 1
 200 150 163 109 116 102 c 1
 116 101 l 1
 116 0 l 25
 84 0 l 25
 84 101.288034818 l 1
 84 101 l 1
 84 102 l 1
116 126 m 5
 149.481193966 134.213607404 175 164.479867787 175 200 c 24
116 126 m 1
 149 134 175 164 175 200 c 24
 175 241 141 275 100 275 c 24
 59 275 25 241 25 200 c 24
 25 164.479867787 50.5188060342 134.213607404 84 126 c 1
 84 126.757636954 l 1
 25 164 51 134 84 126 c 1
 84 127 l 1
 84 200 l 25
 116 200 l 25
 116 126.757636954 l 1
 116 126 l 5
 116 127 l 1
 116 126 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE650
Encoding: 115 58960 115
Encoding: 124 58960 124
Width: 1037
Flags: W
HStem: -2 60<361.035 435.033 953.253 1010.82> 0 49<543.265 652.027> 286 36<514.943 580.797> 359 66<58.4784 125.129> 402 58<367.211 444.917> 607 32<177.106 261>
VStem: 0 57<406 505.924> 148 26<463.933 475.584> 200 67<410.241 442> 274.641 84.3586<205.588 329> 425 56<126.64 255.856> 452 64<466.983 558.522> 585 40<194.878 284.873> 675 60<72.5613 225.441> 887 59<79.8865 267.532> 952 60<-0.820206 56.8202>
LayerCount: 2
Fore
SplineSet
︙
4026
4027
4028
4029
4030
4031
4032
4033

4034
4035
4036
4037
4038
4039
4040
4188
4189
4190
4191
4192
4193
4194

4195
4196
4197
4198
4199
4200
4201
4202







-
+







 758 262 735 202 735 117 c 0
 735 73 753 50 797 40 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE655
Encoding: 116 58965 116
Encoding: 125 58965 125
Width: 540
Flags: W
HStem: 68 93<79.5149 155.133> 182 17<220 222.173 252.655 264.303> 192 94<18.5323 100.176 375.888 460.387> 230 56<60.9363 133.273> 230 18<300.618 361.695> 274 18<213.64 227.572 256.894 258> 315 97<73.5817 155.121 322.704 406.413>
VStem: 179 23<209.875 223.47 254.181 264.625> 192 98<17.9339 97.9009 381.276 451.65> 230 18<113.95 174.519 300.936 363.797> 278 17<212.458 224.186 254.5 261>
LayerCount: 2
Fore
SplineSet
︙
4099
4100
4101
4102
4103
4104
4105
4106

4107
4108
4109
4110
4111
4112
4113
4261
4262
4263
4264
4265
4266
4267

4268
4269
4270
4271
4272
4273
4274
4275







-
+







 173 186 179 196 179 204 c 0
 179 219 166 230 141 230 c 0xcb40
EndSplineSet
Validated: 1
EndChar

StartChar: uniE910
Encoding: 117 59664 117
Encoding: 126 59664 126
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
45 0 m 28
 45 -113 137 -205 250 -205 c 28
︙
4125
4126
4127
4128
4129
4130
4131
4132

4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153

4154
4155
4156
4157
4158
4159
4160
4161
4162
4163





4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177












4178
4179
4180
4181
4182
4183

4184
4185
4186
4187
4188
4189
4190
4287
4288
4289
4290
4291
4292
4293

4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314

4315
4316
4317
4318
4319
4320





4321
4322
4323
4324
4325
4326
4327












4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344

4345
4346
4347
4348
4349
4350
4351
4352







-
+




















-
+





-
-
-
-
-
+
+
+
+
+


-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+





-
+







 283 60 310 33 310 0 c 7
 310 -33 283 -60 250 -60 c 7
EndSplineSet
Validated: 1
EndChar

StartChar: uniE911
Encoding: 118 59665 118
Encoding: 127 59665 127
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
45 0 m 24
 45 -113 137 -205 250 -205 c 24
 363 -205 455 -113 455 0 c 24
 455 113 363 205 250 205 c 24
 137 205 45 113 45 0 c 24
0 0 m 24
 0 139 111 250 250 250 c 24
 388 250 500 139 500 0 c 24
 500 -138 389 -250 250 -250 c 24
 112 -250 0 -138 0 0 c 24
EndSplineSet
Validated: 1
EndChar

StartChar: uniE912
Encoding: 119 59666 119
Encoding: 128 59666 128
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
228 350 m 1
 273 350 l 1
 273 250 l 1
 400 237 500 131 500 0 c 0
 500 -130 401 -237 273 -250 c 1
228 350 m 5
 273 350 l 5
 273 250 l 5
 400 237 500 131 500 0 c 4
 500 -130 401 -237 273 -250 c 5
 273 -350 l 5
 228 -350 l 5
 228 -249 l 1
 100 -238 0 -131 0 0 c 0
 0 132 99 238 228 249 c 1
 228 350 l 1
228 -203.826663616 m 1
 228 203.826663616 l 1
 125.298009966 192.809355922 45 105.569124751 45 0 c 0
 45 -105.569124751 125.298009966 -192.809355922 228 -203.826663616 c 1
273 -203.716902537 m 1
 375.222749877 -192.242459755 455 -105.22624212 455 0 c 0
 455 105.22624212 375.222749877 192.242459755 273 203.716902537 c 1
 273 -203.716902537 l 1
 228 -249 l 5
 100 -238 0 -131 0 0 c 4
 0 132 99 238 228 249 c 5
 228 350 l 5
228 -204 m 5
 228 204 l 5
 125 193 45 106 45 0 c 4
 45 -106 125 -193 228 -204 c 5
273 -204 m 5
 375 -193 455 -105 455 0 c 4
 455 105 375 193 273 204 c 5
 273 -204 l 5
EndSplineSet
Validated: 1
EndChar

StartChar: uniE914
Encoding: 120 59668 120
Encoding: 129 59668 129
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
463 130 m 0
 464 127 465 123 465 120 c 0
︙
4209
4210
4211
4212
4213
4214
4215
4216

4217
4218
4219
4220
4221
4222
4223
4371
4372
4373
4374
4375
4376
4377

4378
4379
4380
4381
4382
4383
4384
4385







-
+







 283 60 310 33 310 0 c 3
 310 -33 283 -60 250 -60 c 3
EndSplineSet
Validated: 1
EndChar

StartChar: uniE915
Encoding: 121 59669 121
Encoding: 130 59669 130
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
463 130 m 0
 464 127 465 123 465 120 c 0
︙
4237
4238
4239
4240
4241
4242
4243
4244

4245
4246
4247
4248
4249
4250
4251
4399
4400
4401
4402
4403
4404
4405

4406
4407
4408
4409
4410
4411
4412
4413







-
+







 0 139 111 250 250 250 c 0
 344 250 420 210 463 130 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE918
Encoding: 122 59672 122
Encoding: 131 59672 131
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
273 249 m 1
 356 243 424 203 463 130 c 0
︙
4274
4275
4276
4277
4278
4279
4280
4281

4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297

4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313

4314
4315
4316
4317
4318
4319
4320
4436
4437
4438
4439
4440
4441
4442

4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458

4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474

4475
4476
4477
4478
4479
4480
4481
4482







-
+















-
+















-
+







 125 193 45 106 45 0 c 0
 45 -106 125 -193 228 -204 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE920
Encoding: 123 59680 123
Encoding: 132 59680 132
Width: 120
Flags: HMW
LayerCount: 2
Fore
SplineSet
60 -60 m 3
 27 -60 0 -33 0 0 c 3
 0 33 27 60 60 60 c 3
 93 60 120 33 120 0 c 3
 120 -33 93 -60 60 -60 c 3
EndSplineSet
Validated: 1
EndChar

StartChar: uniE925
Encoding: 124 59685 124
Encoding: 133 59685 133
Width: 45
Flags: HMW
LayerCount: 2
Fore
SplineSet
45 350 m 9
 45 -350 l 1
 0 -350 l 9
 0 350 l 1
 45 350 l 9
EndSplineSet
Validated: 1
EndChar

StartChar: uniE95D
Encoding: 125 59741 125
Encoding: 134 59741 134
Width: 540
Flags: HMW
LayerCount: 2
Fore
SplineSet
30 70 m 25
 30 -70 l 25
︙
4335
4336
4337
4338
4339
4340
4341
4342

4343
4344
4345
4346
4347
4348
4349
4497
4498
4499
4500
4501
4502
4503

4504
4505
4506
4507
4508
4509
4510
4511







-
+







 0 -190 l 25
 0 190 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniEA02
Encoding: 126 59906 126
Encoding: 135 59906 135
Width: 250
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 65 m 25
 137 250 l 25
︙
4359
4360
4361
4362
4363
4364
4365
4366

4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378


4379
4380
4381
4382
4383
4384



























































































































4385
4386
4387















4388
4389
4521
4522
4523
4524
4525
4526
4527

4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538


4539
4540
4541
4542
4543
4544


4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634
4635
4636
4637
4638
4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687







-
+










-
-
+
+




-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


 32 41 l 25
 0 65 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniEAA4
Encoding: 127 60068 127
Encoding: 136 60068 136
Width: 250
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 270 m 0
 30 300 45 311 85 311 c 0
 125 311 170 250 210 250 c 0
 222 250 230 250 250 270 c 0
 252 272 256 273 260 273 c 0
 266 273 272 271 272 264 c 0
 272 257 266 246 250 230 c 0
 266 273 274 273 274 266 c 0
 274 259 266 246 250 230 c 0
 220 200 205 189 165 189 c 0
 125 189 80 250 40 250 c 0
 28 250 20 250 0 230 c 0
 -2 228 -7 226 -11 226 c 0
 -17 226 -22 229 -22 236 c 0
 -22 243 -17 253 0 270 c 0
 -17 226 -24 227 -24 234 c 0
 -24 241 -17 253 0 270 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA2
Encoding: 137 60578 137
Width: 450
Flags: HMW
LayerCount: 2
Fore
SplineSet
337 -20 m 4
 337 50 264 111 193 111 c 4
 154 111 109 85 109 41 c 4
 109 -30 186 -87 257 -87 c 4
 298 -87 337 -65 337 -20 c 4
450 10 m 4
 450 -60 361 -119 225 -119 c 4
 89 -119 0 -60 0 10 c 4
 0 80 89 139 225 139 c 4
 361 139 450 80 450 10 c 4
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA3
Encoding: 138 60579 138
Width: 328
Flags: HMW
LayerCount: 2
Fore
SplineSet
292 68 m 0
 283 84 264 91 242 91 c 0
 164 91 31 10 31 -49 c 0
 31 -56 33 -62 37 -68 c 0
 45 -84 64 -91 86 -91 c 0
 164 -91 297 -10 297 49 c 0
 297 56 295 62 292 68 c 0
299 680 m 1
 328 680 l 1
 328 22 l 2
 328 -70 223 -148 130 -148 c 0
 83 -148 40 -128 15 -81 c 0
 5 -61 0 -41 0 -22 c 0
 0 70 105 148 198 148 c 0
 237 148 273 134 298 103 c 1
 299 680 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA5
Encoding: 139 60581 139
Width: 320
Flags: HMW
LayerCount: 2
Fore
SplineSet
290 680 m 1
 320 680 l 1
 320 40 l 2
 320 -51 231 -135 110 -135 c 0
 40 -135 0 -90 0 -40 c 0
 0 50 90 135 210 135 c 0
 244 135 271 124 290 107 c 1
 290 680 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA7
Encoding: 140 60583 140
Width: 522
Flags: HMW
LayerCount: 2
Fore
SplineSet
320 441 m 1
 320 40 l 2
 320 -51 231 -135 110 -135 c 0
 40 -135 0 -90 0 -40 c 0
 0 50 90 135 210 135 c 0
 244 135 271 124 290 107 c 1
 290 441 l 1
 290 680 l 1
 320 680 l 1
 341 485 522 459 522 196 c 0
 522 137 505 85 472 10 c 1
 485 57 490 104 490 148 c 0
 490 258 462 396 320 441 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniECA9
Encoding: 141 60585 141
Width: 522
Flags: HMW
LayerCount: 2
Fore
SplineSet
290 107 m 1
 290 337 l 1
 290 680 l 1
 320 680 l 1
 352 547 522 491 522 309 c 0
 522 282 514 253 501 222 c 1
 513 196 523 168 523 138 c 0
 523 104 510 67 489 27 c 1
 493 47 494 67 494 85 c 0
 494 219 390 338 320 338 c 1
 320 40 l 2
 320 -51 231 -135 110 -135 c 0
 40 -135 0 -90 0 -40 c 0
 0 50 90 135 210 135 c 0
 244 135 271 124 290 107 c 1
320 509 m 1
 346 402 429 361 494 235 c 1
 494 242 495 248 495 255 c 0
 495 388 391 509 320 509 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniECB7
Encoding: 142 60599 142
Width: 100
Flags: HMW
LayerCount: 2
Fore
SplineSet
50 -50 m 3
 22 -50 0 -28 0 0 c 3
 0 28 22 50 50 50 c 3
 78 50 100 28 100 0 c 3
 100 -28 78 -50 50 -50 c 3
EndSplineSet
EndChar
EndChars
EndSplineFont
Changes to modules/MIDI.js.
1
2
3
4
5
6
7
8
9
10
11
12








13
14
15
16
17
18
19
20
21












































22
23
24
25

26
27
28
29
30
31
32
33
34


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51


52
53

54
55
56

57
58

59


60
61
62
63
64
65
66


67
68
69
70

71
72

73


74
75
76
77
78
79
80
81
82
83
84
85





86


87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

102

103
104


105
106
107
108
109
110
111
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24





25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

72
73








74
75
76
















77
78


79
80
81

82
83

84

85
86
87
88


89


90
91
92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

119
120
121
122
123
124
125
126
127
128
129
130
131
132



133
134
135
136
137
138
139
140
141
142
143
144
145
146












+
+
+
+
+
+
+
+




-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+

-
-
-
-
-
-
-
-
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+


-
+

-
+
-
+
+


-
-

-
-
+
+




+


+
-
+
+












+
+
+
+
+
-
+
+












-
-
-
+

+


+
+







// MIDI.js - module to handle the %%MIDI parameters
//
// Copyright (C) 2019 Jean-Francois Moine - GPL3+
//
// This module is loaded when "%%MIDI" appears in a ABC source.
//
// Parameters (see abcMIDI for details)
//	%%MIDI channel n
//	%%MIDI program n
//	%%MIDI control k v
//	%%MIDI drummap ABC_note MIDI_pitch

// Using %%MIDI drummap creates a voicemap named "MIDIdrum".
// This name must be used if some print map is required:
//	%%MIDI drummap g 42
//	%%map MIDIdrum g heads=x
// A same effect may be done by
//	%%percmap g 42 x
// but this is not abcMIDI compatible!

abc2svg.MIDI = {

    // parse %%MIDI commands
    do_midi: function(parm) {
    var	pits = new Int8Array([0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6]),
	accs = new Int8Array([0, 1, 0, -1, 0, 0, 1, 0, -1, 0, -1, 0])

    // convert a MIDI pitch to a note
    function tonote(p) {

    // convert a ABC note to b40 (string)
    function abc_b40(p) {
    var	pit,
	acc = 0,
	i = 0

	switch (p[0]) {
	case '^':
		if (p[++i] == '^') {
			acc = 2
			i++
		} else {
			acc = 1
		}
		break
	case '=':
		i++
		break
	case '_':
		if (p[++i] == '_') {
			acc = -2
			i++
		} else {
			acc = -1
		}
		break
	}
	pit = 'CDEFGABcdefgab'.indexOf(p[i++]) + 16
	if (pit < 16)
		return
	while (p[i] == "'") {
		pit += 7
		i++
	}
	while (p[i] == ",") {
		pit -= 7
		i++
	}
	return abc2svg.pab40(pit, acc).toString()
    } // abc_b40()

    // convert a MIDI pitch to b40
    function mid_b40(p) {
    var	pit = Number(p)
	if (isNaN(pit))
		return
	p = ((pit / 12) | 0) * 7 - 19;	// octave
	p = (pit / 12) | 0		// octave
	pit = pit % 12;			// in octave
	p += pits[pit]
	note = {
		pit: p
	}
	if (accs[pit])
		note.acc = accs[pit]
	return note
    } // tonote()
	return p * 40 + abc2svg.isb40[pit] + 2
    } // mid_b40()

    // normalize a note for mapping
    function norm(p) {
    var	a = p.match(/^([_^=]*)([A-Ga-g])([,']*)$/)	// '
	if (!a)
		return
	if (p.match(/[A-Z]/)) {
		p = p.toLowerCase();
		if (p.indexOf("'") > 0)
			p = p.replace("'", '')
		else
			p += ','
	}
	return p
    } // norm()

    var	n, v, s,
    // do_midi()
    var	n, v, s, maps,
	maps = this.get_maps(),
		a = parm.split(/\s+/)
	a = parm.split(/\s+/)

	switch (a[1]) {
	case "channel":
	case "channel":				// channel 10 is bank 128
		if (a[2] != "10")
			break
			break			// other channel values are ignored
		this.set_v_param("midictl", "0 1") // channel 10 is bank 128 program 0
		abc2svg.MIDI.do_midi.call(this, "MIDI control 0 1")	// MSB bank
		abc2svg.MIDI.do_midi.call(this, "MIDI control 32 0")	// LSB bank
		break
	case "drummap":
		if (this.cfmt().sound != "play")
			break
//fixme: should have a 'MIDIdrum' per voice?
		n = norm(a[2]);
		v = tonote(a[3]);
		n = abc_b40(a[2])
		v = mid_b40(a[3])
		if (!n || !v) {
			this.syntax(1, this.errs.bad_val, "%%MIDI drummap")
			break
		}
		maps = this.get_maps()
		if (!maps.MIDIdrum)
			maps.MIDIdrum = {}
		if (!maps.MIDIdrum[n])
		maps.MIDIdrum[n] = [null, v];
			maps.MIDIdrum[n] = []
		maps.MIDIdrum[n][3] = v
		this.set_v_param("mididrum", "MIDIdrum")
		break
	case "program":
		if (a[3] != undefined)	// the channel is unused
			v = a[3]
		else
			v = a[2];
		v = parseInt(v)
		if (isNaN(v) || v < 0 || v > 127) {
			this.syntax(1, "Bad program in %%MIDI")
			return
		}
		if (this.parse.state == 3) {
			s = this.new_block("midiprog");
			s.play = true
			s.instr = v
		} else {
		this.set_v_param("instr", v)
			this.set_v_param("instr", v)
		}
		break
	case "control":
		n = parseInt(a[2])
		if (isNaN(n) || n < 0 || n > 127) {
			this.syntax(1, "Bad controller number in %%MIDI")
			return
		}
		v = parseInt(a[3])
		if (isNaN(v) || v < 0 || v > 127) {
			this.syntax(1, "Bad controller value in %%MIDI")
			return
		}
		if (this.cfmt().sound != "play")
			break
		if (this.parse.state >= 2) {
		if (this.parse.state == 3) {
			s = this.new_block("midictl");
			s.play = true
			s.ctrl = n;
			s.val = v
		} else {
			this.set_v_param("midictl", a[2] + ' ' + a[3])
		}
		break
	}
    }, // do_midi()

    // set the MIDI parameters in the current voice
    set_midi: function(a) {
︙
Changes to modules/ambitus.js.
︙
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74
75
76
77
78

79
80
81
82
83

84
85
86
87
88
89
90
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

79
80
81
82
83

84
85
86
87
88
89
90
91







+











-
+




-
+







		if (s.notes[1].pit - s.notes[0].pit > 4) {
			this.xypath(s.x, 3 * (s.notes[1].pit - 18) + staff_tb[s.st].y);
			this.out_svg('v' +
				((s.notes[1].pit - s.notes[0].pit) * 3).toFixed(1) +
				'" stroke-width=".6"/>\n');
		}
		s.x += 26;
		s.nhd = 0
		p_voice.clef.nhd = 0
	}
	of(p_voice)
    }, // draw_symbols()

    set_pitch: function(of, last_s) {
	of(last_s)
	if (!last_s && this.cfmt().ambitus)
		abc2svg.ambitus.do_ambitus.call(this)
    },

    set_fmt: function(of, cmd, param, lock) {
    set_fmt: function(of, cmd, param) {
	if (cmd == "ambitus") {
		this.cfmt().ambitus = param
		return
	}
	of(cmd, param, lock)
	of(cmd, param)
    },

    set_width: function(of, s) {
	if (s.clef_type != undefined && s.nhd > 0) {
		s.wl = 40;
		s.wr = 12
	} else {
︙
Changes to modules/capo.js.
︙
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
36
21
22
23
24
25
26
27

28

29
30
31
32
33
34
35







-
+
-







		if (!gch)
			return
		if (gch.type == 'g')
			break
	}
	gch2 = Object.create(gch);
	gch2.capo = false;		// (would be erased when setting gch)
	gch2.text = this.gch_tr1(gch2.text,
	gch2.text = this.gch_tr1(gch2.text, -abc2svg.ifb40[transp % 12])
			[0, 5, -2, 3, -4, 1, -6, -1, 4, -3, 2, -5][transp % 12])
	if (!this.capo_first) {			// if new tune
		this.capo_first = true;
		gch2.text += "  (capo: " + transp.toString() + ")"
	}

	gch2.font = this.get_font(cfmt.capofont ? "capo" : "annotation");
	a_gch.splice(i, 0, gch2)
︙
47
48
49
50
51
52
53
54

55
56
57
58
59

60
61
62
63
64
65
66
67
68
69
70
71
72
46
47
48
49
50
51
52

53
54
55
56
57

58
59
60
61
62
63
64
65
66
67
68
69
70
71







-
+




-
+













    },

    output_music: function(of) {
	this.capo_first = false;
	of()
    },

    set_fmt: function(of, cmd, param, lock) {
    set_fmt: function(of, cmd, param) {
	if (cmd == "capo") {
		this.cfmt().capo = param
		return
	}
	of(cmd, param, lock)
	of(cmd, param)
    },

    set_hooks: function(abc) {
	abc.gch_build = abc2svg.capo.gch_build.bind(abc, abc.gch_build);
	abc.output_music = abc2svg.capo.output_music.bind(abc, abc.output_music);
	abc.set_format = abc2svg.capo.set_fmt.bind(abc, abc.set_format)
    }
} // capo

abc2svg.modules.hooks.push(abc2svg.capo.set_hooks);

// the module is loaded
abc2svg.modules.capo.loaded = true
Added modules/clair.js.




















































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// clair.js - module to output Clairnote sheets (https:clairnote.org)
//
// Copyright (C) 2019 Jean-Francois Moine - GPL3+
//
// This module is loaded when "%%clairnote" appears in a ABC source.
//
// Parameters (none)
//	%%clairnote 1

abc2svg.clair = {

// table for helper lines (from C, to c'')
// index = (note pitch + 16)
// value = array of staff offsets / null
    hl_tb: [
	new Int8Array([-20,-16,-12,-8,-4]), // _B,, -18
	new Int8Array([-20,-18,-16,-12,-8,-4]), // B,, -17
	new Int8Array([-16,-12,-8,-4]),	// C, -16
	new Int8Array([-16,-12,-8,-4]),	// ^C, -15
	new Int8Array([-16,-14,-12,-8,-4]), // D, -14
	new Int8Array([-12,-8,-4]),	// ^D, -13
	new Int8Array([-12,-8,-4]),	// E, -12
	new Int8Array([-12,-8,-4]),	// F, -11
	new Int8Array([-12,-10,-8,-4]),	// ^F, -10
	new Int8Array([-8,-4]),		// G, -9
	new Int8Array([-8,-4]),		// ^G, -8
	new Int8Array([-8,-4]),		// A, -7
	new Int8Array([-8,-6,-4]),	// _B, -6
	new Int8Array([-4]),		// B, -5
	new Int8Array([-4]),		// C -4
	new Int8Array([-4]),		// ^C -3
	new Int8Array([-4,-2]),		// D -2
	null,				// ^D -1
	null,				// E 0
	null,				// F 1
	new Int8Array([2]),		// ^F 2
	null,				// G 3
	null,				// ^G 4
	null,				// A 5
	new Int8Array([6,8]),		// _B 6
	new Int8Array([8]),		// B 7
	new Int8Array([8]),		// c 8
	new Int8Array([8]),		// ^c 9
	new Int8Array([8,10]),		// d 10
	null,				// _e 11
	null,				// e 12
	null,				// f 13
	new Int8Array([14]),		// ^f 14
	null,				// g 15
	null,				// ^g 16
	null,				// a 17
	new Int8Array([18,20]),		// _b 18
	new Int8Array([20]),		// b 19
	new Int8Array([20]),		// c' 20
	new Int8Array([20]),		// ^c' 21
	new Int8Array([20,22]),		// d' 22
	new Int8Array([20,22]),		// _e' 23
	new Int8Array([20,24]),		// e' 24
	new Int8Array([20,24]),		// f' 25
	new Int8Array([20,24,26]),	// ^f' 26
	new Int8Array([20,24,26]),	// g' 27
	new Int8Array([20,24,28]),	// ^g' 28
	new Int8Array([20,24,28]),	// a' 29
	new Int8Array([20,24,28,30]),	// _b' 30
	new Int8Array([20,24,28,30]),	// b' 31
	new Int8Array([20,24,28,32])	// c'' 32
    ],

// draw the helper lines
    draw_hl: function(of, x, s, hltype) {
	if (!s.p_v.clair) {
		of(x, s, hltype)
		return
	}

    var	i, m, hl,
	staffb = this.get_staff_tb()[s.st].y

	for (m = 0; m <= s.nhd; m++) {
		hl = abc2svg.clair.hl_tb[s.notes[m].pit]
		if (!hl)
			continue
		for (i = 0; i < hl.length; i++)
			this.xygl(x, staffb + hl[i] * 3, hltype)
	}
    }, // draw_hl()

// draw the key signature
    draw_keysig: function(of, x, s) {
	if (!s.p_v.clair) {
		of(x, s)
		return
	}

    var	i,
	staffb = this.get_staff_tb()[s.st].y,
	a_tb = new Int8Array(24),
	sc = [0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23],
						// major scale (2 octaves)
	bn_tb = [7, 2, -3, 4, -1, 6, 1, 8, 3, -2, 5, 0, 7, 2, -3],
						// #sf -> (major) base note
	bn = bn_tb[s.k_sf + 7]			// base note

	if (!s.k_a_acc) {
		if (s.k_sf > 0) {
			for (i = 1; i <= s.k_sf; i++) {
				a_tb[bn_tb[i] + 3] = 1;		// index = y + 3
				a_tb[bn_tb[i] + 12 + 3] = 1
			}
			if (bn + sc[s.k_mode] > 8)
				bn -= 12
			for (i = 7; --i >= 0; ) {
				y = bn + sc[s.k_mode + i]
				this.xygl(x, staffb + y * 3, (y & 1) ? "wk" : "bk");
				if (a_tb[y + 3])
					this.xygl(x,
						staffb + y * 3,
						"sht");
				switch (s.k_mode + i) {
				case 3:
				case 7:
				case 10:
					x += 4.5
					break
				}
			}
		} else {
			for (i = s.k_sf; i < 0; i++) {
				a_tb[bn_tb[i + 6] + 3] = 1;	// index = y + 3
				a_tb[bn_tb[i + 6] + 12 + 3 ] = 1
			}
			if (bn + sc[s.k_mode] > 8)
				bn -= 12
			for (i = 0; i < 7; i++) {	// down top
				y = bn + sc[s.k_mode + i]
				this.xygl(x, staffb + y * 3, (y & 1) ? "wk" : "bk");
				if (a_tb[y + 3])
					this.xygl(x,
						staffb + y * 3,
						"flt");
				switch (s.k_mode + i) {
				case 2:
				case 6:
				case 9:
					x += 4.5
					break
				}
			}
		}
	} else if (s.k_a_acc.length) {
		for (i = 0; i < s.k_a_acc.length; i++) {
		    var	acc = s.k_a_acc[i];
			j = sc[(acc.pit + 3) % 7];
			a_tb[j] = acc.acc;
			a_tb[j + 12] = acc.acc
		}
// todo
	}
    }, // draw_keysig()

// update a clef
    new_clef: function(s) {
	switch (s.clef_type) {
	case 't':
		s.clef_line = 3;
		s.clefpit = -2;
		s.clef_name = "cl4"
		break
	case 'c':
		s.clef_line = 3;
		s.clefpit = 6;
		s.clef_name = "cl3"
		break
	case 'b':
		s.clef_line = 3;
		s.clefpit = 14;
		s.clef_name = "cl2"
		break
	}
    }, // new_clef()

// function called before SVG generation
    output_music: function(of) {
    var	s, m, mp, p_v, v,
	cfmt = this.cfmt(),
	tsfirst = this.get_tsfirst(),
	voice_tb = this.get_voice_tb(),
	abcmidi = new AbcMIDI();

	abcmidi.add(tsfirst, voice_tb);		// get the MIDI pitches

	// define the new clefs and key signatures
	// (clefs converted from clairnote.ly)
	this.do_begin_end("svg", null, '<defs>\n\
<g id="cl2">\n\
<path transform="translate(-9,-27) scale(8)" d="m0.2656 -0.78107\n\
 C0.3775 -0.79547 0.4351 -0.84567 0.7003 -0.85587\n\
 C0.9459 -0.86587 1.0531 -0.85987 1.1805 -0.83797\n\
 C1.6967 -0.74937 2.1173 -0.13032 2.1173 0.64059\n\
 C2.1173 2.10531 0.9987 3.04975 0.019 3.8078\n\
 C0 3.8345 0 3.846 0 3.8652\n\
 C0 3.9101 0.022 3.94 0.056 3.94\n\
 C0.071 3.94 0.079 3.93904 0.107 3.9231\n\
 C1.3341 3.23572 2.6095 2.2656 2.6095 0.57604\n\
 C2.6095 -0.4711 2.0006 -1.05061 1.1664 -1.05061\n\
 C0.9058 -1.05561 0.7658 -1.05861 0.5568 -1.02591\n\
 C0.4588 -1.01061 0.248 -0.97281 0.219 -0.92831\n\
 C0.165 -0.89151 0.162 -0.77308 0.266 -0.78129"/>\n\
<text x="-5" y="-24" font-size="28px">&#xe083;</text>\n\
</g>\n\
<g id="cl3">\n\
<path transform="translate(-9,-12) scale(8)" d="m1.0406 -2.93878\n\
 C0.9606 -2.93578 0.8881 -2.93178 0.8237 -2.92878\n\
 L0.8237 -2.92846\n\
 C0.6586 -2.92046 0.4659 -2.89806 0.3697 -2.87906\n\
 C0.1409 -2.83386 0.0236 -2.78916 0 -2.75937\n\
 C-0.018 -2.73927 -0.015 -2.71087 0 -2.69037\n\
 C0.023 -2.64587 0.145 -2.67017 0.4188 -2.72887\n\
 C0.5108 -2.74867 0.6924 -2.76597 0.8607 -2.77257\n\
 C1.0868 -2.78157 1.2883 -2.70417 1.3194 -2.69167\n\
 C1.7053 -2.53668 2.0444 -2.24033 2.0444 -1.46855\n\
 C2.0444 -0.8488 1.8942 -0.04261 1.4629 -0.04261\n\
 C1.4489 -0.04061 1.4419 -0.03861 1.4289 -0.02891\n\
 C1.4149 -0.01311 1.4179 -0.00091 1.4169 0.01179\n\
 C1.4169 0.01193 1.4169 0.01195 1.4169 0.01211\n\
 C1.4169 0.01225 1.4169 0.01227 1.4169 0.01243\n\
 C1.4169 0.02513 1.4169 0.03723 1.4289 0.05313\n\
 C1.4389 0.06213 1.4479 0.06493 1.4629 0.06683\n\
 C1.8942 0.06683 2.0444 0.87302 2.0444 1.49278\n\
 C2.0444 2.26455 1.7053 2.56059 1.3194 2.71559\n\
 C1.2884 2.72799 1.0868 2.80579 0.8607 2.79679\n\
 C0.6924 2.78979 0.5113 2.77259 0.4188 2.75279\n\
 C0.145 2.69409 0.0231 2.66979 0 2.71429\n\
 C-0.011 2.73479 -0.014 2.76349 0 2.78359\n\
 C0.024 2.81339 0.1409 2.85799 0.3697 2.90328\n\
 C0.4657 2.92228 0.6586 2.94468 0.8237 2.95268\n\
 L0.8237 2.953\n\
 C0.9525 2.958 1.1126 2.9714 1.305 2.96\n\
 C1.9479 2.916 2.5587 2.47655 2.5587 1.48844\n\
 C2.5587 0.89409 2.1807 0.20184 1.7065 0.01218\n\
 C2.1807 -0.17748 2.5587 -0.86972 2.5587 -1.46406\n\
 C2.5587 -2.45218 1.9479 -2.89194 1.305 -2.93594\n\
 C1.209 -2.94194 1.1207 -2.94094 1.0406 -2.93794"/>\n\
<text x="-5,-5" y="0,-24" font-size="28px">&#xe083;&#xe084;</text>\n\
</g>\n\
<g id="cl4">\n\
<path transform="translate(-9,3) scale(8)" d="m1.5506 -4.76844\n\
 C1.5376 -4.76844 1.5066 -4.75114 1.5136 -4.73384\n\
 L1.7544 -4.17292\n\
 C1.8234 -3.97367 1.8444 -3.88334 1.8444 -3.66416\n\
 C1.8444 -3.16204 1.5635 -2.76967 1.2174 -2.38312\n\
 L1.0789 -2.2278\n\
 C0.5727 -1.68982 0 -1.16441 0 -0.45906\n\
 C0 -0.36713 -0.6414 1.05 1.4549 1.05\n\
 C1.5319 1.05 1.6984 1.0492 1.8799 1.0372\n\
 C2.0139 1.0282 2.1594 0.9969 2.2732 0.9744\n\
 C2.3771 0.9538 2.5752 0.8757 2.5752 0.8757\n\
 C2.7512 0.8152 2.6612 0.62915 2.5442 0.6835\n\
 C2.5442 0.6835 2.3481 0.7626 2.2449 0.7822\n\
 C2.1355 0.803 1.9939 0.8319 1.8645 0.8382\n\
 C1.6935 0.8462 1.5257 0.8402 1.4569 0.8352\n\
 C1.1541 0.8139 0.8667 0.67432 0.6558 0.48763\n\
 C0.5148 0.36284 0.3782 0.17408 0.3582 -0.12709\n\
 C0.3582 -0.76471 0.792 -1.23147 1.255 -1.71365\n\
 L1.3978 -1.86523\n\
 C1.8046 -2.29959 2.185 -2.75829 2.185 -3.32815\n\
 C2.185 -3.77846 1.9185 -4.42204 1.6113 -4.75678\n\
 C1.5983 -4.76858 1.5713 -4.77188 1.5513 -4.76828"/>\n\
<text x="-3" y="0" font-size="28px">&#xe084;</text>\n\
</g>\n\
<ellipse id="bk" class="fill" rx="2.9" ry="2.4"/>\n\
<ellipse id="wk" class="stroke" stroke-width="1" rx="2.5" ry="2"/>\n\
<path id="flt" class="stroke" stroke-width="1.5" d="m-2.5 -1l-7 -7"/>\n\
<path id="sht" class="stroke" stroke-width="1.5" d="m-2.5 1l-7 7"/>\n\
</defs>')

	// change the pitches of all notes
	// (pitch + accidental => offset)
	for (v = 0; v < voice_tb.length; v++) {
		p_v = voice_tb[v]
		if (!p_v.clair)
			continue
		p_v.scale = 1.3;
		for (s = p_v.sym; s; s = s.next) {
			if (!s.dur)
				continue
			for (m = 0; m <= s.nhd; m++) {
				mp = s.notes[m].midi
				if (mp) {
					mp -= 46;
					s.notes[m].pit = mp
					delete s.notes[m].acc
				}
			}
		}
	}
	of()
    },

// set a format parameter
    set_fmt: function(of, cmd, param) {
	if (cmd == "clairnote") {
		this.set_v_param("clair", true);
		this.set_v_param("stafflines", "|-|---|-|")
		this.set_v_param("staffscale", .8)
		return
	}
	of(cmd, param)
    },

// set the pitches according to the clefs
    set_pitch: function(of, last_s) {
	if (last_s) {
		of(last_s)
		return			// not the 1st time
	}
    var	p_v, s, v,
	voice_tb = this.get_voice_tb(),
	staff_tb = this.get_staff_tb()

	for (v = 0; v < voice_tb.length; v++) {
		p_v = voice_tb[v]
		if (!p_v.clair)
			continue
		abc2svg.clair.new_clef(staff_tb[p_v.st].clef)
		for (s = p_v.sym; s; s = s.next) {
			if (s.clef_type)
				abc2svg.clair.new_clef(s)
		}
	}
	of(last_s)
    },

// set the clairnote flag in the current voice
    set_vp: function(of, a) {
    var	i,
	curvoice = this.get_curvoice()

	for (i = 0; i < a.length; i++) {
		if (a[i] == "clair=") {
			curvoice.clair = a[i + 1]
			break
		}
	}
	of(a)
    },

// set the width of the clairnote key signatures
    set_width: function(of, s) {
	if (s.k_sf && s.p_v && s.p_v.clair) {
		s.wl = 8;
		s.wr = 10
	} else {
		of(s)
	}
    },

    set_hooks: function(abc) {
	abc.draw_hl = abc2svg.clair.draw_hl.bind(abc, abc.draw_hl);
	abc.draw_keysig = abc2svg.clair.draw_keysig.bind(abc, abc.draw_keysig);
	abc.output_music = abc2svg.clair.output_music.bind(abc, abc.output_music);
	abc.set_format = abc2svg.clair.set_fmt.bind(abc, abc.set_format);
	abc.set_pitch = abc2svg.clair.set_pitch.bind(abc, abc.set_pitch);
	abc.set_vp = abc2svg.clair.set_vp.bind(abc, abc.set_vp);
	abc.set_width = abc2svg.clair.set_width.bind(abc, abc.set_width)
    }
} // clair

abc2svg.modules.hooks.push(abc2svg.clair.set_hooks);

// the module is loaded
abc2svg.modules.clairnote.loaded = true
Changes to modules/combine.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14


15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21
22













-
+
+







// combine.js - module to add a combine chord line
//
// Copyright (C) 2018-2019 Jean-Francois Moine - GPL3+
//
// This module is loaded when "%%voicecombine" appears in a ABC source.
//
// Parameters
//	%%voicecombine n	'n' is the combine level

abc2svg.combine = {

    // function called at start of the generation when multi-voices
    comb_v: function() {
    var	C = abc2svg.C
    var	C = abc2svg.C,
	delsym = []		// deleted symbols for slurs and ties

    // check if voice combine may occur
    function may_combine(s) {
    var	nhd2,
	s2 = s.ts_next

	if (!s2 || (s2.type != C.NOTE && s2.type != C.REST))
︙
34
35
36
37
38
39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

59


60
61

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81






82
83
84
85
86
87
88
35
36
37
38
39
40
41

42


43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

59
60
61

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76






77
78
79
80
81
82
83
84
85
86
87
88
89







-
+
-
-















+
-
+
+

-
+














-
-
-
-
-
-
+
+
+
+
+
+







	if (s.a_gch && s2.a_gch)
		return false
	if (s.type == C.REST) {
		if (s.type == s2.type && s.invis && !s2.invis)
			return false
		return true
	}
	if (s2.a_ly
	if (s2.a_ly)
	 || s2.sl1 || s2.sl2
	 || s2.slur_start || s2.slur_end)
		return false
	if (s2.beam_st != s.beam_st
	 || s2.beam_end != s.beam_end)
		return false;
	nhd2 = s2.nhd
	if (s.combine <= 1
	 && s.notes[0].pit <= s2.notes[nhd2].pit + 1)
		return false
	return true
    } // may_combine()

    // combine two notes
    function combine_notes(s, s2) {
    var	nhd, type, m;

	for (m = 0; m <= s2.nhd; m++)	// change the container of the notes
	s.notes = s.notes.concat(s2.notes);
		s2.notes[m].s = s
	Array.prototype.push.apply(s.notes, s2.notes);
	s.nhd = nhd = s.notes.length - 1;
	this.sort_pitch(s)		// sort the notes by pitch
	s.notes.sort(abc2svg.pitcmp)	// sort the notes by pitch

	if (s.combine >= 3) {		// remove unison heads
		for (m = nhd; m > 0; m--) {
			if (s.notes[m].pit == s.notes[m - 1].pit
			 && s.notes[m].acc == s.notes[m - 1].acc)
				s.notes.splice(m, 1)
		}
		s.nhd = nhd = s.notes.length - 1
	}

	s.ymx = 3 * (s.notes[nhd].pit - 18) + 4;
	s.ymn = 3 * (s.notes[0].pit - 18) - 4;

	// force the tie directions
	type = s.notes[0].ti1
	if ((type & 0x0f) == C.SL_AUTO)
		s.notes[0].ti1 = C.SL_BELOW | (type & ~C.SL_DOTTED);
	type = s.notes[nhd].ti1
	if ((type & 0x0f) == C.SL_AUTO)
		s.notes[nhd].ti1 = C.SL_ABOVE | (type & ~C.SL_DOTTED)
	type = s.notes[0].tie_ty
	if ((type & 0x07) == C.SL_AUTO)
		s.notes[0].tie_ty = C.SL_BELOW | (type & C.SL_DOTTED);
	type = s.notes[nhd].tie_ty
	if ((type & 0x07) == C.SL_AUTO)
		s.notes[nhd].tie_ty = C.SL_ABOVE | (type & C.SL_DOTTED)
} // combine_notes()

// combine 2 voices
function do_combine(s) {
	var s2, nhd, nhd2, type

	while (1) {
︙
96
97
98
99
100
101
102


103
104








105
106
107
108
109
110


111
112




113
114
115
116
117
118
119
120














121
122
123
124
125
126
127
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156







+
+


+
+
+
+
+
+
+
+






+
+
-
-
+
+
+
+








+
+
+
+
+
+
+
+
+
+
+
+
+
+







			}
		} else if (s.type == C.REST) {
			if (s.invis
			 && !s2.invis)
				delete s.invis
		} else {
			combine_notes.call(this, s, s2)
			if (s2.tie_s)
				s.tie_s = s2.tie_s
		}

		if (s2.sls) {
			if (s.sls)
				Array.prototype.push.apply(s.sls, s2.sls)
			else
				s.sls = s2.sls
		}
		if (s2.sl1)
			s.sl1 = true
		if (s2.a_gch)
			s.a_gch = s2.a_gch
		if (s2.a_dd) {
			if (!s.a_dd)
				s.a_dd = s2.a_dd
			else
				Array.prototype.push.apply(s.a_dd, s2.a_dd)
		}
				s.a_dd = s.a_dd.concat(s2.a_dd)
		}

		// memorize the deleted symbol: it may support slur or tie endings
		delsym.push({s: s2, r: s});

		this.unlksym(s2)			// remove the next symbol

		// there may be more voices
		if (s.in_tuplet || !may_combine.call(this, s))
			break
	}
} // do_combine()

    // replace tie endings
    function tie_repl(s) {
    var	s1 = s.tie_s,
	i = delsym.length

	while (--i >= 0) {
		if (delsym[i].s == s1) {
			s.tie_s = delsym[i].r
			break
		}
	}
    } // tie_repl()

	// code of comb_v()
	var s, s2, g, i, r

	for (s = this.get_tsfirst(); s; s = s.ts_next) {
		switch (s.type) {
		case C.REST:
			if (s.combine == undefined || s.combine < 0)
				continue
︙
166
167
168
169
170
171
172






173
174
175
176
177
178
179
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214







+
+
+
+
+
+







			if (s2.beam_end)
				break
			do {
				s2 = s2.next
			} while (s2.type != C.NOTE && s2.type != C.REST)
		}
	}

	// replace the tie endings
	for (s = this.get_tsfirst(); s; s = s.ts_next) {
		if (s.tie_s)
			tie_repl(s)
	}
    }, // comb_v()

    do_pscom: function(of, text) {
	if (text.slice(0, 13) == "voicecombine ")
		this.set_v_param("combine", text.split(/[ \t]/)[1])
	else
		of(text)
︙
Changes to modules/diag.js.
︙
478
479
480
481
482
483
484
485

486
487
488
489
490

491
492
493
494
495
496
497
478
479
480
481
482
483
484

485
486
487
488
489

490
491
492
493
494
495
496
497







-
+




-
+








    output_music: function(of) {
	if (this.cfmt().diag)
		abc2svg.diag.do_diag.call(this)
	of()
    },

    set_fmt: function(of, cmd, param, lock) {
    set_fmt: function(of, cmd, param) {
	if (cmd == "diagram") {
		this.cfmt().diag = param
		return
	}
	of(cmd, param, lock)
	of(cmd, param)
    },

    set_hooks: function(abc) {
	abc.output_music = abc2svg.diag.output_music.bind(abc, abc.output_music);
	abc.set_format = abc2svg.diag.set_fmt.bind(abc, abc.set_format)
    }
} // diag
︙
Changes to modules/equalbars.js.
︙
12
13
14
15
16
17
18
19

20
21
22
23

24
25
26
27
28
29
30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
45
46
47
12
13
14
15
16
17
18

19
20
21
22

23
24
25
26
27
28
29
30
31
32
33
34
35
36

37
38


39
40
41
42
43
44
45







-
+



-
+













-
+

-
-







    // new tune - clear the width of the start of the staff
    output_music: function(of) {
	this.equalbars_d = 0;
	of()
    },

    // get the equalbars parameter
    set_fmt: function(of, cmd, param, lock) {
    set_fmt: function(of, cmd, param) {
	if (cmd == "equalbars")
		this.cfmt().equalbars = this.get_bool(param)
	else
		of(cmd, param, lock)
		of(cmd, param)
    },

    // adjust the symbol offsets of a music line
    set_sym_glue: function(of, width) {
    var	C = abc2svg.C,
	s, s2, w, i, n, x, g, t, t0,
	bars = [],
	tsfirst = this.get_tsfirst();

	of(width)			// compute the x offset of the symbols
	if (!this.cfmt().equalbars)
		return

	// search the first note/rest/bar
	// search the first note/rest/space
	for (s2 = tsfirst; s2; s2 = s2.ts_next) {
		if (!s2.seqst)
			continue
		switch (s2.type) {
		default:
			continue
		case C.GRACE:
		case C.MREST:
		case C.NOTE:
		case C.REST:
︙
57
58
59
60
61
62
63



64




65
66
67
68
69
70
71
72
73
74
75

76
77

78

79
80
81
82
83
84
85
55
56
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78

79


80
81
82
83
84
85
86
87
88
89







+
+
+
-
+
+
+
+










-
+
-
-
+

+







	t0 = t = s2.time
	for (s = s2; s.ts_next; s = s.ts_next) {
		if (s.type == C.BAR && s.seqst) {
			bars.push([s, s.time - t]);
			t = s.time
		}
	}

	// push the last bar if it is not the invisible bar after a key change
	if (!s.invis || s.prev.type != C.KEY)
	bars.push([s, s.time - t]);
		bars.push([s, s.time - t])
	else
		bars[bars.length - 1][0] = s	// replace the last bar
	width = s.x
	t = s.time
	if (s.dur)
		t += s.dur;

	n = bars.length
	if (n <= 1)
		return				// one or no bar

	// set the measure parameters
	x = s2.type == C.GRACE ? s2.extra.x : s2.x;
	d = this.equalbars_d
	if (this.equalbars_d < x)
	if (!d)
		d = this.equalbars_d = x;	// offset first note/rest
		this.equalbars_d = x		// new offset of the first note/rest

	d = this.equalbars_d
	w = (width - d) / (t - t0)		// width per time unit

	// loop on the bars
	for (i = 0; i < n; i++) {
		s = bars[i][0];			// next bar
		f = w * bars[i][1] / (s.x - x)

︙
Changes to modules/grid.js.
1
2
3
4
5
6
7
8

9
10
11
12
13

14
15
16
17
18
19
20
1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16
17
18
19
20
21







-
+





+







// grid.js - module to insert a chord grid before or after a tune
//
// Copyright (C) 2018-2019 Jean-Francois Moine - GPL3+
//
// This module is loaded when "%%grid" appears in a ABC source.
//
// Parameters
//	%%grid <n> [include=<list>] [nomusic] [norepeat]
//	%%grid <n> [include=<list>] [nomusic] [norepeat] [repbrk]
//		<n> = number of columns (1: auto)
//			> 0: above the tune, < 0: under the tune
//		<list> = comma separated list of (continuous) measure numbers
//		'nomusic' displays only the grid
//		'norepeat' omits the ':' indications
//		'repbrk' starts a new grid line on start/stop repeat
//	%%gridfont font_name size (default: 'serif 16')

abc2svg.grid = {

// function called before tune generation
    do_grid: function() {
    var	C = abc2svg.C,
︙
34
35
36
37
38
39
40
41
42


43
44
45



46
47
48
49
50
51
52
35
36
37
38
39
40
41


42
43

44

45
46
47
48
49
50
51
52
53
54







-
-
+
+
-

-
+
+
+







	 && s.a_meter[0].top[0] % 3 == 0)
		beat = C.BLEN / 8 * 3
	return beat
} // get_beat()

// generate the grid
function build_grid(chords, bars, font, wmx) {
    var	i, j, k, l, nr, line, bar, w, hr, x0, x, y, yl,
	cell = '',
    var	i, k, l, nr, line, bar, w, hr, x0, x, y, yl,
	lc = '',
	pcell = '',
	cells = [],
	nc = grid.n
	nc = grid.n,
	sf = '" style="font-size:' + (font.size * .72).toFixed(1)
						// small font

	// set some chord(s) in each cell
	function set_chords() {
	    var	i, ch,
		pch = '-'

		for (i = 0; i < chords.length; i++) {
︙
70
71
72
73
74
75
76




























































77
78
79
80
81
82
83
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







				continue
			}
			if (!ch[2])
				ch[2] = ch[1] || ch[0];
			pch = ch[3]
		}
	} // set_chords()

	function build_cell(cell, x, y, yl, hr) {
	    var	line = ''
		if (cell.length > 1) {
			line += '<path class="stroke" stroke-width="1" d="M' +
				(x - wmx / 2).toFixed(1) + ' ' +
				yl.toFixed(1) + 'l' +
				wmx.toFixed(1) + ' -' + hr.toFixed(1) +
				'"/>\n'
			if (cell[1]) {
			    line += '<path class="stroke" stroke-width="1" d="M' +
				(x - wmx / 2).toFixed(1) + ' ' +
				(yl - hr).toFixed(1) + 'l' +
				(wmx / 2).toFixed(1) + ' ' + (hr / 2).toFixed(1) +
				'"/>\n'
			    line += '<text class="' + cls + sf + '" x="' +
				(x - wmx / 3).toFixed(1) + '" y="' +
				y.toFixed(1) + '">' +
				cell[0] + '</text>\n'
			    line += '<text class="' + cls + sf + '" x="' +
				x.toFixed(1) + '" y="' +
				(y - hr / 3).toFixed(1) + '">' +
				cell[1] + '</text>\n'
			} else {
			    line += '<text class="' + cls + sf + '" x="' +
				(x - wmx * .2).toFixed(1) + '" y="' +
				(y - hr / 4).toFixed(1) + '">' +
				cell[0] + '</text>\n'
			}
			if (cell.length >= 3) {
			  if (cell[3]) {
			    line += '<path class="stroke" stroke-width="1" d="M' +
				x.toFixed(1) + ' ' +
				(yl - hr / 2).toFixed(1) + 'l' +
				(wmx / 2).toFixed(1) + ' ' + (hr / 2).toFixed(1) +
				'"/>\n'
			    line += '<text class="' + cls + sf + '" x="' +
				x.toFixed(1) + '" y="' +
				(y + hr / 3).toFixed(1) + '">' +
				cell[2] + '</text>\n'
			    line += '<text class="' + cls + sf + '" x="' +
				(x + wmx / 3).toFixed(1) + '" y="' +
				y.toFixed(1) + '">' +
				cell[3] + '</text>\n'
			  } else {
			    line += '<text class="' + cls + sf + '" x="' +
				(x + wmx * .2).toFixed(1) + '" y="' +
				(y + hr / 4).toFixed(1) + '">' +
				cell[2] + '</text>\n'
			  }
			}
		} else {
			line += '<text class="' + cls + '" x="' +
				x.toFixed(1) + '" y="' + y.toFixed(1) + '">' +
				cell[0] + '</text>\n'
		}
		return line
	} // build_cell()

	// build_grid()

	// set some chords in each cell
	set_chords()

	// build the content of the cells
	if (!grid.ls) {
		cells = chords
︙
95
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120





















121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143

144
145
146
147
148
149
150
151
152
153
154
155

156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220

221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238




239
240

241

242

243
244
245
246
247
248
249
157
158
159
160
161
162
163

164
165
166
167
168
169
170

171
172
173
174
175
176
177
178

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

222

223
224
225
226
227
228
229
230
231
232
233
234

235

































































236





237
238
239
240
241
242
243
244
245
246
247
248

249
250
251
252
253
254
255
256
257

258
259
260
261
262
263
264
265







-
+






-








-


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




















-

-
+











-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-












-
+
+
+
+


+

+
-
+







					break
				cells.push(chords[k]);
				bars.push(bar[k])
			}
		}
	}

	// get the number of columns and rows
	// get the number of columns
	if (nc < 0)
		nc = -nc
	if (nc < 3)				// auto
		nc = cells.length % 6 == 0 ? 6 : 8
	if (nc > cells.length)
		nc = cells.length;
	nr = ((cells.length + nc - 1) / nc) |0;

	hr = font.size * 2
	if (wmx < hr * 1.4)
		wmx = hr * 1.4;				// cell width

	w = wmx * nc
	if (w > img.width) {
		nc /= 2;
		nr *= 2;
		w /= 2
	}

	// generate the cells
	yl = 1
	y = 1 - hr / 2 + font.size * .3
	nr = 0
	x0 = (img.width - w) / 2
	for (i = 0; i < cells.length; i++) {
		if (i == 0
		 || (grid.repbrk
		  && (bars[i].slice(-1) == ':' || bars[i][0] == ':'))
		 || k >= nc) {
			y += hr			// new row
			yl += hr
			x = x0 + wmx / 2
			k = 0
			nr++
		}
		k++
		lc += build_cell(cells[i], x, y, yl, hr)
		x += wmx
	}

	// build the SVG image
	line = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	color="black" width="' + img.width.toFixed(0) +
			'px" height="' + (hr * nr + 6).toFixed(0) + 'px"';
	i = cfmt.bgcolor
	if (i)
		line += ' style="background-color: ' + i + '"';
	line += '>\n<style type="text/css">\n\
.mid {text-anchor:middle}\n'

	if (cfmt.fullsvg || grid.nomusic)
		line += '\
.stroke {stroke: currentColor; fill: none}\n\
.' + font_cl + ' {' + this.style_font(font) +  '}\n';
	line += '</style>\n'

	// draw the lines
	line += '<path class="stroke" d="\n';
	x0 = (img.width - w) / 2;
	y = 1
	for (j = 0; j <= nr; j++) {
	for (i = 0; i <= nr; i++) {
		line += 'M' + x0.toFixed(1) + ' ' + y.toFixed(1) +
			'h' + w.toFixed(1)+ '\n';
		y += hr
	}
	x = x0
	for (i = 0; i <= nc; i++) {
		line += 'M' + x.toFixed(1) + ' 1v' + (hr * nr).toFixed(1) + '\n';
		x += wmx
	}
	line += '"/>\n';

	// insert the chords
	// insert the cells
	y = 1 - hr / 2 + font.size * .3;
	yl = 1
	for (i = 0; i < cells.length; i++) {
		cell = cells[i]
		if (i % nc == 0) {
			y += hr;			// new row
			yl += hr;
			x = x0 + wmx / 2
		}
		if (cell.length > 1) {
			line += '<path class="stroke" stroke-width="1" d="M' +
				(x - wmx / 2).toFixed(1) + ' ' +
				yl.toFixed(1) + 'l' +
				wmx.toFixed(1) + ' -' + hr.toFixed(1) +
				'"/>\n';
			if (cell[1]) {
			    line += '<path class="stroke" stroke-width="1" d="M' +
				(x - wmx / 2).toFixed(1) + ' ' +
				(yl - hr).toFixed(1) + 'l' +
				(wmx / 2).toFixed(1) + ' ' + (hr / 2).toFixed(1) +
				'"/>\n';
			    line += '<text class="' + cls + '" style="font-size:' +
				(font.size * .72).toFixed(1) + '" x="' +
				(x - wmx / 3).toFixed(1) + '" y="' +
				y.toFixed(1) + '">' +
				cell[0] + '</text>\n';
			    line += '<text class="' + cls + '" style="font-size:' +
				(font.size * .72).toFixed(1) + '" x="' +
				x.toFixed(1) + '" y="' +
				(y - hr / 3).toFixed(1) + '">' +
				cell[1] + '</text>\n'
			} else {
			    line += '<text class="' + cls + '" style="font-size:' +
				(font.size * .72).toFixed(1) + '" x="' +
				(x - wmx * .2).toFixed(1) + '" y="' +
				(y - hr / 4).toFixed(1) + '">' +
				cell[0] + '</text>\n'
			}
			if (cell.length >= 3) {
			  if (cell[3]) {
			    line += '<path class="stroke" stroke-width="1" d="M' +
				x.toFixed(1) + ' ' +
				(yl - hr / 2).toFixed(1) + 'l' +
				(wmx / 2).toFixed(1) + ' ' + (hr / 2).toFixed(1) +
				'"/>\n';
			    line += '<text class="' + cls + '" style="font-size:' +
				(font.size * .72).toFixed(1) + '" x="' +
				x.toFixed(1) + '" y="' +
				(y + hr / 3).toFixed(1) + '">' +
				cell[2] + '</text>\n';
			    line += '<text class="' + cls + '" style="font-size:' +
				(font.size * .72).toFixed(1) + '" x="' +
				(x + wmx / 3).toFixed(1) + '" y="' +
				y.toFixed(1) + '">' +
				cell[3] + '</text>\n'
			  } else {
			    line += '<text class="' + cls + '" style="font-size:' +
				(font.size * .72).toFixed(1) + '" x="' +
				(x + wmx * .2).toFixed(1) + '" y="' +
				(y + hr / 4).toFixed(1) + '">' +
				cell[2] + '</text>\n'
			  }
			}
		} else {
			line += '<text class="' + cls + '" x="' +
	line += lc
				x.toFixed(1) + '" y="' + y.toFixed(1) + '">' +
				cell[0] + '</text>\n'
		}
		x += wmx
	}

	// show the repeat signs
	y = 1 - hr / 2 + font.size * .3;
	x = x0
	for (i = 0; i < bars.length; i++) {
		bar = bars[i]
		if (bar[0] == ':')
			line += '<text class="' + cls + '" x="' +
				(x - 5).toFixed(1) +
				'" y="' + y.toFixed(1) +
				'" style="font-weight:bold;font-size:' +
			(font.size + 2).toFixed(1) + '">:</text>\n'
		if (i % nc == 0) {
		if (i == 0
		 || (grid.repbrk
		  && (bars[i].slice(-1) == ':' || bars[i][0] == ':'))
		 || k >= nc) {
			y += hr;			// new row
			x = x0
			k = 0
		}
		k++
		if (bar[bar.length - 1] == ':')
		if (bar.slice(-1) == ':')
			line += '<text class="' + cls + '" x="' +
				(x + 5).toFixed(1) +
				'" y="' + y.toFixed(1) +
				'" style="font-weight:bold;font-size:' +
			(font.size + 2).toFixed(1) + '">:</text>\n'
		x += wmx
	}
︙
269
270
271
272
273
274
275
276

277
278
279
280
281
282
283
285
286
287
288
289
290
291

292
293
294
295
296
297
298
299







-
+







	font_cl = this.font_class(font)
	cls = font_cl + " mid";
	this.set_font('grid');		// (for strwh())

	// scan the first voice of the tune
	cur_beat = beat_i = n = wmx = 0;
	bars.push('|')
	for (s = voice_tb[0].sym; s; s = s.next) {
	for (s = tsfirst; s; s = s.ts_next) {
		while (s.time > cur_beat) {
			if (beat_i < 3)		// only 2, 3 or 4 beats / measure...
				beat_i++;
			cur_beat += beat
		}
		switch (s.type) {
		case C.NOTE:
︙
311
312
313
314
315
316
317
318

319


320
321
322
323
324
325
326
327
328
329
330
331
332
333

334
335
336
337
338
339
340
341
342
343
344







-
+

+
+







					break
				chords.push(chord);
				bars.push(bt)
			}
			chord = [];
			cur_beat = s.time;	// synchronize in case of error
			beat_i = 0
			if (bt.indexOf(':'))
			if (bt.indexOf(':') >= 0)
				rep = true	// some repeat
			while (s.ts_next && s.ts_next.type == C.BAR)
				s = s.ts_next
			break
		case C.METER:
			beat = get_beat(s)
			wm = s.wmeasure
			break
		}
	}
︙
338
339
340
341
342
343
344

345
346
347
348
349
350
351
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370







+







	s = {
		type: C.BLOCK,
		subtype: 'ml',
		dur: 0,
		time: 0,
		p_v: p_voice,
		v: p_voice.v,
		st: p_voice.st,
		text: build_grid.call(this, chords, bars, font, wmx)
	}

	if (grid.nomusic) {		// if no music
		this.set_tsfirst(s)
		return
	}
︙
372
373
374
375
376
377
378
379

380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397


398
399
400
401
402
403
404

405
406
407
408
409
410
411
412
413
414
415
416
391
392
393
394
395
396
397

398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424

425
426
427
428
429
430
431
432
433
434
435
436
437







-
+


















+
+






-
+













    output_music: function(of) {
	if (this.cfmt().grid)
		abc2svg.grid.do_grid.call(this);
	of()
    },

    set_fmt: function(of, cmd, parm, lock) {
    set_fmt: function(of, cmd, parm) {
	if (cmd == "grid") {
		if (!parm)
			parm = "1";
		parm = parm.split(/\s+/)
		var grid = {n: Number(parm.shift())}
		if (isNaN(grid.n)) {
			if (parm.length) {
				this.syntax(1, this.errs.bad_val, "%%grid")
				return
			}
			grid.n = 1
		}
		while (parm.length) {
			var item = parm.shift()
			if (item == "norepeat")
				grid.norep = true
			else if (item == "nomusic")
				grid.nomusic = true
			else if (item == "repbrk")
				grid.repbrk = true
			else if (item.slice(0, 8) == "include=")
				grid.ls = item.slice(8).split(',')
		}
		this.cfmt().grid = grid
		return
	}
	of(cmd, parm, lock)
	of(cmd, parm)
    },

    set_hooks: function(abc) {
	abc.output_music = abc2svg.grid.output_music.bind(abc, abc.output_music);
	abc.set_format = abc2svg.grid.set_fmt.bind(abc, abc.set_format)
    }
} // grid

abc2svg.modules.hooks.push(abc2svg.grid.set_hooks);

// the module is loaded
abc2svg.modules.grid.loaded = true
Changes to modules/grid2.js.
︙
8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
23
24
25







26

27
28
29

30
31




32








33
34
35
36
37
38
39
8
9
10
11
12
13
14

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

33
34
35

36
37
38
39
40
41
42

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57







-
+










+
+
+
+
+
+
+
-
+


-
+


+
+
+
+
-
+
+
+
+
+
+
+
+







//	%%grid2 y
// This command must appear in a voice.

abc2svg.grid2 = {

// function called before tune generation
    do_grid: function() {
    var s, v, p_v,
    var s, v, p_v, ix, cs, c_a_cs, bt,
	voice_tb = this.get_voice_tb()

	for (v = 0; v < voice_tb.length; v++) {
		p_v = voice_tb[v]
		if (!p_v.grid2)
			continue
		p_v.clef.invis = true;		// no clef
		p_v.key.k_sf = p_v.key.k_a_acc = 0; // no key signature
		p_v.staffnonote = 2		// draw the staff
		for (s = p_v.sym; s; s = s.next) {
			delete s.a_dd		// no decoration
			if (!s.dur) {
				if (s.bar_type)
					bt = s.time
				continue
			}

			if (s.dur) {		// set all notes
			// set all notes
				s.invis = true;	//  as invisible
				delete s.sl1;	//  with no slur
				s.ti1 = 0	//  and no tie
				delete s.tie_s	//  and no tie
				if (s.tf)	// don't show the tuplets
					s.tf[0] = 1
				if (!s.a_gch) {
					if (s.time == bt)
						s.a_gch = c_a_cs
					continue
			}
				}
				for (ix = 0; ix < s.a_gch.length; ix++) {
					gch = s.a_gch[ix]
					if (gch.type == 'g') {
						c_a_cs = s.a_gch
						break
					}
				}
		}
	}
    }, // do_grid()

    // draw the chord symbol in the middle of the staff
    draw_chosym: function(s) {
    var	ix, gch;
︙
57
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73

74
75
76
77
78
79
80
81
82
83
84
85
86
75
76
77
78
79
80
81

82
83
84
85
86
87
88
89
90

91
92
93
94
95
96
97
98
99
100
101
102
103
104







-
+








-
+













    },

    output_music: function(of) {
	abc2svg.grid2.do_grid.call(this);
	of()
    },

    set_fmt: function(of, cmd, param, lock) {
    set_fmt: function(of, cmd, param) {
	if (cmd == "grid2") {
	    var	curvoice = this.get_curvoice()
		if (curvoice) {
			this.set_v_param("stafflines", "...");	// no staff lines
			curvoice.grid2 = param
		}
		return
	}
	of(cmd, param, lock)
	of(cmd, param)
    },

    set_hooks: function(abc) {
	abc.draw_gchord = abc2svg.grid2.draw_gchord.bind(abc, abc.draw_gchord);
	abc.output_music = abc2svg.grid2.output_music.bind(abc, abc.output_music);
	abc.set_format = abc2svg.grid2.set_fmt.bind(abc, abc.set_format)
    }
} // grid2

abc2svg.modules.hooks.push(abc2svg.grid2.set_hooks);

// the module is loaded
abc2svg.modules.grid2.loaded = true
Changes to modules/page.js.
︙
23
24
25
26
27
28
29
30

31
32
33
34


35
36

37
38

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

54
55
56
57







58

59
60



61
62
63
64
65
66
67
68
69
70
71
72














73
74
75
76
77




78
79
80
81
82
83
84
85
86
87
88
89
90
91
92





93
94
95

96
97
98

99
100
101
102
103
104
105
23
24
25
26
27
28
29

30
31
32


33
34


35


36
37
38
39
40











41




42
43
44
45
46
47
48
49
50


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

103
104
105
106
107
108
109

110
111
112

113
114
115
116
117
118
119
120







-
+


-
-
+
+
-
-
+
-
-
+




-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
+
+
+
+
+
+
+

+
-
-
+
+
+












+
+
+
+
+
+
+
+
+
+
+
+
+
+





+
+
+
+














-
+
+
+
+
+


-
+


-
+







			page.img_out_sav = user.img_out	// keep our reference
		} else {
			user.img_out = cur_img_out	// .. by an other extension
		}
	}
    },

    //function called at end of generation
    // function called at end of generation
    abc_end: function(of) {
    var page = this.page
	if (page.h) {
		abc2svg.page.img_out(page,
	if (page && page.in_page) {
		abc2svg.page.close_page(page)
			page.head + page.style + page.hf + page.out + '</svg>');
		abc2svg.page.img_out(page, '</div>');
		if (user.img_out == page.img_out_sav)
		page.h = 0;
		page.out = ''
			user.img_out = page.user_out
	}
	of()
    }, // abc_end()

    // define the SVG header of the page
    svg_head: function(cfmt) {
	return '<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
 xmlns:xlink="http://www.w3.org/1999/xlink"\n\
 color="black" class="music" stroke-width=".7"\n\
 width="' + cfmt.pagewidth.toFixed(0) +
 'px" height="' + cfmt.pageheight.toFixed(0) + 'px"' +
 (cfmt.bgcolor ? (' style="background-color: ' + cfmt.bgcolor + '"') : '') +
 '>\n'
    }, // svg_head()

    // generate a header or a footer in page.hf and return its height
    // start a new page
    svg_out: function(abc, page) {
    var	h,
	cfmt = abc.cfmt()
    gen_hf: function(page, up, font, str) {
    var	a, i, j, k, x, y, y0, s,
	cfmt = page.abc.cfmt(),
	fh = font.size * 1.1,
	pos = [ '">',
		'" text-anchor="middle">',
		'" text-anchor="end">' ]

	// create the text of a header or a footer
	function header_footer(str) {
	    var	c, i, t,
	function header_footer(o_font, str) {
	    var	c, i, k, t, n_font,
		c_font = o_font,
		nl = 1,
		j = 0,
		r = ["", "", ""]

		if (str[0] == '"')
			str = str.slice(1, -1)
		if (str.indexOf('\t') < 0)		// if no TAB
			str = '\t' + str		// center

		for (i = 0; i < str.length; i++) {
			c = str[i]
			switch (c) {
			case '>':
				r[j] += "&gt;"
				continue
			case '<':
				r[j] += "&lt;"
				continue
			case '&':
				for (k = i + 1; k < i + 8; k++) {
					if (!str[k] || str[k] == '&'
					 || str[k] == ';')
						break
				}
				r[j] += str[k] == ';' ? "&" : "&amp;"
				continue
			case '\t':
				if (j < 2)
					j++		// next column
				continue
			case '\\':			// hope '\n'
				if (c_font != o_font) {
					r[j] += "</tspan>"
//					c_font = o_font
				}
				for (j = 0; j < 3; j++)
					r[j] += '\n';
				nl++;
				j = 0;
				i++
				continue
			default:
				r[j] += c
				continue
			case '$':
				break
			}
			c = str[++i]
			switch (c) {
			case 'd':	// cannot know the modification date of the file
			case 'd':
				if (!abc2svg.get_mtime)
					break // cannot know the change time of the file
				r[j] += strftime(cfmt.dateformat,
						abc2svg.get_mtime(page.abc.parse.fname))
				break
			case 'D':
				r[j] += new Date().toUTCString()
				r[j] += strftime(cfmt.dateformat)
				break
			case 'F':
				r[j] += abc.get_fname()
				r[j] += page.abc.parse.fname
				break
			case 'I':
				c = str[++i]
			case 'T':
				t = abc.info()[c]
				if (t)
					r[j] += t.split('\n', 1)[0]
︙
122
123
124
125
126
127
128








129











130
131



132
133
134
135
136
137
138
139
140
141
142

143
144
145
146
147
148
149
150
151
152
153
154





155
156

157
158

159
160
161
162
163
164
165
166
167
168
169
170










171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192





















193
194
195


196








197



198
199
200












201
202
203

204
205
206
207
208
209
210

211










212
213








214
215
216
217
218
219
220




























221
222
223
224

225
226
227
228
229

230
231

232
233
234
235
236
237
238




239
240
241
242
243
244
245

246
247

248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276


277
278
279
280
281
282
283
284
285
286


287
288
289
290

291
292
293
294
295
296
297
298
299
300


301
302
303
304







305
306
307
308

309
310
311


312
313
314
315
316
317
318
319
320

321

322
323
324
325
326
327
328
329
330
331
332

333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351

352
353
354
355

356
357
358
359
360
361
362
363
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177

178






179





180
181
182
183
184
185

186


187












188
189
190
191
192
193
194
195
196
197






















198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218



219
220
221
222
223
224
225
226
227
228
229

230
231
232
233


234
235
236
237
238
239
240
241
242
243
244
245
246


247



248
249
250
251
252

253
254
255
256
257
258
259
260
261
262
263

264
265
266
267
268
269
270
271
272






273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303

304
305
306
307
308

309
310

311
312
313
314




315
316
317
318







319
320

321

322


















323
324
325
326
327
328



329
330










331
332
333
334
335

336










337
338




339
340
341
342
343
344
345

346
347

348
349


350
351






352
353
354
355

356

357
358
359

360
361
362
363
364

365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383

384
385
386
387

388

389
390
391
392
393
394
395







+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+


+
+
+










-
+
-
-
-
-
-
-

-
-
-
-
-
+
+
+
+
+

-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+

+
+
+
+
+
+
+
+
-
+
+
+

-
-
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
-
-
-




+
-
+
+
+
+
+
+
+
+
+
+

-
+
+
+
+
+
+
+
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+




-
+

-
+



-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+

-
+
-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-






-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
+
+



-
+
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
+
+
+
+
+
+
+
-


-
+

-
-
+
+
-
-
-
-
-
-



+
-
+
-



-





-
+


















-
+



-
+
-







					r[j] += t
					break
				}
				break
			case 'V':
				r[j] += "abc2svg-" + abc2svg.version
				break
			default:
				if (c == '0')
					n_font = o_font
				else if (c >= '1' && c < '9')
					n_font = page.abc.get_font("u" + c)
				else
					break

//fixme: handle the font changes ($<number>) ?
				// handle the font changes
				if (n_font == c_font)
					break
				if (c_font != o_font)
					r[j] += "</tspan>";
				c_font = n_font
				if (c_font == o_font)
					break
				r[j] += '<tspan class="' +
						font_class(n_font) + '">'
				break
			}
		}
		if (c_font != o_font)
			r[j] += "</tspan>";

		r[4] = nl		// number of lines
		return r
	} // header_footer()

	function font_class(font) {
		if (font.class)
			return 'f' + font.fid + cfmt.fullsvg + ' ' + font.class
		return 'f' + font.fid + cfmt.fullsvg
	}

	// generate a header or footer
	// gen_hf
	function gen_hf(up, font, str) {
	    var	a, i, j, k, x, y, y0, s,
		fh = font.size * 1.1,
		pos = [ '">',
			'" text-anchor="middle">',
			'" text-anchor="end">' ]

		if (str[0] == '-') {		// not on 1st page
			if (page.pn == 1)
				return 0
			str = str.slice(1)
		}
	if (str[0] == '-') {		// not on 1st page
		if (page.pn == 1)
			return 0
		str = str.slice(1)
	}

		a = header_footer(str)
	a = header_footer(font, str)
		if (up)
			y0 = page.hbase + fh
	y0 = font.size * .8
		else
			y0 = cfmt.pageheight - page.topmargin - fh * a[4]
		for (i = 0; i < 3; i++) {
			str = a[i]
			if (!str)
				continue
			if (i == 0)
				x = cfmt.leftmargin
			else if (i == 1)
				x = cfmt.pagewidth / 2
			else
				x = cfmt.pagewidth - cfmt.rightmargin;
	for (i = 0; i < 3; i++) {
		str = a[i]
		if (!str)
			continue
		if (i == 0)
			x = cfmt.leftmargin
		else if (i == 1)
			x = cfmt.pagewidth / 2
		else
			x = cfmt.pagewidth - cfmt.rightmargin
			x += page.gutter;
			y = y0;
			k = 0
			while (1) {
				j = str.indexOf('\n', k)
				if (j >= 0)
					s = str.slice(k, j)
				else
					s = str.slice(k)
				if (s)
					page.hf += '<text class="' +
							font_class(font) +
							'" x="' + x.toFixed(1) +
							'" y="' + y.toFixed(1) +
							pos[i] +
							s + '</text>\n'
				if (j < 0)
					break
				k = j + 1;
				y += fh
			}
					
		y = y0
		k = 0
		while (1) {
			j = str.indexOf('\n', k)
			if (j >= 0)
				s = str.slice(k, j)
			else
				s = str.slice(k)
			if (s)
				page.hf += '<text class="' +
						font_class(font) +
						'" x="' + x.toFixed(1) +
						'" y="' + y.toFixed(1) +
						pos[i] +
						s + '</text>\n'
			if (j < 0)
				break
			k = j + 1
			y += fh
		}
	}
		}
		return fh * a[4]
	} // gen_hf()
	return fh * a[4]
    }, // gen_hf()

    // start a new page
    open_page: function(page,
			ht) {	// spacing under the header
    var	h, o_font,
//	font_style,
	abc = page.abc,
	cfmt = abc.cfmt(),
	sty = ""
	// svg_out()

	page.pn++
	page.pna++

	// start a new SVG container for the page
	abc2svg.page.img_out(abc.page, '<div>');
	// start a new page
	if (page.first)
		page.first = false
	else
		sty = "page-break-before:always"
	if (page.gutter)
		sty += (sty ? ";" : "") + "margin-left:" +
			((page.pn & 1) ? page.gutter : -page.gutter).toFixed(1) + "px"
	abc2svg.page.img_out(page, sty ?
			'<div style="' + sty + '">' :
			'<div>')
	page.in_page = true

	page.hbase = page.topmargin;
	page.hmax = cfmt.pageheight - page.topmargin - page.botmargin;
	page.hmax = cfmt.pageheight - page.topmargin - page.botmargin - ht
	page.pn++;
	page.pna++;
	page.gutter = -page.gutter;

	// define the header/footer
	page.hf = ''
	if (page.header) {
		h = abc2svg.page.gen_hf(page, true,
		h = gen_hf(true, abc.get_font("header"), page.header);
					abc.get_font("header"), page.header)
		abc2svg.page.img_out(page,
			'<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	width="' + cfmt.pagewidth.toFixed(0) +
			'px" height="' + (page.topmargin + ht + h).toFixed(0) +
			'px">\n' +
			'<g transform="translate(0,' +
				page.topmargin.toFixed(1) + ')">' +
				page.hf + '</g>\n</svg>')
		page.hmax -= h;
		page.hbase += h
		page.hf = ''
	} else {
		abc2svg.page.img_out(page,
			'<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	width="' + cfmt.pagewidth.toFixed(0) +
			'px" height="' + ht.toFixed(0) +
			'px">\n</svg>')
	}
	if (page.footer)
		page.hmax -= gen_hf(false, abc.get_font("footer"), page.footer);

	page.h = page.hb = 0;
	page.out = ''
    }, // svg_out()
	if (page.footer) {
		page.fh = abc2svg.page.gen_hf(page, false,
					abc.get_font("footer"), page.footer)
		page.hmax -= page.fh
	}

	page.h = 0
    }, // open_page()

    close_page: function(page) {
    var	h,
	cfmt = page.abc.cfmt()

	page.in_page = false
	if (page.footer) {
		h = page.hmax + page.fh - page.h
		abc2svg.page.img_out(page,
			'<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	width="' + cfmt.pagewidth.toFixed(0) +
			'px" height="' + h.toFixed(0) +
			'px">\n<g transform="translate(0,' +
				(h - page.fh).toFixed(1) + ')">' +
			page.hf + '</g>\n</svg>')
	}
	abc2svg.page.img_out(page, '</div>')
	page.h = 0
    }, // close_page()

    // handle the output flow of the abc2svg generator
    img_in: function(p) {
    var h, i,
    var h, ht,
	page = this.page

	// copy a block
	function blkcpy(page) {
	    var b, h, i
	    var b

		if (!page.blk || !page.blk.length) {
		if (!page.blk.length) {
			page.blk = null
			return
		}
		h = page.hb
		for (i = 0; i < page.blk.length; i++) {
			b = page.blk[i]
			page.out += b.p
		page.h = page.hb
		while (page.blk.length) {
			b = page.blk.shift()
			abc2svg.page.img_out(page, b.p)
			    .replace(/<svg(.|\n)*?>/,
				'<g transform="translate(' +
				page.gutter.toFixed(1) + ', ' +
				(h + page.hbase).toFixed(1) +
				')">')
			    .replace('</svg>', '</g>\n');
			h += b.h
			page.h += b.h
		}
		page.blk = null
		page.blk = null			// direct output
		page.h = h
	} // blkcpy()

	// get the style from the image and remove it from the block
	function get_style(p, page) {
	    var	i,
		sty = p.match(/<style.*?>((.|\n)*?)<\/style>/)

		if (!sty)
			return p
		sty = sty[1].split('\n')
		if (!page.style)
			page.style = '<style type="text/css">\n</style>\n'
		for (i = 0; i < sty.length; i++) {
			if (page.style.indexOf(sty[i]) < 0)
				page.style = page.style.replace('</style>\n',
						sty[i] + '\n</style>\n')
		}
		return p.replace(/<style(.|\n)*?\/style>\n/, '')
	} // get_style()

	// img_in()
	switch (p.slice(0, 4)) {
	case "<div":				// new block (tune / paragraph)
		if (p.indexOf('newpage') > 0
		 || (page.oneperpage && this.info().X)
		 || page.h == 0) {
			if (page.h) {
				abc2svg.page.img_out(page,
		 || !page.h)			// empty page
			abc2svg.page.open_page(page, 0)
						page.head + page.style + page.hf +
						page.out + '</svg>');
				abc2svg.page.img_out(page, '</div>')
			}
			abc2svg.page.svg_out(this, page)
			if (!this.cfmt().fullsvg)
				page.style = ''
		}
		page.blk = [];
		page.hb = page.h
		page.blk = []			// in block
		page.hb = page.h		// keep the offset of the start of tune
		break
	case "<svg":				// SVG image
		h = Number(p.match(/height="(\d+)px"/)[1])
		if (h + page.h >= page.hmax) {		// if page overflow
		if (h + page.h >= page.hmax) {	// if page overflow
			if (!page.out)		// if at start of page
				blkcpy(page);	// output the start of tune
			abc2svg.page.img_out(page,
					page.head + page.style + page.hf +
					page.out + '</svg>');
			abc2svg.page.img_out(page, '</div>');
			abc2svg.page.svg_out(this, page);
			page.h = page.hb = this.cfmt().topspace;
			if (page.blk)
				blkcpy(page)	// output the start of tune
			if (page.blk)		// if inside a block
				blkcpy(page)	// output the beginning of the tune
			if (!this.cfmt().fullsvg)
				page.style = ''
		}


			ht = page.blk ? 0 :
				this.cfmt().topspace // tune continuation
//			if (page.in_page)
				abc2svg.page.close_page(page)
			abc2svg.page.open_page(page, ht)
		}
		p = get_style(p, page)

		// if no overflow yet, keep the block
		if (page.blk) {
		if (page.blk)
			page.blk.push({p: p, h: h})
		} else {
			page.out += p
		else
			abc2svg.page.img_out(page, p)
			    .replace(/<svg(.|\n)*?>/,
				'<g transform="translate(' +
				page.gutter.toFixed(1) + ', ' +
				(page.h + page.hbase).toFixed(1) + ')">')
			    .replace('</svg>', '</g>\n')
		}
		page.h += h
		break
	case "</di":				// end of block
		if (page.blk)
		blkcpy(page);
			blkcpy(page)
		page.out += '\n'
		break
//	default:
////fixme: %%beginml cannot be treated (no information about its height)
////		page.out += p
//		break
	}
    }, // img_in()

    // handle the page related parameters
    set_fmt: function(of, cmd, parm, lock) {
    set_fmt: function(of, cmd, parm) {
    var	v,
	cfmt = this.cfmt(),
	page = this.page

	if (cmd == "pageheight") {
		v = this.get_unit(parm)
		if (isNaN(v)) {
			this.syntax(1, errs.bad_val, '%%' + cmd)
			return
		}
		cfmt.pageheight = v

		// if first definition, install the hook
		if (!page && user.img_out && abc2svg.abc_end) {
			this.page = page = {
				abc: this,
				topmargin: 38,	// 1cm
				botmargin: 38,	// 1cm
				gutter: 0,
//				gutter: 0,
				h: 0,		// current page height
				pn: 0,		// page number
				pna: 0,		// absolute page number
				out: '',	// page content
				first: true,	// no skip to next page
				head: abc2svg.page.svg_head(cfmt),
				user_out: user.img_out
			}

			// don't let the backend handle the header/footer
			if (cfmt.header) {
				page.header = cfmt.header;
				cfmt.header = null
︙
377
378
379
380
381
382
383
384

385
386
387


388
389
390
391
392
393
394
409
410
411
412
413
414
415

416
417
418
419
420
421
422
423
424
425
426
427
428







-
+



+
+







				v = this.get_unit(cfmt.topmargin)
				if (!isNaN(v))
					page.topmargin = v
			}
			if (cfmt.gutter) {
				v = this.get_unit(cfmt.gutter)
				if (!isNaN(v))
					page.gutter = (page.pn & 1) ? v : -v
					page.gutter = v
			}
			if (cfmt.oneperpage)
				page.oneperpage = this.get_bool(cfmt.oneperpage)
			if (!cfmt.dateformat)
				cfmt.dateformat = "%b %e, %Y %H:%M"

			// set the hooks
			user.img_out = abc2svg.page.img_in.bind(this);
			page.img_out_sav = user.img_out;
			abc2svg.abc_end = abc2svg.page.abc_end.bind(this,
								abc2svg.abc_end)
		}
︙
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430

431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
449
450
451
452
453
454
455


456
457
458
459
460
461

462








463
464
465
466
467
468
469
470
471
472
473







-
-






-
+
-
-
-
-
-
-
-
-











		case "topmargin":
			v = this.get_unit(parm)
			if (isNaN(v)) {
				this.syntax(1, errs.bad_val, '%%' + cmd)
				return
			}
			page[cmd] = v
			if (cmd == "gutter" && !(page.pn & 1))
				page.gutter = -page.gutter
			return
		case "oneperpage":
			page[cmd] = this.get_bool(parm)
			return
		}
	}
	of(cmd, parm, lock)
	of(cmd, parm)
	if (page) {
		switch (cmd) {
		case "pagewidth":
		case "bgcolor":
			page.head = abc2svg.page.svg_head(cfmt)
			break
		}
	}
    }, // set_fmt()

    set_hooks: function(abc) {
	abc.set_format = abc2svg.page.set_fmt.bind(abc, abc.set_format)
    }
} // page

abc2svg.modules.hooks.push(abc2svg.page.set_hooks);

// the module is loaded
abc2svg.modules.pageheight.loaded = true
Changes to modules/perc.js.
1
2
3
4
5
6
7
8
9
10
11
12


13
14
15
16
17
18

19
20
21
22
23
24
25
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

20
21
22
23
24
25
26
27












+
+





-
+







// perc.js - module to handle %%percmap
//
// Copyright (C) 2018-2019 Jean-Francois Moine - GPL3+
//
// This module is loaded when "%%percmap" appears in a ABC source.
//
// Parameters (from W. Vree)
//	%%percmap ABC_note percussion [note_head]
// The percussion may be a number (MIDI percussion number range 35..81),
// a ABC note or a possibly abbreviated percussion name.
// See https://wim.vree.org/js2/tabDrumDoc.html for more information.

// Using this command creates a voicemap named "MIDIdrum".

abc2svg.perc = {

    // parse %%percmap
    do_perc: function(parm) {
    var	pits = new Int8Array([0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6]),
	accs = new Int8Array([0, 1, 0, -1, 0, 0, 1, 0, -1, 0, -1, 0])
	accs = new Int8Array([3, 1, 3, -1, 3, 3, 1, 3, -1, 3, -1, 3])

// GM drum
// 35 B,,,	Acoustic Bass Drum	a-b-d
// 36 C,,	Bass Drum 1		b-d-1
// 37 ^C,,	Side Stick		s-s
// 38 D,,	Acoustic Snare		a-s
// 39 ^D,,	Hand Clap		h-c
︙
113
114
115
116
117
118
119


















































120
121


122
123
124
125
126
127



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145


146
147

148
149
150
151
152
153
154
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171


172
173
174
175
176



177
178
179


















180
181


182
183
184
185
186
187
188
189







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+



-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+







	"s-g":   73,
	"s-s":   37,
	"s-w":   71,
	"t":     54,
	"v":     58
}

    // convert a ABC note to b40
    function abc_b40(p) {
    var	pit,
	acc = 0,
	i = 0

	switch (p[0]) {
	case '^':
		if (p[++i] == '^') {
			acc = 2
			i++
		} else {
			acc = 1
		}
		break
	case '=':
		i++
		break
	case '_':
		if (p[++i] == '_') {
			acc = -2
			i++
		} else {
			acc = -1
		}
		break
	}
	pit = 'CDEFGABcdefgab'.indexOf(p[i++]) + 16
	if (pit < 16)
		return
	while (p[i] == "'") {
		pit += 7
		i++
	}
	while (p[i] == ",") {
		pit -= 7
		i++
	}
	if (p[i])			// if some extra character
		return
	return abc2svg.pab40(pit, acc)
    } // abc_b40()

    // convert a MIDI pitch to b40
    function mid_b40(pit) {
    var	o = (pit / 12) | 0		// octave
	pit = pit % 12;			// in octave
	return o * 40 + abc2svg.isb40[pit] + 2
    } // mid_b40()

    // convert a drum instrument to a note
    function tonote(p) {
    // convert a drum instrument to b40
    function tob40(p) {
    var	i, j, s,
	pit = Number(p)

	if (isNaN(pit)) {
		s = p.match(/^([_^=]*)([A-Ga-g])([,']*)$/)	// '
		if (s) {				// note name
	if (isNaN(pit)) {		// not a MIDI pitch
		s = abc_b40(p)		// try a ABC note
		if (s)
			i = "CDEFGABcdefgab".indexOf(s[2]) + 16
			switch(s[3]) {
			case "'":
				i += 7 * s[3].length
				break
			case ',':
				i -= 7 * s[3].length
				break
			}
			note = {
				pit: i
			}
			switch (s[1]) {
			case '^': note.acc = 1; break
			case '_': note.acc = -1; break
			}
			return note
		}
			return s


		// drum instrument name
		// try a drum instrument name
		p = p.toLowerCase(p);
		s = p[0];		// get the 1st letters after '-'
		i = 0
		while (1) {
			j = p.indexOf('-', i)
			if (j < 0)
				break
︙
176
177
178
179
180
181
182
183
184
185
186
187



188
189
190
191
192
193
194
195
196
197


198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214


215
216


217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232











233
234
235








236
237

238
239
240
241
242
243




244

245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

261
262
263
264
265
266
267
211
212
213
214
215
216
217





218
219
220





221




222
223
224
















225
226
227

228
229
230

231
232
233
234










235
236
237
238
239
240
241
242
243
244
245



246
247
248
249
250
251
252
253


254






255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275

276
277
278
279
280
281
282
283







-
-
-
-
-
+
+
+
-
-
-
-
-

-
-
-
-
+
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+

-
+
+

-




-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
+
+
+
+

+















-
+







				case 'i':
				case 'o':
					pit = prn[s + p[1]]
					break
				}
				break
			}
		}
	}
	if (!pit)
		return

			if (!pit)
				return
		}
	p = ((pit / 12) | 0) * 7 - 19;	// octave
	pit = pit % 12;			// in octave
	p += pits[pit];
	note = {
		pit: p
	}
	if (accs[pit])
		note.acc = accs[pit]
	return note
    } // tonote()
	return mid_b40(pit)
    } // tob40()

    // normalize a note for mapping
    function norm(p) {
    var	a = p.match(/^([_^]*)([A-Ga-g])([,']*)$/)	// '
	if (!a)
		return
	if (p.match(/[A-Z]/)) {
		p = p.toLowerCase();
		if (p.indexOf("'") > 0)
			p = p.replace("'", '')
		else
			p += ','
	}
	return p
    } // norm()

    var	n, v,
    // do_perc()
    var	vpr, vpl,
	maps = this.get_maps(),
	a = parm.split(/\s+/);
	a = parm.split(/\s+/),
	n = abc_b40(a[1])			// note as b40

	n = norm(a[1])
	if (!n) {
		this.syntax(1, this.errs.bad_val, "%%percmap")
		return
	}
	if (this.cfmt().sound != "play") {		// !play
		if (!a[3])
			return
		if (!maps.MIDIdrum)
			maps.MIDIdrum = {}
		v = tonote(n)
		if (!v) {
			this.syntax(1, this.errs.bad_val, "%%percmap")
			return
		}

	vpr = {					// print
		pit: abc2svg.b40p(n),
		acc: 0
	}

	vpl = tob40(a[2])			// play
	if (!vpl) {
		this.syntax(1, this.errs.bad_val, "%%percmap")
		return
	}
		delete v.acc
		maps.MIDIdrum[n] = [[a[3]], v]
	} else {					// play
	a = a[3] ? [a[3]] : null		// head
	if (!maps.MIDIdrum)
		maps.MIDIdrum = {}
	n = n.toString()
	if (!maps.MIDIdrum[n]) {
		maps.MIDIdrum[n] =
			[a, vpr, null, vpl]	// [heads, print, color, play]
	} else {
		v = tonote(a[2])
		if (!v) {
		if (a)
			this.syntax(1, this.errs.bad_val, "%%percmap")
			return
		}
		if (!maps.MIDIdrum)
			maps.MIDIdrum = {}
		maps.MIDIdrum[n] = [null, v]
			maps.MIDIdrum[n][0] = a
		if (!maps.MIDIdrum[n][1])
			maps.MIDIdrum[n][1] = vpr
		maps.MIDIdrum[n][3] = vpl
	}

	this.set_v_param("perc", "MIDIdrum")
    }, // do_perc()

    // set the MIDI parameters in the current voice
    set_perc: function(a) {
    var	i, item,
	curvoice = this.get_curvoice()

	for (i = 0; i < a.length; i++) {
		switch (a[i]) {
		case "perc=":				// %%percmap
			if (!curvoice.map)
				curvoice.map = {}
			curvoice.map = a[i + 1];
			if (!curvoice.midictl)
				curvoice.midictl = {}
				curvoice.midictl = []
			curvoice.midictl[0] = 1		// bank 128
			break
		}
	}
    }, // set_perc()

    do_pscom: function(of, text) {
︙
Changes to modules/psvg.js.
︙
543
544
545
546
547
548
549
550

551
552
553
554
555
556
557
543
544
545
546
547
548
549

550
551
552
553
554
555
556
557







-
+







	svgbuf = ''
}
Psvg.prototype.ps_flush = ps_flush

// evaluate a PS user sequence (%beginps .. %%endps)
Psvg.prototype.ps_eval = function(txt) {
	wps.parse(txt);
	ps_flush()
	ps_flush(true)
}

// ------ output builtin decorations
// common part
function pscall(f, x, y, script) {
	gcur.xorig = gcur.xoffs = abcobj.sx(0);
	gcur.yorig = gcur.yoffs = abcobj.sy(0);
︙
Changes to modules/soloffs.js.
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19











-
+







// soloffs.js - module to set the X offset of some elements at start of music line
//
// Copyright (C) 2018-2019 Jean-Francois Moine - GPL3+
//
// Parameters
//	%%soloffs <type>=<offset> [<type>=<offset>]*
//		<type> is one of 'part', 'tempo' or 'space'
//		<offset> is the X offset from the start of staff

abc2svg.soloffs = {

    set_fmt: function(of, cmd, parm, lock) {
    set_fmt: function(of, cmd, parm) {
	if (cmd == "soloffs") {
	    var	i, v,
		C = abc2svg.C,
		soloffs = this.cfmt().soloffs = {}

		parm = parm.split(/\s+/)
		while (parm.length) {
︙
37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51







-
+







//			default:
//				// error
//				break
			}
		}
		return
	}
	of(cmd, parm, lock)
	of(cmd, parm)
    },

    set_sym_glue: function(of, width) {
    var	s,
	C = abc2svg.C,
	tsfirst = this.get_tsfirst(),
	soloffs = this.cfmt().soloffs;
︙
Changes to modules/sth.js.
︙
96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112

113
114
115
116
117
118
119
120
121
122
123

124
125
126
127
128
129
130
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110
111

112
113
114
115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
130







-
+








-
+










-
+







	if (curvoice.sth && s && s.type == C.NOTE) {
		s.sth = curvoice.sth;
		curvoice.sth = null	// some stem widths in this voice
	}
	return s
    },

    set_format: function(of, cmd, param, lock) {
    set_fmt: function(of, cmd, param) {
	if (cmd == "sth") {
	    var	curvoice = this.get_curvoice()
		if (this.parse.state == 2)
			this.goto_tune()
		if (curvoice)
			curvoice.sth = param.split(/[ \t;-]+/)
		return
	}
	of(cmd, param, lock)
	of(cmd, param)
    },

    set_stems: function(of) {
	of();
	abc2svg.sth.set_sth.call(this)
    },

    set_hooks: function(abc) {
	abc.calculate_beam = abc2svg.sth.calculate_beam.bind(abc, abc.calculate_beam);
	abc.new_note = abc2svg.sth.new_note.bind(abc, abc.new_note);
	abc.set_format = abc2svg.sth.set_format.bind(abc, abc.set_format);
	abc.set_format = abc2svg.sth.set_fmt.bind(abc, abc.set_format);
	abc.set_stems = abc2svg.sth.set_stems.bind(abc, abc.set_stems)
    }
} // sth

abc2svg.modules.hooks.push(abc2svg.sth.set_hooks);

// the module is loaded
︙
Added modules/strftime.js.









































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/* Port of strftime() by T. H. Doan (https://thdoan.github.io/strftime/)
 *
 * Day of year (%j) code based on Joe Orost's answer:
 * http://stackoverflow.com/questions/8619879/javascript-calculate-the-day-of-the-year-1-366
 *
 * Week number (%V) code based on Taco van den Broek's prototype:
 * http://techblog.procurios.nl/k/news/view/33796/14863/calculate-iso-8601-week-and-year-in-javascript.html
 */
function strftime(sFormat, date) {
  if (!(date instanceof Date)) date = new Date();
  var nDay = date.getDay(),
    nDate = date.getDate(),
    nMonth = date.getMonth(),
    nYear = date.getFullYear(),
    nHour = date.getHours(),
    aDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
    aMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
    aDayCount = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
    isLeapYear = function() {
      return (nYear%4===0 && nYear%100!==0) || nYear%400===0;
    },
    getThursday = function() {
      var target = new Date(date);
      target.setDate(nDate - ((nDay+6)%7) + 3);
      return target;
    },
    zeroPad = function(nNum, nPad) {
      return ((Math.pow(10, nPad) + nNum) + '').slice(1);
    };
  return sFormat.replace(/%[a-z]/gi, function(sMatch) {
    return (({
      '%a': aDays[nDay].slice(0,3),
      '%A': aDays[nDay],
      '%b': aMonths[nMonth].slice(0,3),
      '%B': aMonths[nMonth],
      '%c': date.toUTCString(),
      '%C': Math.floor(nYear/100),
      '%d': zeroPad(nDate, 2),
      '%e': nDate,
      '%F': date.toISOString().slice(0,10),
      '%G': getThursday().getFullYear(),
      '%g': (getThursday().getFullYear() + '').slice(2),
      '%H': zeroPad(nHour, 2),
      '%I': zeroPad((nHour+11)%12 + 1, 2),
      '%j': zeroPad(aDayCount[nMonth] + nDate + ((nMonth>1 && isLeapYear()) ? 1 : 0), 3),
      '%k': nHour,
      '%l': (nHour+11)%12 + 1,
      '%m': zeroPad(nMonth + 1, 2),
      '%n': nMonth + 1,
      '%M': zeroPad(date.getMinutes(), 2),
      '%p': (nHour<12) ? 'AM' : 'PM',
      '%P': (nHour<12) ? 'am' : 'pm',
      '%s': Math.round(date.getTime()/1000),
      '%S': zeroPad(date.getSeconds(), 2),
      '%u': nDay || 7,
      '%V': (function() {
              var target = getThursday(),
                n1stThu = target.valueOf();
              target.setMonth(0, 1);
              var nJan1 = target.getDay();
              if (nJan1!==4) target.setMonth(0, 1 + ((4-nJan1)+7)%7);
              return zeroPad(1 + Math.ceil((n1stThu-target)/604800000), 2);
            })(),
      '%w': nDay,
      '%x': date.toLocaleDateString(),
      '%X': date.toLocaleTimeString(),
      '%y': (nYear + '').slice(2),
      '%Y': nYear,
      '%z': date.toTimeString().replace(/.+GMT([+-]\d+).+/, '$1'),
      '%Z': date.toTimeString().replace(/.+\((.+?)\)$/, '$1')
    }[sMatch] || '') + '') || sMatch;
  });
}
Changes to modules/temper.js.
︙
36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

62
63
64
65
66
67
68
69
70
71
72
73
74
36
37
38
39
40
41
42

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68
69
70
71
72
73
74







-
+

















-
+













		v0.temper = new Float32Array(12)
		for (var i = 0; i < 12; i++)
			v0.temper[i] = this.cfmt().temper[i] / 100
	}
    },

    // get the temperament
    set_fmt: function(of, cmd, param, lock) {
    set_fmt: function(of, cmd, param) {
	if (cmd == "temperament") {
	    var	ls = new Float32Array(param.split(/ +/)),
		i = ls.length

		if (i == 12) {
			while (--i >= 0) {
				if (isNaN(parseInt(ls[i])))
					break
			}
			if (i < 0) {
				this.cfmt().temper = ls
				return
			}
		}
		this.syntax(1, this.errs.bad_val, "%%temperament")
		return
	}
	of(cmd, param, lock)
	of(cmd, param)
    },

    set_hooks: function(abc) {
	abc.set_bar_num = abc2svg.temper.set_bar_num.bind(abc, abc.set_bar_num);
	abc.set_format = abc2svg.temper.set_fmt.bind(abc, abc.set_format)
    }
} // temper


abc2svg.modules.hooks.push(abc2svg.temper.set_hooks);

// the module is loaded
abc2svg.modules.temperament.loaded = true
Changes to package.json.
1
2
3

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

19


20
21
22

23
24
25
26
27
28
29
1
2

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33


-
+















+

+
+



+







{
  "name": "abc2svg",
  "version": "1.19.1",
  "version": "1.20.7",
  "description": "ABC music notation translator",
  "devDependencies": {
    "uglify-js": "^2.5.0"
  },
  "files": [
    "COPYING",
    "COPYING.LESSER",
    "MIDI-1.js",
    "Scc1t2/",
    "abc2odt",
    "abc2svg",
    "abc2svg-1.js",
    "abc2svg.ttf",
    "abcdoc-1.js",
    "abcemb-1.js",
    "abcemb1-1.js",
    "abcemb2-1.js",
    "abctopdf",
    "abcweb2-1.js",
    "ambitus-1.js",
    "break-1.js",
    "capo-1.js",
    "clair-1.js",
    "clip-1.js",
    "cmdline.js",
    "combine-1.js",
    "diag-1.js",
    "edit-1.css",
    "edit-1.js",
    "edit-1.xhtml",
︙
43
44
45
46
47
48
49
50

51

52
53
54
55

56

57
58
59
60
61
62
63
64


65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81





47
48
49
50
51
52
53

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87


88
89
90
91
92







-
+

+




+

+







-
+
+















-
-
+
+
+
+
+
    "grid2-1.js",
    "json-1.js",
    "page-1.js",
    "perc-1.js",
    "play-1.js",
    "psvg-1.js",
    "setmidi-1.js",
    "sth-1.js",
    "snd-1.js",
    "soloffs-1.js",
    "sth-1.js",
    "temper-1.js",
    "toabc.js",
    "toaudio-1.js",
    "toabw.js",
    "tomei.js",
    "toodt.js",
    "toparam.js",
    "toxhtml.js"
  ],
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "bin": {
    "abc2odt": "./abc2odt",
    "abc2svg": "./abc2svg"
    "abc2svg": "./abc2svg",
    "abctopdf": "./abctopdf"
  },
  "repository": {
    "type": "fossil",
    "url": "https://chiselapp.com/user/moinejf/repository/abc2svg"
  },
  "keywords": [
    "ABC",
    "music",
    "SVG"
  ],
  "author": "Jean-François Moine",
  "license": "LGPL-3.0",
  "bugs": {
    "url": "https://chiselapp.com/user/moinejf/repository/abc2svg/reportlist"
  },
  "homepage": "https://chiselapp.com/user/moinejf/repository/abc2svg"
}
  "homepage": "https://chiselapp.com/user/moinejf/repository/abc2svg",
  "dependencies": {
    "npm": "^6.10.1"
  }
}
Changes to toabc.js.
︙
43
44
45
46
47
48
49

50
51
52
53
54
55
56
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57







+







		uppermordent: 'P',
		segno: 'S',
		trill: 'T',
		upbow: 'u',
		downbow: 'v',
		roll: '~'
	},
	old_font = [],
	mode_tb = [			// key modes
		[0, ""],
		[2, "dor"],
		[4, "phr"],
		[-1, "lyd"],
		[1, "mix"],
		[3, "m"],
︙
80
81
82
83
84
85
86
87

88


89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106




107
108
109
110
111
112
113
114




115
116
117
118
119
120
121
122
123
124
125
126

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142

143
144
145
146
147
148
149
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95
96
97
98
99
100
101

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135

136





137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155







-
+

+
+










-







+
+
+
+








+
+
+
+











-
+
-
-
-
-
-











+







			ln = 'V:' + p_voice.id

			if (!vold[v]) {
				vold[v] = true
				if (p_voice.clef
				 && p_voice.clef.clef_type != 'a')
					ln += ' ' + clef_dump(p_voice.clef)
				if (p_voice.nm)
				if (p_voice.nm) {
					ln += ' nm="' + p_voice.nm + '"'
					font_def("voice", p_voice.nm)
				}
				if (p_voice.snm)
					ln += ' snm="' + p_voice.snm + '"';
				if (p_voice.scale != 1)
					ln += ' scale=' + p_voice.scale
				if (p_voice.uscale)
					ln += ' microscale=' + p_voice.uscale
			}
			abc2svg.print(ln)
		} // vi_out()

		eoln = false
		if (nv == 1) {
			if (vo[0].length == 0)
				return
			if (!vold[0]
			 && (voice_tb[0].nm || voice_tb[0].snm
			  || voice_tb[0].scale != 1 || voice_tb[0].uscale))
				vi_out(0);
			if (eoln) {
				eoln = false
				vo[0][vo[0].length - 1] += "$"
			}
			abc2svg.print(vo[0].join(''));
			vo[0] = []
			return
		}
		for (var v = 0; v < nv; v++) {
			if (vo[v].length == 0)
				continue
			vi_out(v);
			if (eoln) {
				eoln = false
				vo[v][vo[v].length - 1] += "$"
			}
			abc2svg.print(vo[v].join(''));
			vo[v] = []
		}
	} // voice_out()

	function block_dump(s) {
	    var	ln = "%%" + s.subtype + ' '
		switch (s.subtype) {
		case "ml":
			ln += s.text
			break
		case "leftmargin":
		default:
		case "rightmargin":
		case "pagescale":
		case "pagewidth":
		case "scale":
		case "staffwidth":
			ln += s.param
			break
		case "sep":
			ln += s.sk1.toFixed(1) + ' ' +
				s.l.toFixed(1) + ' ' +
				s.sk2.toFixed(1)
			break
		case "skip":
			ln += s.sk
			break
		case "text":
			font_def("text", s.text)
			if (s.text.indexOf('\n') <= 0
			 && (!s.opt || s.opt == 'c')) {
				if (s.opt == 'c')
					ln = "%%center " + s.text
				else
					ln += s.text
				break
︙
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
164
165
166
167
168
169
170



171
172
173
174
175
176
177







-
-
-







			abc2svg.print(ln +
				'\n%%' + s.text.replace(/\n/g, '\n%%') +
				"\n%%endtext")
			return
		case "title":
			abc2svg.print('T:' + s.text.replace(/\n/g, '\nT:'))
			return
		default:
			ln += "(unknown)"
			break
		}
		abc2svg.print(ln)
	} // block_dump()

	function clef_dump(s) {
	    var	ln
		switch (s.clef_type) {
︙
216
217
218
219
220
221
222
223













































224
225
226
227
228


229
230
231
232
233
234
235
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274

275
276
277
278
279
280
281
282
283
284








+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-

+
+







break
}
			d++
		}
		if (d > 0)
			line += "//////".slice(0, d)
	} // dur_dump()

	function font_def(fn, p) {
	    var	c, f,
		i = p.indexOf('$')

		abc.get_font(fn)		// used font
// fixme: one '$' only
		if (i >= 0) {
			c = p[i + 1]
			if (c >= '1' && c <= '9') {
				c = "u" + c
				f = abc.cfmt()[c + "font"]	// user font
				f.fid = abc.get_font(c).fid	// dump it!
			}
		}
		font_dump()		// dump the new fonts
	} // font_def()

	function font_dump() {
	    var	k, f, def,
		cfmt = abc.cfmt()
//		fs = abc.get_font_style().split("\n").shift()

		for (k in cfmt) {
			if (k.slice(-4) != "font")
				continue
			f = cfmt[k]
			if (f.fid == undefined)
				continue	// not used
			if (old_font[f.fid])
				continue	// already out
			old_font[f.fid] = true
			def = f.name || ""
			if (f.weight)
				def += f.weight
			if (f.style)
				def += f.style
			if (!def)
				def = "*"
			if (k[0] == "u")
				k = "setfont-" + k[1]
			abc2svg.print('%%' + k + ' ' +
				def + ' ' + (f.size || "*"))
		}
	} // font_dump()

	function gch_dump(a_gch) {
	    var i, j, gch
		for (i = 0; i < a_gch.length; i++) {
			line += '"';
			gch = a_gch[i]
			font_def(gch.type == 'g' ? "gchord" : "annotation", gch.text)
			line += '"';
			switch (gch.type) {
			case 'g':
				for (j = 0; j < gch.text.length; j++) {
					switch (gch.text.charCodeAt(j)) {
					case 0x266d:
						line += 'b'
						break
︙
299
300
301
302
303
304
305
306
307



308
309
310
311
312
313
314
315
316
317
318
319
320
321

322
323
324

325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
348
349
350
351
352
353
354


355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370

371



372



















373
374
375
376
377
378
379







-
-
+
+
+













-
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







		if (clef && clef.clef_type != 'a')
			ln += ' ' + clef_dump(clef)
//fixme: to continue
		return ln
	} // key_dump()

	function lyric_dump() {
	    var	v, s, i, ly, nly, t, w,
		nb = 0
	    var	v, s, i, ly, nly, t, w

		font_def("vocal", "")

		for (v = 0; v < nv; v++) {
			nly = 0;
			w = []
			for (s = voice_tb[v].sym; s; s = s.next) {
				ly = s.a_ly
				if (ly)
					while (nly < ly.length)
						w[nly++] = ""
			}
			if (nly == 0)
				continue
			for (s = voice_tb[v].sym; s; s = s.next) {
				switch (s.type) {
				if (s.type != C.NOTE)
				case C.BAR:
					if (w.length == 0)
						continue
					continue
					if (++nb < 4)
						continue
					nb = 0
					if (!s.next)
						continue
					for (i = 0; i < nly; i++)
						w[i] += '\n'
//					t =  "|"
//					if (++nb >= 4) {
//						nb = 0;
//						t = "|\n"
//					}
//					for (i = 0; i < nly; i++)
//						w[i] += t
				default:
					continue
				case C.NOTE:
					break
				}
				ly = s.a_ly
				if (!ly) {
					for (i = 0; i < nly; i++)
						w[i] += "*"
					continue
				}
				for (i = 0; i < nly; i++) {
︙
362
363
364
365
366
367
368


369

370
371
372
373
374
375
376
377
378

379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401












402
403
404
405
406
407
408
391
392
393
394
395
396
397
398
399

400
401
402
403
404
405
406
407
408

409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429



430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448







+
+
-
+








-
+




















-
-
-
+
+
+
+
+
+
+
+
+
+
+
+







					case '_\n':
						w[i] += '_'
						break
					default:
						t = t.replace(/ /g, '~')
						t = t.replace(/-/g, '\\-')
						t = t.replace(/\n/g, '-');
						if (t.slice(-1) != "-")
							t += ' '
						w[i] += t + ' '
						w[i] += t
						break
					}
				}
			}
			if (w.length != 0) {
				if (voice_tb.length > 1)
					abc2svg.print("V:" + voice_tb[v].id)
				for (i = 0; i < w.length; i++)
					abc2svg.print("w:" + w[i].replace(/\n/g, '\n+:'))
					abc2svg.print("w:" + w[i].replace(/\*+$/,""))
			}
		}
	} // lyric_dump()

	function meter_dump(s) {
	    var	i, ln
		if (s.wmeasure == 1)
			return "M:none"
		ln = "M:"
		for (i = 0; i < s.a_meter.length; i++) {
			if (i != 0)
				ln += ' ';
			ln += s.a_meter[i].top
			if (s.a_meter[i].bot)
				ln += '/' + s.a_meter[i].bot
		}
		return ln
	} // meter_dump()

	function note_dump(s, note, tie_ch) {
	    var	p, j
		if (note.sl1)
			slti_dump(note.sl1, '(')
	    var	p, j, sl, s2
		if (note.sls) {
			for (j = 0; j < note.sls.length; j++) {
				sl = note.sls[j];
				slti_dump(sl.ty, '(');
				s2 = sl.note.s
				if (s2.sl2)
					s2.sl2++
				else
					s2.sl2 = 1
			}
		}
		if (note.a_dcn) {
			for (j = 0; j < note.a_dcn.length; j++)
				line += '!' + note.a_dcn[j] + '!'
		}
		if (note.color)
			line += "!" + note.color + "!"
		switch (note.acc) {
︙
436
437
438
439
440
441
442
443
444
445
446






447
448
449
450
451
452
453
476
477
478
479
480
481
482




483
484
485
486
487
488
489
490
491
492
493
494
495







-
-
-
-
+
+
+
+
+
+







				if (p < 16) {
					line += ","
					if (p < 9)
						line += ","
				}
			}
		}
		if (!tie_ch && note.ti1)
			slti_dump(note.ti1, '-')
		if (note.sl2)
			line += ')'
		if (!tie_ch && note.tie_ty)
			slti_dump(note.tie_ty, '-')
		while (note.sl2) {
			line += ')';
			note.sl2--
		}
	} // note_dump()

	function slti_dump(fl, ty) {
		if (fl & C.SL_DOTTED)
			line += ".";
		line += ty
		switch (fl & 0x07) {
︙
468
469
470
471
472
473
474
475

476
477
478
479
480
481
482
510
511
512
513
514
515
516

517
518
519
520
521
522
523
524







-
+







		st = -1,
		sy = s.sy;

		curv = -1;
		ln = "%%score "
		for (v = 0; v < sy.voices.length; v++) {
			p_v = sy.voices[v]
			if (p_v.range >= 0)
			if (p_v)
				vn[p_v.range] = v
		}
		for (v = 0; v < sy.voices.length; v++) {
			if (vn[v] == undefined || vn[v] < 0)
				continue
			p_v = sy.voices[vn[v]]
			if (p_v.st != st) {
︙
587
588
589
590
591
592
593
594

595
596
597
598
599
600
601
602
603
604

605
606
607
608
609
610
611
612
613

614
615

616
617

618
619
620
621

622
623
624
625
626





627
628
629

630
631
632
633
634

635
636
637
638
639
640
641
642
643
644

645


646

647
648
649













650
651
652
653
654
655
656
629
630
631
632
633
634
635

636










637









638


639


640




641





642
643
644
645
646
647
648

649
650
651
652
653

654
655
656
657
658
659
660
661
662
663

664
665
666
667

668



669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688







-
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
+
-
-
+
-
-
+
-
-
-
-
+
-
-
-
-
-
+
+
+
+
+


-
+




-
+









-
+

+
+
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+







		}
		if (s.tempo_str2)
			ln += '"' + s.tempo_str2 + '"'
		info_out(ln)
	} // tempo_dump()

	function tuplet_dump(s) {
	    var	s2, r
	    var	tp
		if (s.tp0) {
			line += '(' + s.tp0;
			r = 1
			for (s2 = s.next; s2; s2 = s2.next) {
				if (!s2.dur)
					continue
				r++
				if (s2.te0)
					break
			}

			if (r == s.tp0
			 && ((s.tp0 == 2 && s.tq0 == 3)
			  || (s.tp0 == 3 && s.tq0 == 2)
			  || (s.tp0 == 4 && s.tq0 == 3)))
				;
			else
				line += ':' + s.tq0 + ':' + r
		}
		if (s.tp1) {
		while (1) {
			line += '(' + s.tp1;
			r = 1
			tp = s.tp.shift()
			for (s2 = s.next; s2; s2 = s2.next) {
				if (!s2.dur)
			if (!tp)
					continue
				r++
				if (s2.te1)
					break
				break
			}
			if (r == s.tp1
			 && ((s.tp1 == 2 && s.tq1 == 3)
			  || (s.tp1 == 3 && s.tq1 == 2)
			  || (s.tp1 == 4 && s.tq1 == 3)))
			line += '(' + tp.p
			if (tp.ro == tp.p
			 && ((tp.p == 2 && tp.q == 3)
			  || (tp.p == 3 && tp.q == 2)
			  || (tp.p == 4 && tp.q == 3)))
				;
			else
				line += ':' + s.tq1 + ':' + r
				line += ':' + tp.q + ':' + tp.ro
		}
	} // tuplet_dump()

	function sym_dump(s) {
	    var	tie_ch
	    var	tie_ch, i, sl

		if (s.repeat_n) {
			if (s.repeat_n < 0)
				line += "[I:repeat " + (-s.repeat_n).toString() +
					' ' + s.repeat_k + ']'
			else
				line += "[I:repeat " + s.repeat_n +
					' ' + s.repeat_k + ']'
		}
		if (s.tp0 || s.tp1)
		if (s.tp)
			tuplet_dump(s)
		if (s.sls) {
			for (i = 0; i < s.sls.length; i++) {
		tmp = s.slur_start
				sl = s.sls[i];
		while (tmp) {
			slti_dump(tmp, '(');
			tmp >>= 4
				slti_dump(sl.ty, '(');
				if (sl.is_note) {
					if (sl.note.sl2)
						sl.note.sl2++
					else
						sl.note.sl2 = 1
				} else {
					if (sl.note.s.sl2)
						sl.note.s.sl2++
					else
						sl.note.s.sl2 = 1
				}
			}
		}
		if (s.a_gch)
			gch_dump(s.a_gch)
		if (s.a_dd)
			deco_dump(s.a_dd)
		if (s.invis && s.type != C.REST && s.type != C.SPACE)
			line += "!invisible!"
︙
695
696
697
698
699
700
701













702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722

723
724
725
726
727
728
729
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766

767
768
769
770
771
772
773
774







+
+
+
+
+
+
+
+
+
+
+
+
+




















-
+







			break
		case C.MREST:
			line += s.invis ? 'X' : 'Z'
			if (s.nmes != 1)
				line += s.nmes
			break
		case C.NOTE:
			if (s.stem) {			// if forced stem direction
				if (s.stem > 0) {
					if (s.p_v.pos.stm != C.SL_ABOVE) {
						s.p_v.pos.stm = C.SL_ABOVE
						line += "[I:pos stem up]"
					}
				} else {
					if (s.p_v.pos.stm != C.SL_BELOW) {
						s.p_v.pos.stm = C.SL_BELOW
						line += "[I:pos stem down]"
					}
				}
			}
			if (s.beam_br1)
				line += "!beambr1!"
			if (s.beam_br2)
				line += "!beambr2!"
			if (s.trem1)
				line += '!' + "///".slice(0, s.ntrem) + '!'
			if (s.xstem)
				line += "!xstem!"
			if (s.stemless
			 && s.notes[0].dur < C.BLEN)	// head duration
				line += "!stemless!"
			if (s.trem22)
				line += "!trem" + s.ntrem + "!"
			else if (s.trem2 && s.next && s.next.trem2)
				s.next.trem22 = true
			tie_ch = true
			if (s.notes.length == 1) {
				note_dump(s, s.notes[0], tie_ch)
			} else {
				for (i = 0; i < s.notes.length; i++) {
					if (!s.notes[i].ti1) {
					if (!s.notes[i].tie_n) {
						tie_ch = false
						break
					}
				}
				line += '['
				for (i = 0; i < s.notes.length; i++)
					note_dump(s, s.notes[i], tie_ch);
︙
740
741
742
743
744
745
746
747
748
749



750
751
752
753
754
755

756
757
758
759
760
761
762
785
786
787
788
789
790
791



792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808







-
-
-
+
+
+






+







				dur_dump(tmp, true)
			} else {
				tmp = s.notes[0].dur	// head duration
				if (s.trem2)
					tmp /= 2;
				dur_dump(tmp)
			}
			if (s.ti1 && tie_ch)
				slti_dump(s.notes[0].ti1, '-');
			tmp = s.slur_end
			if (s.tie_s && tie_ch)
				slti_dump(s.notes[0].tie_ty, '-');
			tmp = s.sl2
			while (tmp) {
				line += ')';
				tmp--
			}
			break
		case C.PART:
			font_def("parts", s.text)
			info_out('P:' + s.text)
			break
		case C.REST:
			line += s.invis ? 'x' : 'z';
			dur_dump(s.dur)
			break
		case C.SPACE:
︙
786
787
788
789
790
791
792


793
794
795
796
797
798
799
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847







+
+







			break
		default:
			voice_out();
			abc2svg.print('% ??sym: ' + music_types[s.type])
			break
		}
	} // sym_dump()

	font_dump()

	abc2svg.print('\nX:' + info['X'])
	header_dump("TC")

	abc2svg.print(meter_dump(voice_tb[0].meter));

	ulen = voice_tb[0].ulen < 0 ? C.BLEN / 4 : voice_tb[0].ulen;
︙
823
824
825
826
827
828
829

830
831



832
833
834
835
836

837
838
839



840
841

842
843
844
845
846
847
848
849
850
851
852
853

854
855


856
857
858
859
860
861
862
863
864
865
866
867
868
871
872
873
874
875
876
877
878


879
880
881
882
883
884
885
886
887



888
889
890


891
892
893
894
895
896
897
898
899
900
901
902

903
904

905
906
907
908
909
910
911
912
913
914
915
916
917
918
919







+
-
-
+
+
+





+
-
-
-
+
+
+
-
-
+











-
+

-
+
+













			voice_out();
		if (s.del)
			continue
		line = "";
		// (all voices are synchronized on %%score)
		if (s.type != C.STAVES && s.time > vti[s.v]) {
//fixme: put 'X' if more than one measure
			if (s.time > vti[s.v] + 2) {
			line += 'x';
			dur_dump(s.time - vti[s.v]);
				line += 'x';
				dur_dump(s.time - vti[s.v]);
			}
			vti[s.v] = s.time
		}
		sym_dump(s)
		if (s.dur)
			vti[s.v] = s.time + s.dur
		if (s.next) {
		if (s.beam_end)
			line += ' '
		if (s.eoln && s.next) {
			if (s.beam_end && !s.beam_st && !s.next.beam_end)
				line += ' '
			if (s.eoln)
			line += '$';
			eoln = true
				eoln = true
		}
		if (line)
			vo[s.v].push(line)
	}
	voice_out();
	lyric_dump();
	header_dump("W")
} // abc_dump()
user.get_abcmodel = abc_dump

// -- local functions
abc2svg.abc_init = function() {
abc2svg.abc_init = function(args) {
	abc2svg.print('%abc-2.2\n\
% generated by abc2svg toabc\n\
% generated by abc2svg toabc from\n\
%\t' + args.join(' ') + '\n\
%%linebreak $')
}

abc2svg.abc_end = function() {
	if (user.errtxt)
		abc2svg.print("Errors:\n" + user.errtxt)
}

abc2svg.abort = function(e) {
	abc2svg.print(e.message + "\n*** Abort ***\n" + e.stack);
	abc2svg.abc_end();
	abc2svg.quit()
}
Changes to toabw.js.
︙
79
80
81
82
83
84
85


86


87
88
89
90
91
92

93
94
95
96
97
98
99
79
80
81
82
83
84
85
86
87

88
89
90
91
92
93
94

95
96
97
98
99
100
101
102







+
+
-
+
+





-
+







			r[j] += c
			continue
		case '$':
			break
		}
		c = str[++i]
		switch (c) {
		case 'd':
			if (!abc2svg.get_mtime)
		case 'd':	// cannot know the modification date of the file
				break // cannot know the modification date of the file
			r[j] += abc2svg.get_mtime(abc.parse.fname)
			break
		case 'D':
			r[j] += (new Date()).toUTCString()
			break
		case 'F':
			r[j] += abc.get_fname()
			r[j] += abc.parse.fname
			break
		case 'I':
			c = str[++i]
		case 'T':
			t = abc.info()[c]
			if (t)
				r[j] += t.split('\n', 1)[0]
︙
130
131
132
133
134
135
136
137

138
139
140
141
142
143
144
133
134
135
136
137
138
139

140
141
142
143
144
145
146
147







-
+







	res = '<section id="'+ (type[0] == 'h' ? '0' : '1') +
		'" listid="0" parentid="0" type="' + type +
		'" xid="' + (++seq).toString() + '">\n\
<table xid="' + (++seq).toString() + '"\
 props="list-tag:1; table-column-props:6.00cm/6.00cm/6.00cm/;\
table-column-leftpos:' + (page_type[0] == 'L' ? '0.6in' : '1.5cm') + '">\n';

	a = header_footer(str)
	a = header_footer(clean_txt(str))
	for (i = 0; i < 3; i++) {
		res += '<cell xid="' + (++seq).toString() + '"\
 props="left-attach:' + i + '; right-attach:' + (i + 1).toString() +
			'; bot-attach:1; top-attach:0;\
 top-color:ffffff; right-color:ffffff; bg-style:1;\
 left-color:ffffff; bot-color:ffffff">\n';
		str = a[i]
︙
222
223
224
225
226
227
228

229
230
231
232
233
234
235
236
237
238
225
226
227
228
229
230
231
232
233


234
235
236
237
238
239
240







+

-
-








// replace <>& by XML character references
function clean_txt(txt) {
	return txt.replace(/<|>|&.*?;|&/g, function(c) {
		switch (c) {
		case '<': return "&lt;"
		case '>': return "&gt;"
		case '&': return "&amp;"
		}
		if (c == '&')
			return "&amp;"
		return c
	})
}

abc2svg.abort = function(e) {
	abc.blk_flush();
	if (typeof abc2svg.printErr == 'function')
︙
Added tomei.js.

































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// abc2svg - tomei.js - convert ABC to MEI
//
// Copyright (C) 2019 Jean-Francois Moine
//
// This file is part of abc2svg.
//
// abc2svg is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

// constants from core/abc2svg.js
    var	C = abc2svg.C,

	OPEN_BRACE = 0x01,
	CLOSE_BRACE = 0x02,
	OPEN_BRACKET = 0x04,
	CLOSE_BRACKET = 0x08,
	OPEN_PARENTH = 0x10,
	CLOSE_PARENTH = 0x20,
	STOP_BAR = 0x40,
	FL_VOICE = 0x80,
	OPEN_BRACE2 = 0x0100,
	CLOSE_BRACE2 = 0x0200,
	OPEN_BRACKET2 = 0x0400,
	CLOSE_BRACKET2 = 0x0800,
	MASTER_VOICE = 0x1000

	// generate a new reference
	function new_ref(s) {
		if (!s.ref) {
			s.ref = ++meas.ref
			if (!s.items)
				s.items = ""
			s.items += ' xml:id="a' + s.ref.toString() + '"'
		}
	}

// --- decorations

	// decoration as text (dynam, fing) at end of measure
	// d[1] = tag
	// d[2] = item in the tag
	// d[3] = optional 'place'
	function decotxt1(s, d) {
		new_ref(s)
	    var	t = '\t  <' + d[1] + ' startid="#a' + s.ref + '"'

		//fixme: above or below
		if (d[3])			// place
			t += ' ' + d[3] + '="above"'
		meas.tags.push(t + '>' + d[2] + '</' + d[1] + '>')
	}

	// decoration in a symbol
	// d[1] = tag
	// d[2] = item in the tag
	// d[3] = optional 'place'
	function decosym(s, d) {
//fixme: if no place
//		if (1)
//			return' ' + d[2]

	    var	t = '\t\t<' + d[1]

		if (d[2])
			t += ' ' + d[2]
		//fixme: above or below
		if (d[3])			// place
			t += ' ' + d[3] + '="above"'
		if (!s.tags)
			s.tags = []
		s.tags.push(t + '/>')
	}

	// (decoration either in a symbol or at end of the measure)
	// decoration at end of the measure
	// d[1] = tag
	// d[2] = item in the tag
	// d[3] = optional 'place'
	function decotag1(s, d) {
		new_ref(s)
	    var	t = '\t  <' + d[1] + ' startid="#a' + s.ref + '"'

		if (d[2])
			t += ' ' + d[2]
		//fixme: above or below
		if (d[3])			// place
			t += ' ' + d[3] + '="above"'
		meas.tags.push(t + '/>')
	}

    var	deco_l = {
//		"-("
//		"-)"
//		"~("
//		"~)"
//fixme: form=cres/dim, place=above/below and 2 references
//		"<(": [null, "hairpin"],	// "cres",
//		"<)": [null, "hairpin"],	// "cres",
//		">"
//		">(": [null, "hairpin"],	// "dim",
//		">)": [null, "hairpin"],	// "dim",
		"^": [decosym, "artic", 'artic="marc"', "place"],
//		"+":
		"0": [decotxt1, "fing", "0", "place"],
		"1": [decotxt1, "fing", "1", "place"],
//		"15va("
//		"15va)"
//		"15vb("
//		"15vb)"
		"2": [decotxt1, "fing", "2", "place"],
		"3": [decotxt1, "fing", "3", "place"],
		"4": [decotxt1, "fing", "4", "place"],
		"5": [decotxt1, "fing", "5", "place"],
//		"8va("
//		"8va)"
//		"8vb("
//		"8vb)"
		accent: [decosym, "artic", 'artic="acc"', "place"],
//fixme: plist on notes
//		arpeggio

		breath: [decotag1, "breath"],
//		coda: [null, "O"],
//		crescendo(
//		crescendo)
//		dacapo
//		dacoda
//		"D.C."
//		"D.C.alcoda"
//		"D.C.alfine"
//		diminuendo(
//		diminuendo)
//		"D.S."
//		"D.S.alcoda"
//		"D.S.alfine"
		dot: [decosym, "artic", 'artic="stacc"', "place"],
		downbow: [decosym, "artic", 'artic="dnbow"', "place"],
//		emphasis: [null, "L"],
		f: [decotxt1, "dynam", "f", "place"],
		fermata: [decotag1, "fermata", null, "place"],
		ff: [decotxt1, "dynam", "ff", "place"],
		fff: [decotxt1, "dynam", "fff", "place"],
		ffff: [decotxt1, "dynam", "ffff", "place"],
//		fine
//		gmark
		invertedfermata: [decotag1, "fermata", 'form="inv"', "place"],
//		invertedturn: [decotag1, "fermata", 'form="inv"', "place"],
//		invertedturnx: [decotag1, "fermata", 'form="inv"', "place"],
//		longphrase
		lowermordent: [decotag1, "mordent", 'form="lower"'],
		marcato: [decosym, "artic", 'artic="marc"', "place"],
//		mediumphrase
		mf: [decotxt1, "dynam", "mf", "place"],
		mp: [decotxt1, "dynam", "mp", "place"],
		open: [decosym, "artic", 'artic="open"', "place"],
		p: [decotxt1, "dynam", "p", "place"],
		pralltriller: [decotag1, "mordent", 'form="upper"'],
//		ped
		"ped)": [decotxt1, "pedal", dir="up"],
		"ped(": [decotxt1, "pedal", dir="down"],
//		ped-up
//		plus
		pp: [decotxt1, "dynam", "pp", "place"],
		ppp: [decotxt1, "dynam", "ppp", "place"],
		pppp: [decotxt1, "dynam", "pppp", "place"],
//		rbstop
//		roll: [null, '~'],
//		segno: [null, 'S'],
		sfz: [decotxt1, "dynam", "sfz", "place"],
//		shortphrase
//		slide
		snap: [decosym, "artic", 'artic="snap"', "place"],
		tenuto: [decosym, "artic", 'artic="ten"', "place"],
//		thumb
		trill: [decotag1, "trill", null, "place"],
//2 references
//		"trill(": [null, "trill("],		// place=above/below
//		"trill)": [null, "trill)"],
		turn: [decotag1, "turn", 'form="upper"'],
		turnx: [decotag1, "turn", 'form="lower"'],
		upbow: [decosym, "artic", 'artic="upbow"', "place"],
		uppermordent: [decotag1, "mordent", 'form="upper"']
//		wedge
	}

	// build the decorations of a symbol
	// a decoration may be:
	// - as an item in the symbol (in s.items)
	// - as a tag in the symbol (in s.tags)
	// - as a tag at the end of the measure (in meas.tags)
	function deco_build(s) {
	    var	i, n, d

		for (i = 0; i < s.a_dd.length; i++) {
			n = s.a_dd[i].name
			d = deco_l[n]
//keep until all done
if (!d)
{
abc2svg.printErr('<!-- no conversion of '+n+'-->')
	continue
}
			d[0](s, d)
		}
	} // deco_build()

	// global variables/constants
    var	mode_tb = ["major", "dor", "phr", "lyd", "mix", "minor", "loc"],
	bar_ty = {".|": "dashed",
		"||": "dbl",
		".||": "dbldashed",
		".||": "dbldotted",
		".|": "dotted",
		"|]": "end",
		"": "invis",
		"[|:": "rptstart",
		":[]:": "rptboth",
		":|]": "rptend",
		"|": "single"
	},
	meas = {			// measure
		bar_num: 0,
		no_head: true,
		st: [],
		vo: [],
		ref: 100,
		clefs: [],		// clefs per staff
		tags: []
	}

	// dump the staves at start of tune (no 's') and inside the tune
	function staves_dump(s) {
	    var	v, p_v, staff, in_parenth, ln, stop, s2,
		voice_tb = abc.get_voice_tb(),
		vn = [],
		st = -1,
		sy = s ? s.sy : abc.get_cur_sy()

		// clef in staffDef
		function clef_dump(s) {
		    var	ln = ' clef.shape="'
			switch (s.clef_type) {
			case 't': ln += 'G"'; break
			case 'c': ln += 'C"'; break
			case 'b': ln += 'F"'; break
			case 'p': ln += 'perc"'; break
//fixme: TAB
			default:  return ''
			}
			ln += ' clef.line="' + s.clef_line + '"'
			if (s.clef_octave)
				ln += ' clef.dis="8" clef.dis.place="' +
					(s.clef_octave > 0 ? 'above"' : 'below"')
			return ln
		} // clef_dump()

//		meas.sy = sy

		for (v = 0; v < sy.voices.length; v++) {
			p_v = sy.voices[v]
			if (p_v)
				vn[p_v.range] = v
		}

		for (v = 0; v < sy.voices.length; v++) {
			if (vn[v] == undefined)
				continue
			p_v = sy.voices[vn[v]]
			if (p_v.st == st)
				continue
			if (st >= 0) {
				if (staff.flags & CLOSE_BRACE2)
					abc2svg.print('\t    </staffGrp>')
				if (staff.flags & CLOSE_BRACE)
					abc2svg.print('\t  </staffGrp>')
				if (staff.flags & CLOSE_BRACKET2)
					abc2svg.print('\t   </staffGrp>')
				if (staff.flags & CLOSE_BRACKET)
					abc2svg.print('\t </staffGrp>')
			}

			staff = sy.staves[++st]

			stop = (staff.flags & STOP_BAR) ?
					' bar.thru="false"' : ''
			if (staff.flags & OPEN_BRACKET)
				abc2svg.print('\t <staffGrp symbol="bracket"' +
						stop + '>')
			if (staff.flags & OPEN_BRACKET2)
				abc2svg.print('\t   <staffGrp symbol="bracket"' +
						stop + '>')
			if (staff.flags & OPEN_BRACE)
				abc2svg.print('\t  <staffGrp symbol="brace"' +
						stop + '>')
			if (staff.flags & OPEN_BRACE2)
				abc2svg.print('\t    <staffGrp symbol="brace"' +
						stop + '>')

			ln = '\t     <staffDef n="' + (st + 1).toString() + '"'
			for (s2 = voice_tb[vn[v]].sym; s2; s2 = s2.next) {
				if (s2.dur)
					break
				if (s2.type == C.CLEF) {
					if (!s2.invis)
						ln += clef_dump(s2)
					meas.clefs[st] = s2
					break
				}
			}
			abc2svg.print(ln + '/>')

		}
		if (staff.flags & CLOSE_BRACE2)
			abc2svg.print('\t    </staffGrp>')
		if (staff.flags & CLOSE_BRACE)
			abc2svg.print('\t  </staffGrp>')
		if (staff.flags & CLOSE_BRACKET2)
			abc2svg.print('\t   </staffGrp>')
		if (staff.flags & CLOSE_BRACKET)
			abc2svg.print('\t </staffGrp>')
	} // staves_dump()

// dump the header
function head_dump() {
    var	i, v, s, g, line, ulen, tmp, tmp2, grace, bagpipe, eoln, curv,
	info = abc.info(),
	first = abc.get_tsfirst(),
	voice_tb = abc.get_voice_tb(),
	nv = voice_tb.length,
	vo = [],		// dump line per voice
	vold = [],		// false/true for voice name
	vti = []

	// -- main code of head_dump()

	info.T = info.T ? info.T.replace(/\n/g,'<lb/>') : ''
	abc2svg.print('\
<mei xmlns="http://www.music-encoding.org/ns/mei" meiversion="4.0.0">\n\
 <meiHead>\n\
  <fileDesc>\n\
   <titleStmt>\n\
    <title>' + info.T + '</title>\n\
   </titleStmt>\n\
   <pubStmt />\n\
  </fileDesc>\n\
  <encodingDesc>\n\
   <appInfo>\n\
    <application version="' + abc2svg.version + '">\n\
     <name>abc2svg</name>\n\
     <p>Transcoded from ABC</p>\n\
    </application>\n\
   </appInfo>\n\
  </encodingDesc>\n\
  <workList>\n\
   <work>\n' +
	(info.T ? ('\
    <title>\n\
      ' + info.T + '\n\
    </title>\n') : '') +
	(info.C ? ('\
    <composer>\n\
      ' + info.C + '\n\
    </composer>\n') : '') + '\
   </work>\n\
  </workList>\n\
 </meiHead>\n\
 <music>\n\
  <body>\n\
   <mdiv>\n\
     <score>')
	line = '\
       <scoreDef'
	s = voice_tb[0].meter
	if (s.wmeasure != 1) {
		meas.wmeasure = s.wmeasure
		tmp = s.a_meter[0].top
		tmp2 = s.a_meter[0].bot
		if (tmp2)
			line += ' meter.count="' + tmp + '" meter.unit="' + tmp2 + '"'
		else if (tmp == "C")
			line += ' meter.sym="common"'
		else if (tmp == "C|")
			line += ' meter.sym="cut"'
		else
			line += ' meter.count="' + tmp + '"'
	}
	s = voice_tb[0].key
	tmp = s.k_sf
	if (tmp > 0)
		tmp = tmp.toString() + 's'
	else if (tmp < 0)
		tmp = (-tmp).toString() + 'f'
	line += ' key.sig="' + tmp + '" key.mode="' +
		mode_tb[s.k_mode] + '">'
	abc2svg.print(line)

	// output the staff system
	staves_dump()

	abc2svg.print('\
       </scoreDef>\n\
       <section>')
} // head_dump()

user.img_out = function() {}	// do the generation with no output

// dump a whole measure
function meas_dump(s2) {
    var ln, i, s, t, st, v, staff, voice, tim, g, s1

	// clef in a measure
	function clef_dump(s) {
if (!meas.clefs[s.st])
abc2svg.printErr('!! V:'+s.p_v.id+' '+s.time+' '+s.type+' st '+s.st)
		if (s.clef_type == meas.clefs[s.st].clef_type)
			return
		meas.clefs[s.st] = s
	    var	ln = '\t     <clef shape="'

		switch (s.clef_type) {
		case 't': ln += 'G"'; break
		case 'c': ln += 'C"'; break
		case 'b': ln += 'F"'; break
		case 'p': ln += 'perc"'; break
//fixme: TAB
		default:  return ''
		}
		ln += ' line="' + s.clef_line + '"'
		if (s.clef_octave)
			ln += ' dis="8" dis.place="' +
				(s.clef_octave > 0 ? 'above"' : 'below"')
		abc2svg.print(ln + '/>')
	} // clef_dump()

	// dump a duration
	function dur_dump(dur, nflags) {
	    var	t,
		d = ' dur="'

		if (nflags == undefined) {
//fixme: what if dur % 12 != 0 ?
			t = (dur / 12) | 0
			for (nflags = 5; t; t >>= 1, nflags--) {
				if (t & 1)
					break
			}
		}
		if (nflags == -4)
			d += "long"
		else if (nflags == -3)
			d += "breve"
		else
			d += (1 << (nflags + 2)).toString()
		dur >>= (5 - nflags)
		switch (dur) {
		case 12: break
		case 18: d += '" dots="1'; break
		case 21: d += '" dots="2'; break
//		case 22:
		default: d += '" dots="3'; break
		}
		return d + '"'
	} // dur_dump()

	function gch_dump(s) {
		new_ref(s)
	    var i, gch, t

		for (i = 0; i < s.a_gch.length; i++) {
			t = ' startid="#a' + s.ref
			gch = s.a_gch[i]
//			font_def(gch.type == 'g' ? "gchord" : "annotation", gch.text)
			if (gch.type == 'g') {
				t = '\t  <harm' + t + '">' + gch.text + '</harm>'
			} else {
				if (gch.type == "^")
					t += '" place="above"'
				else if (gch.type == "_")
					t += '" place="below"'
				t = '\t  <dir' + t + '>' + gch.text + '</dir>'
			}
			meas.tags.push(t)
		}
	} // gch_dump()

	function note_dump(s) {
	    var m, note, t, item, s2, b_tp, i, r,
		n = s.nhd,
		line = ""

		function ly_dump(s) {
		    var	ly, i,
			nly = s.a_ly.length

			function ly_du1(t) {
			    var	ln = '\t\t <syl'
				switch (t) {
				case '-\n':
				case '_\n':
//fixme: no text...
					return
				default:
					if (t.slice(-1) == "\n") {
						t = t.slice(0, -1)
						ln += ' con="d"'
					}
					break
				}
				abc2svg.print(ln + '>' + t + '</syl>')
			}

			if (nly == 1) {
				ly_du1(s.a_ly[0].t)
				return
			}
			for (i = 0; i < nly; i++) {
				abc2svg.print('\t\t<verse n="' +
						(i + 1).toString() + '">')
				ly_du1(s.a_ly[i].t)
				abc2svg.print('\t\t</verse>')
			}
		} // ly_dump()

		function pit_dump(note) {
		    var pit = (note.opit || note.pit) + 12,
			ln = ' pname="' +
				"cdefgab"[pit % 7] + '" oct="' +
				((pit / 7) | 0).toString() + '"'
			switch (note.acc) {
			case -2: ln += ' accid="ff"'; break
			case -1: ln += ' accid="f"'; break
			case 1: ln += ' accid="s"'; break
			case 2: ln += ' accid="x"'; break
			case 3: ln += ' accid="n"'; break
			}
			return ln
		} // pit_dump()

		// dump the starting beam and tuplets
		function btps_dump(s) {
		    var i, tp, ln, st

			function tp_dump(tp) {
			    var	ln = '\t    <tuplet num="' +
					tp.p + '" numbase="' +
					tp.q
				if (Number(tp.f[0]) == 1) {
					ln +=
					    '" num.visible="false" bracket.visible="false'
				} else {
					switch (Number(tp.f[2])) {
					case 1:	ln += '" bracket.visible="false'; break
					case 2:	ln += '" num.format="ratio'; break
					}
					switch (Number(tp.f[3])) {
					case 1:	ln += '" bracket.place="above'; break
					case 2:	ln += '" bracket.place="below'; break
					}
				}
				abc2svg.print(ln + '">')
			} // tp_dump()

			// if start of a beam, check if crossing a measure bar
//			// or with notes on different staves
			if (s.beam_st && !s.beam_end) {
//				st = s.st
				for (s2 = s.next; ; s2 = s2.next) {
//					if (s2.st != st || s2.type == C.BAR) {
					if (s2.type == C.BAR) {
						while (!s2.beam_end)
							s2 = s2.next
						new_ref(s)
						new_ref(s2)
//						if (s.st != s2.st)
//							beam.with="above" or "below"
						meas.tags.push(
							'\t  <beamSpan startid="#a' +
								s.ref + '" endid="#a' +
								s2.ref + '"/>')
						s2 = null	// the beam is treated
						break
					}
					if (s2.beam_end)
						break
				}
			}
			if (!s2) {			// if no beam
				if (s.tp) {		// but tuplets
					i = s.tp.length
					while (--i >= 0)
						tp_dump(s.tp[i])
				}
				return
			}

			ln = '\t    <beam>'
			if (!s.tp) {			// beam without any tuplet
				abc2svg.print(ln)
				return
			}

			// start of beam and tuplet(s)
			i = 0
			while (i < s.tp.length) {
				tp = s.tp[i]
				r = tp.ro
				for (s2 = s; s2; s2 = s2.next) {
					if (s2.dur)
						r--
					if (s2.beam_end || r < 0)
						break
				}
				if (!s2.beam_end)
					break
				tp_dump(tp)
				i++
			}
			abc2svg.print(ln)
			while (i < s.tp.length)
				tp_dump(s.tp[i++])
		} // btps_dump()

		// -- main of note_dump()

		btps_dump(s)

		if (s.beam_br1 || s.beam_br2) {
			if (!s.items)
				s.items = ""
			s.items += s.beam_br1 ? ' breaksec="1"' :
						' breaksec="2"'
		}
		if (n) {				// chord
			line = '\t      <chord' + dur_dump(s.dur_orig, s.nflags)
			if (s.grace)
				line += ' grace="unknown"'
			if (s.stem > 0)
				line += ' stem.dir="up"'
			else if (s.stem < 0)
				line += ' stem.dir="down"'
			if (s.st != s.p_v.st)
				line += ' staff="' + (s.st + 1).toString() + '"'
			if (s.items)
				line += s.items
			abc2svg.print(line + '>')
			line = ""
			for (m = 0; m <= n; m++) {
				note = s.notes[m]
				line = '\t       <note' + pit_dump(note)
				if (note.items)
					line += note.items
				abc2svg.print(line + '/>')
			}
			if (s.tags)
				abc2svg.print(s.tags.join("\n"))
			if (s.a_ly)
				ly_dump(s)
			abc2svg.print('\t      </chord>')
		} else {				// single note
			line = '\t      <note' + dur_dump(s.dur_orig, s.nflags) +
				pit_dump(s.notes[0])
			if (s.stem > 0)
				line += ' stem.dir="up"'
			else if (s.stem < 0)
				line += ' stem.dir="down"'
			if (s.grace)
				line += ' grace="unknown"'
			if (s.st != s.p_v.st)
				line += ' staff="' + (s.st + 1).toString() + '"'
			if (s.items)
				line += s.items
			if (s.tags || s.a_ly) {
				abc2svg.print(line + '>')
				if (s.tags)
					abc2svg.print(s.tags)
				if (s.a_ly)
					ly_dump(s)
				abc2svg.print('\t      </note>')
			} else {
				abc2svg.print(line + '/>')
			}
		}

		n = s.tpe
		if (s.beam_st || !s.beam_end) {		// if no beam end
			while (--n >= 0)
				abc2svg.print('\t    </tuplet>')
			return
		}
		if (!n) {			// beam end but no tuplet end
			abc2svg.print('\t    </beam>')
			return
		}
		for (s2 = s.prev; s2; s2 = s2.prev) {
			if (s2.beam_st) {
				abc2svg.print('\t    </beam>')
				break
			}
			if (s2.tp) {
				i = s2.tp.length
				while (i > 0 && n > 0) {
					abc2svg.print('\t    </tuplet>')
					i--
					n--
				}
			}
		}
		while (--n >= 0)
			abc2svg.print('\t    </tuplet>')
	} // note_dump()

	function tempo_dump(s) {
		if (s.del)
			return
	    var	v,
		t = '\t  <tempo place="above" startid="#a',
		s2 = s.ts_next

		while (!s2.dur && s2.type != C.SPACE)
			s2 = s2.ts_next
		new_ref(s2)
		t += s2.ref + '"'

		v = s.tempo
		if (v) {
			if (s.tempo_notes)
				v *= s.tempo_notes[0] / C.BLEN * 4
			t += ' mm="' + v + '"'
		}
		if (!s.tempo_str1 && !s.tempo_str2) {
			meas.tags.push(t + '/>')
		} else {
			meas.tags.push(t + '>')
			t = ''
			if (s.tempo_str1)
				t += s.tempo_str1
			//fixme: note
			if (!t)
				t += ' '
			t += 'fixme=' + s.tempo
			if (s.tempo_str2) {
				if (!t)
					t += ' '
				t += s.tempo_str2
			}
			meas.tags.push('\t  </tempo>')
		}
	} // tempo_dump()

	// -- main of meas_dump()

	// get the start time
	tim = 1000000
	for (st = 0; st < meas.st.length; st++) {
		staff = meas.st[st]
		if (!staff)
			continue
		for (v = 0; v < staff.vo.length; v++) {
			s = staff.vo[v].s[0]
			if (s && s.time < tim) {
				s1 = s
				tim = s.time
			}
		}
	}
	if (!s1)
		return				// !

	if (tim == 0 && s2.time < meas.wmeasure)
		meas.bar_num = -1		// upbeat
	ln = '\t <measure n="' + (++meas.bar_num).toString() +'"'
	if (meas.bar_num == 0)
		ln += ' metcon="false" type="upbeat"'
	if (s1.type == C.BAR			// if bar at start of tune
	 && s1.time == 0) {
		t = s1.bar_type
		if (s1.bar_dotted)
			t = '.' + t
		t = bar_ty[t]
		if (t)
			ln += ' left="' + t + '"'
	}

	if (s2) {
		t = s2.bar_type			// bar at end of measure
		if (s2.bar_dotted)
			t = '.' + t
		t = bar_ty[t]
		if (t && t != "single")
			ln += ' right="' + t + '"'
	}
	abc2svg.print(ln + '>')

	for (st = 0; st < meas.st.length; st++) {
		staff = meas.st[st]
		if (!staff)
			continue
		ln = '\t  <staff n="' + (st + 1).toString() + '"'
		abc2svg.print(ln + '>')
		for (v = 0; v < staff.vo.length; v++) {
			voice = staff.vo[v]
//			if (!voice)
//				continue
			ln = '\t   <layer n="' + (v + 1).toString() + '"'
			abc2svg.print(ln + '>')
			tim = s1.time
			for (i = 0; i < voice.s.length; i++) {
				s = voice.s[i]
				if (s.time > tim)
					abc2svg.print('\t      <space' +
						dur_dump(s.time - tim) + '/>')
				if (s.a_dd)
					deco_build(s)
				if (s.a_gch)
					gch_dump(s)
//fixme:s.repeat_..
//fixme:s.invis
				switch (s.type) {
				case C.CLEF:
					clef_dump(s)
					break
				case C.GRACE:
					for (g = s.extra; g; g = g.next)
						note_dump(g)
					break
//				case C.KEY:
//					break
//				case C.METER:
//					break
//				case C.MREST:
//					break
				case C.NOTE:
					note_dump(s)
					break
				case C.REST:
//fixme: test if full measure
					ln = '\t      <' +
						(s.invis ? 'space' : 'rest') +
						dur_dump(s.dur_orig, s.nflags)
					if (s.items)
						ln += s.items
					if (s.tags) {
						abc2svg.print(ln + '>')
						abc2svg.print(s.tags)
						abc2svg.print('\t      </rest>')
					} else {
						abc2svg.print(ln + '/>')
					}
					break
				case C.TEMPO:
					tempo_dump(s)
					break
				}
				if (s.dur)
					tim = s.time + s.dur
			}
			abc2svg.print('\t   </layer>')
		}
		abc2svg.print('\t  </staff>')
	}

	// output the decorations
	if (meas.tags.length) {
		abc2svg.print(meas.tags.join('\n'))
		meas.tags = []
	}

	abc2svg.print('\t </measure>')
} // meas_dump()

// dump a music line (SVG image)
function glue(of, width) {
   var	s, staff, v, voice, ending,
	tsfirst = this.get_tsfirst()

	of(width)

	function slur_dump(s) {
	    var	i, m, note, note2, sl, refs, refs2, ref, ref2

		function slur_out(ty, r1, r2) {
		    var	line = '\t  <slur startid="#a' + r1 +
				'" endid="#a' + r2 + '"'
			if (ty & C.SL_DOTTED)
				line += ' lform="dotted"'
			switch (ty & 0x07) {
			case C.SL_ABOVE:
				line += ' curvedir="above"'
				break
			case C.SL_BELOW:
				line += ' curvedir="below"'
				break
			}
			meas.tags.push(line + '/>')
		} // slur_out()

		if (s.sls) {
			new_ref(s)
			ref = s.ref
			for (i = 0; i < s.sls.length; i++) {
				sl = s.sls[i]
				note = sl.note
				if (sl.is_note) {
					new_ref(note)
					ref2 = note.ref
				} else {
					new_ref(note.s)
					ref2 = note.s.ref
				}
				slur_out(sl.ty, ref, ref2)
			}
		}
		if (s.sl1) {
			for (m = 0; m <= s.nhd; m++) {
				note = s.notes[m]
				if (note.sls) {
					new_ref(note)
					ref = note.ref
					for (i = 0; i < note.sls.length; i++) {
						sl = note.sls[i]
						note2 = sl.note
						if (sl.is_note) {
							new_ref(note2)
							ref2 = note2.ref
						} else {
							new_ref(note2.s)
							ref2 = note2.s.ref
						}
						slur_out(sl.ty, ref, ref2)
					}
				}
			}
		}

	} // slur_dump()

	function tie_dump(s) {
	    var	m, note, note2, refs, refs2, ref, ref2,
		s2 = s.tie_s

		if (!s.nhd) {
			new_ref(s)
			refs = s.ref
		}
		if (!s2.nhd) {
			new_ref(s2)
			refs2 = s2.ref
		}
		for (m = 0; m <= s.nhd; m++) {
			note = s.notes[m]
			note2 = note.tie_n
			if (refs) {
				ref = refs
			} else {
				new_ref(note)
				ref = note.ref
			}
			if (refs2) {
				ref2 = refs2
			} else {
				new_ref(note2)
				ref2 = note2.ref
			}
			line = '\t  <tie startid="#a' + ref +
				'" endid="#a' + ref2 + '"'
			if (note.tie_ty & C.SL_DOTTED)
				line += ' lform="dotted"'
			switch (note.tie_ty & 0x07) {
			case C.SL_ABOVE:
				line += ' curvedir="above"'
				break
			case C.SL_BELOW:
				line += ' curvedir="below"'
				break
			}
			meas.tags.push(line + '/>')
		}
	} // tie_dump()

	// main code of glue()

	// output the MEI header if not done yet
	if (meas.no_head) {
		meas.no_head = false
		head_dump()
	}

	// loop on the measures
	for (s = tsfirst; s; s = s.ts_next) {
//		if (abc.get_cur_sy() != meas.sy)
//			staves_dump()

		if (ending && s.rbstop)
			ending = 2
		if (s.type == C.BAR)
			continue

		// create the staves and voices if not done yet
		staff = meas.st[s.st]
		if (!staff) {
			meas.st[s.st] = staff = {
				vo: []
			}
		}

//other structure: voice sequences per staff
//		v = staff.vo.length
//		while (--v >= 0) {
//			if (staff.vo[v].v == s.v)
//				break
//		}
//		if (v < 0) {			// new voice in this staff
//			v = staff.vo.length
//			staff.vo.push({
//				v: s.v,
//				s: []		// array of symbols
//			})
//		}
//		voice = staff.vo[v]

//simple structure: use 'staff=' on staff changes
		voice = meas.vo[s.v]
		if (!voice) {
			meas.vo[s.v] = voice = {
				s: []
			}
			staff.vo.push(voice)
		}

		if (s.time == 0 && s.prev && s.prev == C.BAR
		 && voice.s.length == 0)		// start with a bar
			voice.s.push(s.prev)

		voice.s.push(s)

		switch (s.type) {
		case C.GRACE:
		case C.NOTE:
			if (s.sls || s.sl1)
				slur_dump(s)
			if (s.tie_s)
				tie_dump(s)
			break
		case C.STAVES:
			staves_dump(s)
			break
		}

		if (!s.ts_next || s.ts_next.type == C.BAR) {
			if (meas.eol) {
				meas.eol = false
				abc2svg.print('\t <sb/>')
			}
			meas_dump(s.ts_next)
			if (ending == 2) {		// if end of ending
				abc2svg.print('\t</ending>')
				ending = 0
			}
			if (s.ts_next && s.ts_next.text) {	// if ending
				abc2svg.print('\t<ending n="' +
						s.ts_next.text + '">')
				ending = 1
			}
			meas.st = []
			meas.vo = []
		}
	}
	meas.eol = true
} // glue()

// hook at tune generation start
function mei_output(of) {
	if (meas.done)			// do only one generation
		return
	meas.done = true
	of()
}

// -- local functions
abc2svg.abc_init = function(args) {

	abc.output_music = mei_output.bind(abc, abc.output_music)
	abc.set_sym_glue = glue.bind(abc, abc.set_sym_glue)

	abc2svg.print('<?xml version="1.0" encoding="UTF-8"?>\n\
<?xml-model href="http://music-encoding.org/schema/4.0.0/mei-all.rng"\n\
 type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>\n\
<?xml-model href="http://music-encoding.org/schema/4.0.0/mei-all.rng"\n\
 type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>')
}

abc2svg.abc_end = function() {
	abc2svg.print('\
       </section>\n\
     </score>\n\
   </mdiv>\n\
  </body>\n\
 </music>\n\
</mei>')
//fixme: terminer derniere mesure si pas barre de fin
//fixme: W: dans <section> - cf
//		/home/jef/abc/mei/McFerrin_Don't_worry.mei
	if (user.errtxt)
		abc2svg.printErr("Errors:\n" + user.errtxt)
}

abc2svg.abort = function(e) {
	abc2svg.printErr(e.message + "\n*** Abort ***\n" + e.stack)
	abc2svg.abc_end()
	abc2svg.quit()
}
Changes to toodt.js.
︙
63
64
65
66
67
68
69












70
71
72
73
74
75
76
77
78
79

80
81
82
83
84
85
86
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99







+
+
+
+
+
+
+
+
+
+
+
+










+







function set_unit(p) {
	if (typeof p == "string")
		return p
	if (page_type[0] == 'L')
		return (p / 96).toFixed(1) + 'in'
	return (p / 37.8).toFixed(1) + 'cm'
}

// replace <>& by XML character references
function clean_txt(txt) {
	return txt.replace(/<|>|&.*?;|&/g, function(c) {
		switch (c) {
		case '<': return "&lt;"
		case '>': return "&gt;"
		case '&': return "&amp;"
		}
		return c
	})
}

// output a header or a footer
function gen_hf(type, stype, str) {
    var	c, i, res_left,
	j = 0,
	res = '<style:' + type + '>\n\
<text:p text:style-name="' + stype + '\"><text:tab/>'

	if (str[0] == '"')
		str = str.slice(1, -1)
	str = clean_txt(str)
	if (str.indexOf('\t') < 0)		// if no TAB
		str = '\t' + str		// center

	for (i = 0; i < str.length; i++) {
		c = str[i]
		switch (c) {
		case '\t':
︙
100
101
102
103
104
105
106
107
108



109
110
111
112
113
114

115
116
117
118
119
120
121
113
114
115
116
117
118
119


120
121
122
123
124
125
126
127

128
129
130
131
132
133
134
135







-
-
+
+
+





-
+







			continue
		case '$':
			break
		}
		c = str[++i]
		switch (c) {
		case 'd':
			t = abc.get_fname();
			res += fs.statSync(t).ctime.toLocaleString()
			if (!abc2svg.get_mtime)
				break // cannot know the modification date of the file
			res += abc2svg.get_mtime(abc.parse.fname).toLocaleString()
			break
		case 'D':
			res += (new Date()).toLocaleString()
			break
		case 'F':
			res += abc.get_fname()
			res += abc.parse.fname
			break
		case 'I':
			c = str[++i]
		case 'T':
			t = abc.info()[c]
			if (t) {
				t_info = '\
︙
Added toparam.js.
































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// abc2svg - toparam - dump the abc2svg parameters
//
// Copyright (C) 2019 Jean-Francois Moine
//
// This file is part of abc2svg.
//
// abc2svg is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

abc2svg.abort = function(e) {
	abc2svg.quit()
}

abc2svg.abc_init = function() {
}

abc2svg.abc_end = function() {
    var	k, v, t,
	cfmt = abc.cfmt()

	if (user.errtxt)
		abc2svg.print("Errors:\n" + user.errtxt)
	abc2svg.print('% abc2svg default parameters\n')
	for (k in cfmt) {
		if (cfmt.hasOwnProperty(k)) {
			v = cfmt[k]
			if (k.slice(-4) == "font") {
				t = v.name || ""
				if (v.weight)
					t += v.weight
				if (v.style)
					t += v.style
				if (!t)
					t = "*"
				if (k[0] == "u")
					k = "setfont-" + k[1]
				v = t + ' ' + (v.size || "*")
			} else if (k == "infoname") {
				t = v.split("\n")
				while (t.length)
					abc2svg.print('%%' + k + ' ' + t.shift())
				continue
			} else if (k.slice(-6) == "margin"
				|| k.slice(0, 4) == "page") {
				v = (v / 37.8).toFixed(2).replace(/\.?0+$/,"") + "cm"
			} else if (k == "writefields") {
				v = v + " 1"
			} else if (v === "") {
				v = '""'
			}
			abc2svg.print('%%' + k + ' ' + v)
		}
	}
}
Changes to toxhtml.js.
︙
74
75
76
77
78
79
80


81


82
83
84
85
86
87

88
89
90
91
92
93
94
74
75
76
77
78
79
80
81
82

83
84
85
86
87
88
89

90
91
92
93
94
95
96
97







+
+
-
+
+





-
+







			r[j] += c
			continue
		case '$':
			break
		}
		c = str[++i]
		switch (c) {
		case 'd':
			if (!abc2svg.get_mtime)
		case 'd':	// cannot know the modification date of the file
				break // cannot know the modification date of the file
			r[j] += abc2svg.get_mtime(abc.parse.fname)
			break
		case 'D':
			r[j] += get_date()
			break
		case 'F':
			r[j] += abc.get_fname()
			r[j] += abc.parse.fname
			break
		case 'I':
			c = str[++i]
		case 'T':
			t = abc.info()[c]
			if (t)
				r[j] += t.split('\n', 1)[0]
︙
148
149
150
151
152
153
154
155

156
157
158
159
160
161
162
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165







-
+







    var cfmt = abc.cfmt()

	// output a header or footer
	function gen_hf(type, str) {
		var	a, i, page,
			lcr = ["left", "center", "right"];

		a = header_footer(str)
		a = header_footer(clean_txt(str))
		for (i = 0; i < 3; i++) {
			str = a[i]
			if (!str)
				continue
			if (str.indexOf('\x0c') >= 0) {
				str = str.replace('\x0c', '');
				page = " page"
︙
225
226
227
228
229
230
231
232
233
234










235
236
237
238
239
240
241
228
229
230
231
232
233
234



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251







-
-
-
+
+
+
+
+
+
+
+
+
+







<meta name="generator" content="abc2svg-' + abc2svg.version + '"/>\n\
<!-- CreationDate: ' + get_date() + '-->\n\
<style type="text/css">\n\
svg {display:block}\n\
body {width:' + cfmt.pagewidth.toFixed(0) +'px}\n\
p {' + set_pstyle() + 'margin-top:0}\n\
p span {line-height:' + ((cfmt.lineskipfac * 100) | 0).toString() + '%}\n' +
			((header || footer) ? media_f : media_s) + '\n\
@page{margin:' + topmargin + ' 0 ' + botmargin + ' 0}\n\
</style>\n\
			((header || footer) ? media_f : media_s))
// works with chrome and --headless
		if (abc.page)
			abc2svg.print('@page{size:' +
				(cfmt.pagewidth / 96).toFixed(2) + 'in ' +
				(cfmt.pageheight / 96).toFixed(2) + 'in;margin:0}')
		else
			abc2svg.print('@page{margin:0' + topmargin + ' 0 ' +
							 botmargin + ' 0}')
		abc2svg.print('</style>\n\
<title>abc2svg document</title>\n\
</head>\n\
<body>')
		if (header)
			gen_hf("header", header)
		if (footer)
			gen_hf("footer", footer);
︙
Changes to util/abcdoc.js.
︙
13
14
15
16
17
18
19


20
21
22
23
24
25
26
27
28
29
30
31
32


33

34
35

36
37
38
39
40






41
42
43





44
45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101


102
103
104

105
106

107
108
109
110
111
112
113
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

37
38

39
40




41
42
43
44
45
46



47
48
49
50
51

52
53
54
55
56
57
58

59
60
61
62
63
64
65
66
67
68
69
70
71
72










73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88










89
90
91
92

93

94
95
96
97
98
99
100
101
102







+
+













+
+
-
+

-
+

-
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+
+
-







-
+













-
-
-
-
-
-
-
-
-
-
















-
-
-
-
-
-
-
-
-
-
+
+


-
+
-

+







// abc2svg is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

(function(){

window.onerror = function(msg, url, line) {
	if (typeof msg == 'string')
		alert("window error: " + msg +
			"\nURL: " + url +
			"\nLine: " + line)
	else if (typeof msg == 'object')
		alert("window error: " + msg.type + ' ' + msg.target.src)
	else
		alert("window error: " + msg)
	return false
}

// function called when abc2svg is fully loaded
function abcdoc() {
var	errtxt = '',
    var	errtxt = '',
	new_page = '',
	abc,
	page,				// document source
	jsdir = document.currentScript ?
		document.currentScript.src.match(/.*\//) :
		(function() {
			var scrs = document.getElementsByTagName('script');
			return scrs[scrs.length - 1].src.match(/.*\//) || ''
		    document.currentScript.src.match(/.*\//) :
		    (function() {
		     var s_a = document.getElementsByTagName('script')
			for (var k = 0; k < s_a.length; k++) {
				if (s_a[k].src.indexOf('abcdoc-') >= 0)
					return s_a[k].src.match(/.*\//) || ''
		})()

// -- abc2svg init argument
			}
			return ""	// ??

	})(),
    user = {	// -- abc2svg init argument
var user = {
	errmsg: function(msg, l, c) {	// get the errors
		errtxt += clean_txt(msg) + '\n'
	},
	img_out: function(str) {	// image output
		new_page += str
	},
	page_format: true		// define the non-page-breakable blocks
}
    }

// replace <>& by XML character references
function clean_txt(txt) {
	return txt.replace(/<|>|&.*?;|&/g, function(c) {
		switch (c) {
		case '<': return "&lt;"
		case '>': return "&gt;"
		case '&': return "&amp;"
		}
		return c
	})
}

// function called when the page is loaded
function dom_loaded() {

	// loop until abc2svg is fully loaded
	if (typeof abc2svg != "object"
	 || !abc2svg.modules) {
		setTimeout(dom_loaded, 500)
		return
	}

// function to load javascript files
	abc2svg.loadjs = function(fn, relay, onerror) {
		var s = document.createElement('script');
		if (/:\/\//.test(fn))
			s.src = fn		// absolute URL
		else
			s.src = jsdir + fn;
		s.type = 'text/javascript'
		if (relay)
			s.onload = relay;
		s.onerror = onerror || function() {
			alert('error loading ' + fn)
		}
		document.head.appendChild(s)
	}

	var page = document.body.innerHTML

	// accept page formatting
	abc2svg.abc_end = function() {}

	// load the required modules
	if (!abc2svg.modules.load(page, dom_loaded))
		return

	// search the ABC tunes and add their rendering as SVG images
    // search the ABC tunes and add their rendering as SVG images
    function render() {
	var	i = 0, j, k, res,
		re = /\n%abc|\nX:/g,		// start on "%abc" or "X:"
		re_stop = /\n<|\n%.begin/g;	// stop on "<" and skip "%%begin"
		re_stop = /\n<|\n%.begin/g,	// stop on "<" and skip "%%begin"

	abc = new abc2svg.Abc(user);

	abc.tosvg('abcdoc', '%%bgcolor white\n\
%%rightmargin 0.8cm\n\
%%leftmargin 0.8cm\n\
%%topspace 0')
	for (;;) {

		// get the start of a ABC sequence
︙
164
165
166
167
168
169
170























171
172
173
174

153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+

	try {
		document.body.innerHTML = new_page + page.slice(i)
	} catch (e) {
		alert("abc2svg bad generated SVG: " + e.message +
			"\nStack:\n" + e.stack)
	}
    } // render()

	// --- abcdoc() main code ---

	// get the page content
	page = document.body.innerHTML;

	// accept page formatting
	abc2svg.abc_end = function() {}

	// load the required modules, then render the music
	if (abc2svg.modules.load(page, render))
		render()
} // abcdoc()

// loop until abc2svg is fully loaded
function dom_loaded() {
	if (typeof abc2svg != "object"
	 || !abc2svg.modules) {
		setTimeout(dom_loaded, 500)
		return
	}
	abcdoc()
}

// wait for the page to be loaded
document.addEventListener("DOMContentLoaded", dom_loaded, false)
})()
Changes to util/abcemb.js.
︙
26
27
28
29
30
31
32




33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52









53
54
55

56
57
58
59
60
61
62

63







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

83
84
85
86
87

88
89
90
91
92
93
94
26
27
28
29
30
31
32
33
34
35
36
37
38

39
40



41
42
43
44
45
46
47





48
49
50
51
52
53
54
55
56
57
58

59
60
61
62
63
64
65
66
67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92

93
94
95
96
97

98
99
100
101
102
103
104
105







+
+
+
+


-


-
-
-







-
-
-
-
-
+
+
+
+
+
+
+
+
+


-
+







+
-
+
+
+
+
+
+
+


















-
+




-
+







	else if (typeof msg == 'object')
		alert("window error: " + msg.type + ' ' + msg.target.src)
	else
		alert("window error: " + msg)
	return false
}

var abc2svg = {}

// function called when abc2svg is fully loaded
function dom_loaded() {
var	errtxt = '',
	new_page = '',
	abc,				// (must be global for follow.js)
	playing,
	abcplay,
	playconf = {
		onend: endplay
	},
	page,				// document source
	a_src = [],			// index: #sequence,
					//	value: [start_idx, end_idx]
	a_pe = [],			// index: #sequence, value: playing events
	glop,				// global sequence for play
	old_gm,
	jsdir = document.currentScript ?
		document.currentScript.src.match(/.*\//) :
		(function() {
			var scrs = document.getElementsByTagName('script');
			return scrs[scrs.length - 1].src.match(/.*\//) || ''
		})()
		    document.currentScript.src.match(/.*\//) :
		    (function() {
		     var s_a = document.getElementsByTagName('script')
			for (var k = 0; k < s_a.length; k++) {
				if (s_a[k].src.indexOf('abcemb-') >= 0)
					return s_a[k].src.match(/.*\//) || ''
			}
			return ""	// ??
	})(),

// -- abc2svg init argument
var user = {
    user = {
	errmsg: function(msg, l, c) {	// get the errors
		errtxt += clean_txt(msg) + '\n'
	},
	img_out: function(str) {	// image output
		new_page += str
	},
	page_format: true		// define the non-page-breakable blocks
    };
}

// play arguments
    playconf = {
	onend: function() {
		playing = false
	}
    }

// replace <>& by XML character references
function clean_txt(txt) {
	return txt.replace(/<|>|&.*?;|&/g, function(c) {
		switch (c) {
		case '<': return "&lt;"
		case '>': return "&gt;"
		case '&': return "&amp;"
		}
		return c
	})
}

function endplay() {
	playing = false
}

// function called on click on the music
function playseq(seq) {
abc2svg.playseq = function(seq) {
    var	outputs

	if (!abcplay) {
		if (typeof AbcPlay == "undefined") { // as play-1.js not loaded,
			playseq = function(){}	     // don't come here anymore
			abc2svg.playseq = function(){}	// don't come here anymore
			return
		}
		abcplay = AbcPlay(playconf);
	}
	if (playing) {
		abcplay.stop();
		return
︙
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163





164
165














166
167
168
169
170
171
172
121
122
123
124
125
126
127










128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143










144

145
146
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181







-
-
-
-
-
-
-
-
-
-
















-
-
-
-
-
-
-
-
-
-

-



-
+





+
+
+
+
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+







			return
		}
		a_pe[seq] = abcplay.clear()	// keep the playing events
	}
	abcplay.play(0, 100000, a_pe[seq])
}

// function called when the page is loaded
function dom_loaded() {

	// loop until abc2svg is fully loaded
	if (typeof abc2svg != "object"
	 || !abc2svg.modules) {
		setTimeout(dom_loaded, 500)
		return
	}

// function to load javascript files
	abc2svg.loadjs = function(fn, relay, onerror) {
		var s = document.createElement('script');
		if (/:\/\//.test(fn))
			s.src = fn		// absolute URL
		else
			s.src = jsdir + fn;
		s.type = 'text/javascript'
		if (relay)
			s.onload = relay;
		s.onerror = onerror || function() {
			alert('error loading ' + fn)
		}
		document.head.appendChild(s)
	}

	page = document.body.innerHTML;

	// accept page formatting
	abc2svg.abc_end = function() {}

	// load the required modules
	if (abc2svg.modules.load(page, render))
		render()
}

function render() {
	page = document.body.innerHTML;

	// search the ABC tunes,
	// replace them by SVG images with play on click
	var	i = 0, j, k, res, src,
    var	i = 0, j, k, res, abc,
		seq = 0,
		re = /\n%abc|\nX:/g,
		re_stop = /\nX:|\n<|\n%.begin/g,
		select = window.location.hash.slice(1);

	// aweful hack: user.anno_stop must be defined before Abc creation
	// for being set later by follow() !
	if (typeof follow == "function")
		user.anno_stop = function(){};

	abc = new abc2svg.Abc(user)

	// handle MEI files
	j = page.indexOf("<mei ")
	if (j >= 0) {
		k = page.indexOf("</mei>") + 6
		abc.mei2mus(page.slice(j, k))
		document.body.innerHTML = new_page
		// no play yet
		return
	}

	// initialize the play follow function
	if (typeof follow == "function")
		follow(abc, user, playconf)

	// check if a selection
	if (select) {
		select = decodeURIComponent(select);
		select = page.search(select)
		if (select < 0)
			select = 0
	}
︙
199
200
201
202
203
204
205
206

207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225


226
227

228
229
230
231
232
233
234
208
209
210
211
212
213
214

215
216
217
218
219
220
221
222
223
224
225
226
227

228





229
230
231
232
233
234
235
236
237
238
239
240







-
+












-

-
-
-
-
-
+
+


+







			k = re_stop.lastIndex - 2;

	    // selection
	    if (!select || page[j] != 'X' || (select >= j && select < k)) {

		// clicking on the music plays this tune
		if (page[j] == 'X') {
			new_page += '<div onclick="playseq(' +
			new_page += '<div onclick="abc2svg.playseq(' +
					a_src.length + ')">\n';
			a_src.push([j, k])
		} else if (!glop) {
			glop = [j, k]
		}

		try {
			abc.tosvg('abcemb', page, j, k)
		} catch (e) {
			alert("abc2svg javascript error: " + e.message +
				"\nStack:\n" + e.stack)
		}
		abc2svg.abc_end()		// close the page if %%pageheight
		if (errtxt) {
			i = page.indexOf("\n", j);
			i = page.indexOf("\n", i + 1);
			alert("Errors in\n" +
				page.slice(j, i) +
				"\n...\n\n" + errtxt);
			new_page += '<pre style="background:#ff8080">' +
					errtxt + "</pre>\n";
			errtxt = ""
		}
		abc2svg.abc_end()		// close the page if %%pageheight
		if (page[j] == 'X')
			new_page += '</div>\n'
	    } // selection

		i = k
		if (i >= page.length)
			break
︙
249
250
251
252
253
254
255






















256
257
258
259


255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285


286
287







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


-
-
+
+
	delete user.img_out;		// stop SVG generation
	old_gm = user.get_abcmodel;
	user.get_abcmodel = function(tsfirst, voice_tb, music_types, info) {
			if (old_gm)
				old_gm(tsfirst, voice_tb, music_types, info);
			abcplay.add(tsfirst, voice_tb)
		}
} // render()

	// --- dom_loaded() main code ---

	// get the page content
	page = document.body.innerHTML

	// load the abc2svg core if not done by <script>
	if (!abc2svg.Abc) {
		abc2svg.loadjs(page.indexOf("<mei ") >= 0 ?
					"mei2svg-1.js" :
					"abc2svg-1.js",
						dom_loaded)
		return
	}

	// accept page formatting
	abc2svg.abc_end = function() {}

	// load the required modules, then render the music
	if (abc2svg.modules.load(page, render))
		render()
} // dom_loaded()

// wait for the page to be loaded
document.addEventListener("DOMContentLoaded", dom_loaded, false)
// wait for the scripts to be loaded
window.addEventListener("load", function() {setTimeout(dom_loaded, 500)})
Added util/abcemb1.js.







































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
//#javascript
// abcemb1-1.js file to include in html pages with abc2svg-1.js
//
// This script either:
// - builds a list of the tunes when there is no selection or
// - displays the selected tune.
//
// When the tune is displayed, if playing is not enabled,
// scrolling the music may be done by clicking/taping
// on the 'start scrolling' button.
//
// The header of the tune list ("Tunes:") may be set in a global
// javascript variable 'list_head'.
//
// Copyright (C) 2019 Jean-Francois Moine
//
// This file is part of abc2svg.
//
// abc2svg is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

window.onerror = function(msg, url, line) {
	if (typeof msg == 'string')
		alert("window error: " + msg +
			"\nURL: " + url +
			"\nLine: " + line)
	else if (typeof msg == 'object')
		alert("window error: " + msg.type + ' ' + msg.target.src)
	else
		alert("window error: " + msg)
	return false
}

// remove the menu button on print
window.onbeforeprint = function() {
   var	e = document.getElementById("dd")
	if (e)
		e.style.display = "none"
}
window.onafterprint = function() {
   var	e = document.getElementById("dd")
	if (e)
		e.style.display = "block"
}

var abc2svg = {}

// function called when abc2svg is fully loaded
function dom_loaded() {
var	errtxt = '',
	app = "abcemb",
	new_page,
	playing,
	abcplay,

	tune_dur,			// scroll tune duration
	scroll_to,			// scroll timeout
	dt,				// scroll delta per timeout
	sY,				// current scroll Y

	page,				// document source
	pe,				// playing events
	jsdir = document.currentScript ?
		    document.currentScript.src.match(/.*\//) :
		    (function() {
		     var s_a = document.getElementsByTagName('script')
			for (var k = 0; k < s_a.length; k++) {
				if (s_a[k].src.indexOf('abcemb1-') >= 0)
					return s_a[k].src.match(/.*\//) || ''
			}
			return ""	// ??
	})(),

// -- abc2svg init argument
    user = {
	errmsg: function(msg, l, c) {	// get the errors
		errtxt += clean_txt(msg) + '\n'
	},

	// function called before SVG generation
	get_abcmodel: function(tsfirst, voice_tb) {
	    var	d, i, n, pf,
		s = tsfirst

		while (1) {
			if (s.tempo && !pf) {
				d = 0;
				n = s.tempo_notes.length
				for (i = 0; i < n; i++)
					d += s.tempo_notes[i];
				pf = d * s.tempo / 60
			}
			if (!s.ts_next)
				break
			s = s.ts_next
		}
		if (!pf)
			pf = abc2svg.C.BLEN / 8;	// default: Q:1/4=120
//			     abc2svg.C.BLEN / 4 * 120 / 60
		tune_dur = s.time / pf
	},

	img_out: function(str) {	// image output
		new_page += str
	},

	page_format: true		// define the non-page-breakable blocks
    };

// play arguments
    playconf = {
	onend: function() {
		playing = false
	}
    }

// replace <>& by XML character references
function clean_txt(txt) {
	return txt.replace(/<|>|&.*?;|&/g, function(c) {
		switch (c) {
		case '<': return "&lt;"
		case '>': return "&gt;"
		case '&': return "&amp;"
		}
		return c
	})
}

// scroll the displayed music
function do_scroll(old) {

	// if start, get the window parameters and delay the first scroll
	if (!old) {
	    var d = document.documentElement;

		// time for scrolling one pixel
		dt = tune_dur / d.scrollHeight

		// start scrolling at this time (1/4 of the first screen)
	    var	ttop = dt * d.clientHeight / 4
		document.getElementById("ss").style.display = "block"
		scroll_to = setTimeout(do_scroll, ttop * 1000, 1)

		// (in Android Browser, remove the address bar)
		window.scrollTo(0, 8);		// go to the top
		sY = 0;
	} else {
		if (sY == window.pageYOffset) {	// no scroll -> finished
			document.getElementById("ss").style.display = "none"
			scroll_to = null
			return
		}
		sY = window.pageYOffset;
		window.scrollTo(0, sY + 1);
		scroll_to = setTimeout(do_scroll, dt * 1000, 1)
	}
}

// menu functions
// click inside/outside of the menu button
window.onmouseup = function(event) {
    var	e = document.getElementById("dc")
	if (e) {
		if (event.target.className == "db")
			e.classList.toggle("show")
		else if (e.classList.contains("show"))
			e.classList.remove("show")
	}
}

// source edit
abc2svg.src_upd = function() {
	page = document.getElementById('ta').value
	abc2svg.get_sel()
} // src_upd()

abc2svg.src_edit = function() {
	// offer a textarea with the ABC source and 2 buttons
	document.body.innerHTML = '\
<textarea id="ta" rows="50" cols="80">' + page + '</textarea>\
<br/>\
<a href="#" onclick="abc2svg.src_upd()"> Apply </a> - \
<a href="#" onclick="abc2svg.get_sel()"> Cancel </a>'
} // src_edit()

// start/stop scrolling when no play
abc2svg.st_scroll = function() {
	if (scroll_to) {
		clearTimeout(scroll_to);
		document.getElementById("ss").style.display = "none"
		scroll_to = null
	} else {
		scroll_to = setTimeout(do_scroll, 500, 0)	// scroll start
	}
}

// function called on click in the music:
//	start / stop playing and scrolling
abc2svg.playseq = function(select) {
    var	outputs

	if (!abcplay) {				// if first time
		delete user.img_out;		// stop SVG generation
		user.get_abcmodel = function(tsfirst, voice_tb) {
			abcplay.add(tsfirst, voice_tb)
		}
		abcplay = AbcPlay(playconf)
	}

	// stop
	if (playing) {
		if (scroll_to) {			// if scrolling active
			clearTimeout(scroll_to);	// stop it
			scroll_to = null
		}
		abcplay.stop();
		return
	}

	playing = true
	if (!pe) {			// if no playing event
		var abc = new abc2svg.Abc(user);

		abcplay.clear();
		abc.tosvg("play", "%%play")
		if (select)
			abc.tosvg(app, "%%select " + select)
		try {
			abc.tosvg(app, page)
		} catch(e) {
			alert(e.message + '\nabc2svg tosvg bug - stack:\n' + e.stack);
			playing = false;
			pe = null
			return
		}
		pe = abcplay.clear()		// keep the playing events
	}
	if (document.documentElement.scrollHeight > window.innerHeight)
		scroll_to = setTimeout(do_scroll, 500, 0);	// scroll start
	abcplay.play(0, 100000, pe)
} // playseq()

// function to load javascript files
	abc2svg.loadjs = function(fn, relay, onerror) {
		var s = document.createElement('script');
		if (/:\/\//.test(fn))
			s.src = fn		// absolute URL
		else
			s.src = jsdir + fn;
		s.type = 'text/javascript'
		if (relay)
			s.onload = relay;
		s.onerror = onerror || function() {
			alert('error loading ' + fn)
		}
		document.head.appendChild(s)
	} // loadjs()

// build a list of the tunes
abc2svg.get_sel = function() {
    var	j, k,
	n = 0,
	i = 0,
	t = (typeof list_head == "undefined" ? "Tunes:" : list_head) + '<ul>\n'

	// --- get_sel() main ---
	for (;;) {
		i = page.indexOf("\nX:", i)
		if (i < 0)
			break
		j = page.indexOf("\nT:", ++i)
		if (j < 0)
			break
		n++;
		t += '<li><a \
style="cursor:pointer;color:blue;text-decoration:underline" \
onclick="abc2svg.do_render(\'' + page.slice(i, j) + '\')">'
		k = page.indexOf("\n", j + 1);
		t += page.slice(j + 3, k)
		if (page[k + 1] == 'T' && page[k + 2] == ':') {
			j = k + 3;
			k = page.indexOf("\n", j)
			if (k > 0)
				t += " - " + page.slice(j, k)
		}
		t += '</a></li>\n';
		i = k
	}
	if (n <= 1) {
		abc2svg.do_render()
		return
	}

	t += '</ul>';

	document.body.innerHTML = t
} // get_sel()

// search/ask the tune to be rendered
function render() {
    var	select = window.location.hash.slice(1)		// after '#'

	// create styles for the menu
   var	sty = document.createElement('style')
	sty.innerHTML = '\
.dd{position:fixed;top:0;bottom:0;right:0;height:40px;cursor:pointer;font-size:16px}\
#ss{display:none;background-color:red}\
.db{display:block;margin:5px; padding:5px;background-color:yellow}\
.db:hover,.db:focus{background-color:lightgreen}\
.dc{position:absolute;left:-70px;min-width:100px;display:none;background-color:yellow}\
.dc label{display:block;padding:0 5px 0 5px;margin:2px}\
.dc label:hover{outline:solid;outline-width:2px}\
.show{display:block}'
	document.head.appendChild(sty)

	// if no selection and many tunes, get the references of the tunes
	// and ask which one to display
	if (!select)
		abc2svg.get_sel()
	else
		abc2svg.do_render(decodeURIComponent(select))
} // render()

// replace the (previous) body by the music
abc2svg.do_render = function(select) {

	// aweful hack: user.anno_stop must be defined before Abc creation
	// for being set later by follow() !
	if (typeof follow == "function")
		user.anno_stop = function(){};

	abc = new abc2svg.Abc(user)
	new_page = ""

	// initialize the play follow function
	if (typeof follow == "function")
		follow(abc, user, playconf)

	if (select) {
		if (typeof AbcPlay != "undefined")
			new_page += '<div onclick="abc2svg.playseq(\'' +
					select + '\')">'
		abc.tosvg(app, "%%select " + select)
	} else if (typeof AbcPlay != "undefined") {
		new_page += '<div onclick="abc2svg.playseq()">'
	}
	try {
		abc.tosvg(app, page)
	} catch (e) {
		alert("abc2svg javascript error: " + e.message +
			"\nStack:\n" + e.stack)
	}
	if (typeof AbcPlay != "undefined")
		new_page += '</div>'
	if (errtxt) {
		new_page += '<pre style="background:#ff8080">' +
				errtxt + "</pre>\n";
		errtxt = ""
	}

	// add the menu
	new_page += '\
<div id="dd" class="dd">\
<label class="db">|||</label>\
<div id="dc" class="dc">\
<label id="edit" onclick="abc2svg.src_edit()">Source edit</label>\
<label id="list" onclick="abc2svg.get_sel()">Tune list</label>\
<label id="play" onclick="abc2svg.st_scroll()">Scroll</label>\
</div>\
</div>\
<label id="ss" class="dd" onclick="abc2svg.st_scroll()">Scroll<br/>stop</label>'

	// change the page
	try {
		document.body.innerHTML = new_page
	} catch (e) {
		alert("abc2svg bad generated SVG: " + e.message +
			"\nStack:\n" + e.stack)
		return
	}

	// update the menu
	setTimeout(function() {

		// remove scroll in menu if play or not a big tune
		if (typeof AbcPlay != "undefined"
		 || document.documentElement.scrollHeight <= window.innerHeight)
			document.getElementById("play").style.display = "none"
	}, 500)

} // render()

	// --- dom_loaded() main code ---

	// load the abc2svg core if not done by <script>
	if (!abc2svg.Abc) {
		abc2svg.loadjs("abc2svg-1.js", dom_loaded)
		return
	}

	// get the page content
	page = document.body.innerHTML;

	// accept page formatting
	abc2svg.abc_end = function() {}

	// load the required modules, then render the music
	if (abc2svg.modules.load(page, render))
		render()
} // dom_loaded()

// wait for the scripts to be loaded
window.addEventListener("load", function() {setTimeout(dom_loaded, 500)})
Changes to util/abcemb2.js.
︙
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52







53
54
55

56
57
58
59
60
61
62
63

64
65
66
67
68
69
70
27
28
29
30
31
32
33

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48



49
50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73







-















-
-
-
+
+
+
+
+
+
+


-
+







-
+







		alert("window error: " + msg.type + ' ' + msg.target.src)
	else
		alert("window error: " + msg)
	return false
}

var	errtxt = '',
	abc,				// (must be global for follow.js)
	elts,				// ABC HTML elements
	tunes = '',			// source of the ABC sequences
	indx = [],			// indexes of the tunes in tunes
	select,
	playing,
	abcplay,
	playconf = {
		onend: endplay
	},
	a_pe = [],			// index: #sequence, value: playing events
	glop,				// global sequence index for play
	old_gm,
	jsdir = document.currentScript ?
		document.currentScript.src.match(/.*\//) :
		(function() {
			var scrs = document.getElementsByTagName('script');
			return scrs[scrs.length - 1].src.match(/.*\//) || ''
		})()
		     var s_a = document.getElementsByTagName('script')
			for (var k = 0; k < s_a.length; k++) {
				if (s_a[k].src.indexOf('abcemb2-') >= 0)
					return s_a[k].src.match(/.*\//) || ''
			}
			return ""	// ??
		})(),

// -- abc2svg init argument
var user = {
    user = {
	errmsg: function(msg, l, c) {	// get the errors
		errtxt += clean_txt(msg) + '\n'
	},
	img_out: function(str) {	// image output
		new_page += str
	},
	page_format: true		// define the non-page-breakable blocks
}
    }

// replace <>& by XML character references
function clean_txt(txt) {
	return txt.replace(/<|>|&.*?;|&/g, function(c) {
		switch (c) {
		case '<': return "&lt;"
		case '>': return "&gt;"
︙
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
118
119
120
121
122
123
124







125
126
127
128
129
130
131







-
-
-
-
-
-
-







	}
	abcplay.play(0, 100000, a_pe[i])
}

// function called when the page is loaded
function dom_loaded() {

	// loop until abc2svg is fully loaded
	if (typeof abc2svg != "object"
	 || !abc2svg.modules) {
		setTimeout(dom_loaded, 500)
		return
	}

	// convert HTML to ABC
	function toabc(s) {
		return s.replace(/&gt;/g, '>')
			.replace(/&lt;/g, '<')
			.replace(/&amp;/g, '&')
			.replace(/[ \t]+(%%)/g, '$1')
			.replace(/[ \t]+(.:)/g, '$1')
︙
159
160
161
162
163
164
165
166

167
168
169
170
171
172
173
174
175
176
177
178





179
180




181
182

183
184
185
186
187
188
189
155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186

187
188
189
190
191
192
193
194







-
+












+
+
+
+
+


+
+
+
+

-
+







		tunes += toabc(elt.innerHTML) + '\n'
	}
	indx[i] = tunes.length;
	ready()
}

function ready() {
    var	i, j
    var	i, j, abc

	// load the required modules
	if (!abc2svg.modules.load(tunes, ready))
		return

	// accept page formatting
	abc2svg.abc_end = function() {}

	var sel = window.location.hash.slice(1)
	if (sel)
		select = '%%select ' + decodeURIComponent(sel);

	// aweful hack: user.anno_stop must be defined before Abc creation
	// for being set later by follow() !
	if (typeof follow == "function")
		user.anno_stop = function(){};

	abc = new abc2svg.Abc(user)

	// initialize the play follow function
	if (typeof follow == "function")
		follow(abc, user, playconf)

	// generate and replace
	for (var i = 0; i < elts.length; i++) {
	for (i = 0; i < elts.length; i++) {
		new_page = ""

		// set the playing callback
		j = tunes.indexOf('X:', indx[i])
		if (j >= 0 && j < indx[i + 1])
			new_page += '<div onclick="playseq(' + i + ')">\n'
		else if (glop == undefined)
︙
197
198
199
200
201
202
203
204
205


206
207
208
209
210
211
212
202
203
204
205
206
207
208


209
210
211
212
213
214
215
216
217







-
-
+
+







		try {
			abc.tosvg('abcemb2', tunes, indx[i], indx[i + 1])
		} catch (e) {
			alert("abc2svg javascript error: " + e.message +
				"\nStack:\n" + e.stack)
		}
		if (errtxt) {
			new_page += '<p style="background:#ff8080">' +
					errtxt + "</p>\n";
			new_page += '<pre style="background:#ff8080">' +
					errtxt + "</pre>\n";
			errtxt = ""
		}
		try {
			elts[i].innerHTML = new_page
		} catch (e) {
			alert("abc2svg bad generated SVG: " + e.message +
				"\nStack:\n" + e.stack)
︙
224
225
226
227
228
229
230
231

229
230
231
232
233
234
235

236







-
+
			if (old_gm)
				old_gm(tsfirst, voice_tb, music_types, info);
			abcplay.add(tsfirst, voice_tb)
		}
} // dom_loaded()

// wait for the page to be loaded
document.addEventListener("DOMContentLoaded", dom_loaded, false)
window.addEventListener("load", function() {setTimeout(dom_loaded, 500)})
Added util/abcweb2.js.







































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
//#javascript
// abcemb2-1.js file to include in html pages with abc2svg-1.js
//
// Copyright (C) 2018-2019 Jean-Francois Moine
//
// This file is part of abc2svg.
//
// abc2svg is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

window.onerror = function(msg, url, line) {
	if (typeof msg == 'string')
		alert("window error: " + msg +
			"\nURL: " + url +
			"\nLine: " + line)
	else if (typeof msg == 'object')
		alert("window error: " + msg.type + ' ' + msg.target.src)
	else
		alert("window error: " + msg)
	return false
}

if (typeof abc2svg == "undefined")
    var	abc2svg = {}

// function called when abc2svg is fully loaded
function dom_loaded() {
    var	abc, i, elt,
	errtxt = '',
	elts,				// ABC HTML elements
	abcsrc = "",			// ABC source
	indx = [],			// indexes of the tunes in abcsrc
	select,
	playing,
	abcplay,
	playconf = {
		onend: function() {
			playing = false
		}
	},
	tune_lst,		// array of [tsfirst, voice_tb] per tune
	jsdir = document.currentScript ?
		    document.currentScript.src.match(/.*\//) :
		    (function() {
		     var s_a = document.getElementsByTagName('script')
			for (var k = 0; k < s_a.length; k++) {
				if (s_a[k].src.indexOf('abcweb2-') >= 0)
					return s_a[k].src.match(/.*\//) || ''
			}
			return ""	// ??
		})(),

	// abc2svg init argument
	user = {
		errmsg: function(msg, l, c) {	// get the errors
			errtxt += clean_txt(msg) + '\n'
		},
		img_out: function(str) {	// image output
			if (str.slice(0, 4) == "<svg")
				new_page += '<svg class="abc' +
					abc.tunes.length + '"' +
					str.slice(4)
			else
				new_page += str
		},
		page_format: true	// define the non-page-breakable blocks
	} // user

	// replace <>& by XML character references
	function clean_txt(txt) {
		return txt.replace(/<|>|&.*?;|&/g, function(c) {
			switch (c) {
			case '<': return "&lt;"
			case '>': return "&gt;"
			case '&': return "&amp;"
			}
			return c
		})
	} // clean_txt()

	// function called on click in the screen
	abc2svg.playseq = function(evt) {
	    var	outputs, e, i,
		tunes = abc.tunes,
		svg = evt.target

		// search if click in a SVG image
		while (svg.tagName != 'svg') {
			svg = svg.parentNode
			if (!svg)
				return
		}
		i = svg.getAttribute('class').match(/abc(\d)/)
		if (!i)
			return
		i = i[1] - 1			// tune number

		// initialize the play object
		if (!abcplay) {

			// if play-1.js is not loaded, don't come here anymore
			if (typeof AbcPlay == "undefined") {
				abc2svg.playseq = function(){}
				return
			}
			abcplay = AbcPlay(playconf)
		}
		if (playing) {
			abcplay.stop()
			return
		}

		// if first time, get the tunes references
		// and generate the play data of all tunes
		if (tunes.length) {
			tune_lst = tunes.slice(0)	// (array copy)
			while (1) {
				e = tunes.shift()
				if (!e)
					break
				abcplay.add(e[0], e[1])
			}
		}

		playing = true
		abcplay.play(tune_lst[i][0], null)
	} // playseq()

	function render() {
	    var	i, sel

		// aweful hack: user.anno_stop must be defined before Abc creation
		// for being set later by follow() !
		if (typeof follow == "function")
			user.anno_stop = function(){}

		abc = new abc2svg.Abc(user)

		// initialize the play follow function
		if (typeof follow == "function")
			follow(abc, user, playconf)

		// do the selection
		sel = window.location.hash.slice(1)
		if (sel) {
			select = '%%select ' + decodeURIComponent(sel)
			abc.tosvg("abcemb2", select)
		}

		// generate and replace
		for (i = 0; i < elts.length; i++) {
			new_page = ""

			try {
				abc.tosvg('abcemb2', abcsrc, indx[i], indx[i + 1])
			} catch (e) {
				alert("abc2svg javascript error: " + e.message +
					"\nStack:\n" + e.stack)
			}
			if (errtxt) {
				new_page += '<pre style="background:#ff8080">' +
						errtxt + "</pre>\n"
				errtxt = ""
			}
			try {
				elts[i].innerHTML = new_page
			} catch (e) {
				alert("abc2svg bad generated SVG: " + e.message +
					"\nStack:\n" + e.stack)
			}
		}
		abc2svg.abc_end()		// close the page if %%pageheight
	} // render()

	// convert HTML to ABC
	function toabc(s) {
		return s.replace(/&gt;/g, '>')
			.replace(/&lt;/g, '<')
			.replace(/&amp;/g, '&')
			.replace(/[ \t]+(%%)/g, '$1')
			.replace(/[ \t]+(.:)/g, '$1')
	} // toabc()

	// function to load javascript files
	abc2svg.loadjs = function(fn, relay, onerror) {
	    var	s = document.createElement('script')
		if (/:\/\//.test(fn))
			s.src = fn		// absolute URL
		else
			s.src = jsdir + fn
		s.type = 'text/javascript'
		if (relay)
			s.onload = relay
		s.onerror = onerror || function() {
			alert('error loading ' + fn)
		}
		document.head.appendChild(s)
	}

	// accept page formatting
	abc2svg.abc_end = function() {}

	// prepare for play on click
	window.onclick = abc2svg.playseq

	// extract the ABC source
	elts = document.getElementsByClassName('abc')
	for (i = 0; i < elts.length; i++) {
		elt = elts[i]
		indx[i] = abcsrc.length
		abcsrc += toabc(elt.innerHTML) + '\n'
	}
	indx[i] = abcsrc.length

	// load the required modules
	if (abc2svg.modules.load(abcsrc, render))
		render()
} // dom_loaded()

// wait for the page to be loaded
window.addEventListener("load", dom_loaded)
Changes to util/follow.js.
︙
15
16
17
18
19
20
21


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45





46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

61
62

63
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42





43
44
45
46
47










48
49
50
51

52


53








+
+



















-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-




-
+
-
-
+
-
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

// init
function follow(abc, user, playconf) {
var	ref = [],
	ignore_types = {
		beam:true,
		slur:true,
		tuplet:true
	}

user.anno_stop = function(type, start, stop, x, y, w, h) {
	if (ignore_types[type])
		return
	ref[start] = stop;		// keep the source reference

	// create a rectangle
	abc.out_svg('<rect class="abcr _' + start + '_" x="');
	abc.out_sxsy(x, '" y="', y);
	abc.out_svg('" width="' + w.toFixed(2) +
		'" height="' + h.toFixed(2) + '"/>\n')
}

function notehlight(i, on) {
	var elts = document.getElementsByClassName('_' + i + '_')
	if (elts && elts[0])
		elts[0].style.fillOpacity = on ? 0.4 : 0
}
	playconf.onnote = function(i, on) {
		var elts = document.getElementsByClassName('_' + i + '_')
		if (elts && elts[0])
			elts[0].style.fillOpacity = on ? 0.4 : 0
	}

// init
function follow() {

	// wait until the driver is loaded
	if (!playconf) {
		setTimeout(follow, 1000)
		return
	}
	playconf.onnote = notehlight

	// create the style of the rectangles
	var sty = document.createElement("style");
	sty.innerHTML = ".abcr {fill: #d00000; fill-opacity: 0; z-index: 15}";
	document.body.appendChild(sty)
	document.head.appendChild(sty)
}

} // follow()
setTimeout(follow, 1000)
Changes to util/json.js.
︙
36
37
38
39
40
41
42



43
44
45
46
47
48
49
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52







+
+
+







		links = {
			next: true,
			prev: true,
			ts_next: true,
			ts_prev: true,
			extra: true,
			p_v: true,
			s: true,
			sn: true,
			tie_s: true,
			dd_st: true
		}
	// generate an attribute
	function attr_gen(ind, attr, val) {
		var	i, e,
			indn = ind + inb	// next indentation

︙
Changes to util/midi.js.
1
2
3
4

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21



22
23
24
25
26
27
28
29
30
31
32

33
34
35
36
37
38
39
40
41
42

43
44
45
46




47
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

83
84
85
86
87
88
89
90
91
92
93
94
95
96



97








98
99
100
101







102
103
104


105
106

107
108
109
110
111



112
113
114

115
116
117
118




119
120
121
122
123
124






125
126

127
128
129
130


131
132
133
134
135
136

137
138
139
140
141
142
143
144
145
146
147
148
149
150




151
152

153
154
155
156
157
158
159


160
161
162

163
164
165
166
167
168
169
170


171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190


191
1
2
3

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31




32
33
34
35
36
37
38
39
40
41
42
43




44
45
46
47









48







49




















50











51


52
53
54
55
56
57
58
59
60
61
62
63




64
65
66
67
68
69
70



71
72


73





74
75
76



77
78



79
80
81
82






83
84
85
86
87
88
89

90




91
92






93









94




95
96
97
98


99
100
101
102




103
104



105

106



107


108
109














110
111
112
113
114

115
116
117



-
+

















+
+
+







-
-
-
-
+










+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-

-
-
+
+
+

+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
-
-
+
-
-
-
-
-
+
+
+
-
-
-
+

-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+

-
+
-
-
-
-
+
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-

-
-
-
-
+
+
+
+
-
-
+



-
-
-
-
+
+
-
-
-
+
-

-
-
-

-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-





-
+
+

//#javascript
// Set the MIDI pitches in the notes
//
// Copyright (C) 2015-2017 Jean-Francois Moine
// Copyright (C) 2015-2019 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg-core is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

// Usage:
//	// Create a AbcMIDI object
//	var abcmidi = AbcMIDI()
//
//	// Define a get_abcmodel() callback function
//	// This one is called by abc2svg after ABC parsing 
//	user.get_abcmodel = my_midi_callback
//
//	// In this function
//	function my_midi_callback(tsfirst, voice_tb, music_types, info) {
//
//		// Create a AbcMIDI instance
//		var abcmidi = new AbcMIDI();
//
//		// and set the MIDI pitches
//		// set the MIDI pitches
//		abcmidi.add(tsfirst, voice_tb);
//
//		// The MIDI pitches are stored in the notes
//		//	s.notes[i].midi
//	}

// AbcMIDI creation
function AbcMIDI() {
    var	C = abc2svg.C

	return {					// returned object
	// add MIDI pitches
	AbcMIDI.prototype.add = function(s,		// starting symbol
					voice_tb) {	// voice table

	  // add MIDI pitches
	  add: function(s,				// starting symbol
			voice_tb) {			// voice table
	    var p_v, s,
		var	scale = new Int8Array(		// note to pitch
					[0, 2, 4, 5, 7, 9, 11]),
			bmap = new Int8Array(7),	// measure base map
			map = new Int8Array(70),	// current map - 10 octaves
			tie_map,			// index = MIDI pitch
			tie_time,
			v,
			transp				// clef transpose

		temper = voice_tb[0].temper,		// (set by the module temper.js)
		// re-initialize the map on bar
		function bar_map() {
			for (var j = 0; j < 10; j++)
				for (var i = 0; i < 7; i++)
					map[j * 7 + i] = bmap[i]
		} // bar_map()

		v = voice_tb.length
		// define the note map
		function key_map(s) {
			for (var i = 0; i < 7; i++)
				bmap[i] = 0
			switch (s.k_sf) {
			case 7: bmap[6] = 1
			case 6: bmap[2] = 1
			case 5: bmap[5] = 1
			case 4: bmap[1] = 1
			case 3: bmap[4] = 1
			case 2: bmap[0] = 1
			case 1: bmap[3] = 1; break
			case -7: bmap[3] = -1
			case -6: bmap[0] = -1
			case -5: bmap[4] = -1
			case -4: bmap[1] = -1
			case -3: bmap[5] = -1
			case -2: bmap[2] = -1
			case -1: bmap[6] = -1; break
			}

			bar_map()
		} // key_map()

		// convert ABC pitch to MIDI
		function pit2midi(p, a) {
			if (a)
				map[p] = a == 3 ? 0 : a; // (3 = natural)
			return ((p / 7) | 0) * 12 + scale[p % 7] +
					(tie_time[p] ? tie_map[p] :  map[p])
		} // pit2midi()

		// initialize the clefs and keys
		for (v = 0; v < voice_tb.length; v++) {
			if (!voice_tb[v].sym)
		while (--v >= 0) {
			p_v = voice_tb[v]
			if (!p_v.sym)
				continue
			if (p_v.key.k_bagpipe)
	// detune in cents for just intonation in A
	//  C    ^C     D    _E     E     F    ^F     G    _A     A    _B     B
	// 15.3 -14.0  -2.0 -10.0   1.9  13.3 -16.0 -31.8 -12.0   0.0  11.4   3.8
	// (C is ^C,			F is ^F and G is =G)
	// 86				 84
	// temp = [100-14, -14, -2, -10, 2, 100-16, -16, -32, -12, 0, 11, 4]
	// but 'A' bagpipe = 480Hz => raise Math.log2(480/440)*1200 = 151
			s = voice_tb[v].clef
			if (!s.clef_octave
			 || s.clef_oct_transp)
				transp = 0
				temper = new Float32Array([
	2.37, 1.37, 1.49, 1.41, 1.53, 2.35, 1.35, 1.19, 1.39, 1.51, 1.62, 1.55
				])

			s = p_v.clef
			vloop(p_v.sym,
				p_v.key.k_sndtran || 0,
			else
				transp = s.clef_octave

				s.clef_octave && !s.clef_oct_transp ?
					(s.clef_octave / 7 * 40) : 0)
			key_map(voice_tb[v].key);	// init acc. map from key sig.

		}
			// and loop on the symbols of the voice
			vloop(v)
		}
	    function vloop(v) {
		var	i, g, p, note,

	    function vloop(s, sndtran, ctrans) {
		var	i, g, note,
			s = voice_tb[v].sym,
			vtime = s.time,		// next time
			rep_tie_map = []
			transp = sndtran + ctrans

		tie_map = []
		tie_time = []
		while (s) {
		function midi_set(note) {
		    var m = abc2svg.b40m(note.b40 + transp)
			if (temper		// if not equal temperament
			 && (!note.acc
			if (s.time > vtime) {	// if time skip
				bar_map()	// force a measure bar
				vtime = s.time
			}
			if (s.dur)
				vtime = s.time + s.dur
			  || note.acc | 0 == note.acc)) // and not micro-tone
				m += temper[m % 12]
			note.midi = m
		}

		while (s) {
			switch (s.type) {
			case C.BAR:
			case C.CLEF:
//fixme: pb when lack of measure bar (voice overlay, new voice)
				// x times repeat
				if (s.text) {
					if (s.text[0] == '1') {	// 1st time
				ctrans = (s.clef_octave && !s.clef_oct_transp) ?
						(s.clef_octave / 7 * 40) : 0
						rep_tie_map = [];
						rep_tie_time = []
						for (i = 0; i < tie_map.length; i++)
							rep_tie_map[i] = tie_map[i]
					} else if (rep_tie_map.length != 0) {
						tie_map = []
				transp = ctrans + sndtran
						tie_time = []
						for (i = 0; i < rep_tie_map.length; i++) {
							tie_map[i] = rep_tie_map[i];
							tie_time[i] = s.time
						}
					}
				}
				if (!s.invis)
					bar_map()
				break
			case C.CLEF:
				if (!s.clef_octave
				 || s.clef_oct_transp)
					transp = 0
			case C.KEY:
				if (s.k_sndtran != undefined) {
					sndtran = s.k_sndtran
					transp = ctrans + sndtran
				else
					transp = s.clef_octave
				}
				break
			case C.GRACE:
				for (g = s.extra; g; g = g.next) {
					if (!g.type != C.NOTE)
						continue
					for (i = 0; i <= g.nhd; i++) {
						note = g.notes[i];
					for (i = 0; i <= g.nhd; i++)
						midi_set(g.notes[i])
						p = note.pit + 19 + transp;
						note.midi = pit2midi(p, note.acc)
					}
				}
				}
				break
			case C.KEY:
				key_map(s)
				break
			case C.NOTE:
				for (i = 0; i <= s.nhd; i++) {
					note = s.notes[i];
				for (i = 0; i <= s.nhd; i++)
					midi_set(s.notes[i])
					p = note.pit + 19 +	// pitch from C-1
							transp
					if (tie_time[p]) {
						if (s.time > tie_time[p]) {
							delete tie_map[p]
							delete tie_time[p]
						}
					}
					note.midi = pit2midi(p, note.acc)
					if (note.ti1) {
						tie_map[p] = map[p];
						tie_time[p] = s.time + s.dur
					}
				}
				break
			}
			s = s.next
		}
	    } // vloop()
	} // add()
	  } // add()
	} // returned object
} // end AbcMidi
Changes to util/play.js.
︙
80
81
82
83
84
85
86
87

88
89
90

91
92
93
94
95
96
97
80
81
82
83
84
85
86

87
88
89
90
91
92
93
94
95
96
97
98







-
+



+







	function play2(out) {
	    var o

		if (!out)
			out = []
		o = audio5.get_outputs()	// get the HTML5 audio port
		if (o)
			out = out.concat(o)
			Array.prototype.push.apply(out, o)
		if (out.length == 0) {
			if (conf.onend)		// no output port
				conf.onend()
			return
		}
		if (out.length == 1) {
			o = 0			// only one port
		} else {
			o = -1			// ask which port?
			var pr = "Use"
			for (var i = 0; i < out.length; i++)
︙
143
144
145
146
147
148
149




144
145
146
147
148
149
150
151
152
153
154







+
+
+
+
	if (typeof Midi5 == "function")
		midi5 = Midi5(conf)
	if (typeof Audio5 == "function")
		audio5 = Audio5(conf);

	return abcplay
} // AbcPlay

// nodejs
if (typeof module == 'object' && typeof exports == 'object')
	exports.AbcPlay = AbcPlay
Added util/sndaud.js.
























































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// sndaud5.js - audio output using HTML5 audio
//
// Copyright (C) 2019 Jean-Francois Moine
//
// This file is part of abc2svg.
//
// abc2svg is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

// Audio5 creation

// @conf: configuration object - all items are optional:
//	ac: audio context - (default: created on play start)
//	sfu: soundfont URL (sf2 base64 encoded - default: "Scc1t2")
//	onend: callback function called at end of playing
//		Argument:
//			repv: last repeat variant number
//	onnote: callback function called on note start/stop playing
//		Arguments:
//			i: start index of the note in the ABC source
//			on: true on note start, false on note stop
//	instr_load: function to load the sound font of an instrument
//			(optional)
//		Arguments:
//			instr: MIDI instrument number
//			done: callback function in case of success
//				Arguments: soundfont in binary format
//			fail: callback function in case of error
//				no argument
//	errmsg: function called on error (default: alert)
//
//  When playing, the following items must/may be set:
//	gain: (mandatory) volume, must be set to [0..1]
//	speed: (mandatory) must be set to 1
//	new_speed: (optional) new speed value

// Audio5 methods

// get_outputs() - get the output devices
//	return ['sf2'] or null
//
// play() - start playing
// @start -
// @stop: start and stop music symbols
// @level: repeat variant (optional, default = 0)
//
// stop() - stop playing
//
// set_vol() - set the current sound volume
// @volume: range [0..1] - undefined = return current value

    var	abcsf2 = []			// SF2 instruments

function Audio5(i_conf) {
    var	C = abc2svg.C,
	conf = i_conf,		// configuration
	onend = function() {},
	onnote = function() {},
	errmsg = alert,
	ac,			// audio context
	gain,			// global gain

	// instruments/notes
	instr = [],		// [voice] bank + instrument
	params = [],		// [instr][key] note parameters per instrument
	rates = [],		// [instr][key] playback rates
	w_instr = 0,		// number of instruments being loaded

	// -- play the memorized events --
	s_cur,			// current music symbol
	s_end,			// last music symbol / null
	stop,			// stop playing
	repn,			// don't repeat when true
	repv,			// repeat level (variant number)
	stime,			// start playing time
	timouts = []		// note start events

	// default sound font load function
	if (!conf.instr_load) {
		conf.instr_load = function(instr, done, fail) {
			abc2svg.loadjs(conf.sfu + '/' + instr + '.js',
				function() {
					 done(b64dcod(abcsf2[instr]))
				},
				fail
			)
		}
	}

	// base64 stuff
    var b64d = []
	function init_b64d() {
	    var	b64l = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
		l = b64l.length
		for (var i = 0; i < l; i++)
			b64d[b64l[i]] = i
		b64d['='] = 0
	}
	function b64dcod(s) {
	    var	i, t, dl, a,
		l = s.length,
		j = 0

		dl = l * 3 / 4			// destination length
		if (s[l - 1] == '=') {
			if (s[l - 2] == '=')
				dl--
			dl--
			l -= 4
		}
		a = new Uint8Array(dl)
		for (i = 0; i < l; i += 4) {
			t =	(b64d[s[i]] << 18) +
				(b64d[s[i + 1]] << 12) +
				(b64d[s[i + 2]] << 6) +
				 b64d[s[i + 3]]
			a[j++] = (t >> 16) & 0xff
			a[j++] = (t >> 8) & 0xff
			a[j++] = t & 0xff
		}
		if (l != s.length) {
			t =	(b64d[s[i]] << 18) +
				(b64d[s[i + 1]] << 12) +
				(b64d[s[i + 2]] << 6) +
				 b64d[s[i + 3]]
			a[j++] = (t >> 16) & 0xff
			if (j < dl)
				a[j++] = (t >> 8) & 0xff
		}
		return a
	}

	// copy a sf2 sample to an audio buffer
	// @b = audio buffer (array of [-1..1])
	// @s = sf2 sample (PCM 16 bits)
	function sample_cp(b, s) {
	    var	i, n,
		a = b.getChannelData(0)		// destination = array of float32

		for (i = 0; i < s.length; i++)
			a[i] = s[i] / 196608	// volume divided by 6
	}

	// create all notes of an instrument
	function sf2_create(parser, instr) {
	    var i, sid, gen, parm, sampleRate, sample,
		infos = parser.getInstruments()[0].info

		rates[instr] = []
		for (i = 0; i < infos.length; i++) {
			gen = infos[i].generator
			if (!gen.sampleID)	// (empty generator!)
				continue
			sid = gen.sampleID.amount
			sampleRate = parser.sampleHeader[sid].sampleRate
			sample = parser.sample[sid]
			parm = {
				attack: Math.pow(2, (gen.attackVolEnv ?
					gen.attackVolEnv.amount : -12000) / 1200),
				hold: Math.pow(2, (gen.holdVolEnv ?
					gen.holdVolEnv.amount : -12000) / 1200),
				decay: Math.pow(2, (gen.decayVolEnv ?
					gen.decayVolEnv.amount : -12000) / 1200) / 3,
				sustain: gen.sustainVolEnv ?
					(gen.sustainVolEnv.amount / 1000) : 0,
//				release: Math.pow(2, (gen.releaseVolEnv ?
//					gen.releaseVolEnv.amount : -12000) / 1200),
				buffer: ac.createBuffer(1,
							sample.length,
							sampleRate)
			}
			parm.hold += parm.attack
			parm.decay += parm.hold

			// sustain > 40dB is not audible
			if (parm.sustain >= .4)
				parm.sustain = 0.01	// must not be null
			else
				parm.sustain = 1 - parm.sustain / .4

			sample_cp(parm.buffer, sample)

			if (gen.sampleModes && (gen.sampleModes.amount & 1)) {
				parm.loopStart = parser.sampleHeader[sid].startLoop /
					sampleRate
				parm.loopEnd = parser.sampleHeader[sid].endLoop /
					sampleRate
			}

			// define the notes
		    var scale = (gen.scaleTuning ?
					gen.scaleTuning.amount : 100) / 100,
			tune = (gen.coarseTune ? gen.coarseTune.amount : 0) +
				(gen.fineTune ? gen.fineTune.amount : 0) / 100 +
				parser.sampleHeader[sid].pitchCorrection / 100 -
				(gen.overridingRootKey ?
					gen.overridingRootKey.amount :
					parser.sampleHeader[sid].originalPitch)

			for (j = gen.keyRange.lo; j <= gen.keyRange.hi; j++) {
				rates[instr][j] = Math.pow(Math.pow(2, 1 / 12),
							(j + tune) * scale)
				params[instr][j] = parm
			}
		}
	} // sf2_create()

	// load an instrument (.js file)
	function load_instr(instr) {
		w_instr++
		conf.instr_load(instr,
			function(sf2_bin) {
			    var	parser = new sf2.Parser(sf2_bin)
				parser.parse()
				sf2_create(parser, instr)
				if (--w_instr == 0)
					play_start()
			},
			function() {
				errmsg('could not find the instrument ' +
					((instr / 128) | 0).toString() + '-' +
					(instr % 128).toString())
				if (--w_instr == 0)
					play_start()
			})
	} // load_instr()

	// start loading the instruments
	function load_res() {
	    var i,
		s = s_cur

		while (s) {
			i = s.instr
			if (i != undefined && !params[i]) {
				params[i] = []
				load_instr(i)
			}
			s = s.ts_next
		}
	}

	// create a note
	// @s = symbol
	// @key = MIDI key + detune
	// @t = audio start time
	// @d = duration adjusted for speed
	function note_run(s, key, t, d) {
	    var	g, st,
		instr = s.instr,
		parm = params[instr][key | 0],
		o = ac.createBufferSource()

		if (!parm)		// if the instrument could not be loaded
			return		// or if it has not this key
		o.buffer = parm.buffer
		if (parm.loopStart) {
			o.loop = true
			o.loopStart = parm.loopStart
			o.loopEnd = parm.loopEnd
		}
		if (o.detune) {
		    var	dt = (key * 100) % 100
			if (dt)			// if micro-tone
				 o.detune.value = dt
		}
//		o.playbackRate.setValueAtTime(parm.rate, ac.currentTime)
		o.playbackRate.value = rates[instr][key]

		g = ac.createGain()
		if (parm.hold < 0.002) {
			g.gain.setValueAtTime(1, t)
		} else {
			if (parm.attack < 0.002) {
				g.gain.setValueAtTime(1, t)
			} else {
				g.gain.setValueAtTime(0, t)
				g.gain.linearRampToValueAtTime(1, t + parm.attack)
			}
			g.gain.setValueAtTime(1, t + parm.hold)
		}

		g.gain.exponentialRampToValueAtTime(parm.sustain,
					t + parm.decay)

		o.connect(g)
		g.connect(gain)

		// start the note
		o.start(t)
		o.stop(t + d)
	} // note_run()

	// handle a tie
	function do_tie(s, b40, d) {
	    var	i, note,
		v = s.v,
		end_time = s.time + s.dur

		// search the end of the tie
		while (1) {
			s = s.ts_next
			if (!s)
				return d
			switch (s.type) {
			case C.BAR:
				if (s.rep_p) {
					if (!repn) {
						s = s.rep_p
						end_time = s.time
					}
				}
				if (s.rep_s) {
					if (!s.rep_s[repv + 1])
						return d
					s = s.rep_s[repv + 1]
					end_time = s.time
				}
				while (s.ts_next && s.ts_next.type == C.BAR)
					s = s.ts_next
			}
			if (s.time > end_time)
				return d
			if (s.type == C.NOTE && s.v == v)
				break
		}
		i = s.notes.length
		while (--i >= 0) {
			note = s.notes[i]
			if (note.b40 == b40) {
				note.ti2 = true		// the sound is generated
				d += s.pdur / conf.speed
				return note.tie_ty ? do_tie(s, b40, d) : d
			}
		}

		return d
	} // do_tie()

	// generate 2 seconds of music
	function play_next() {
	    var	d, i, st, m, note, g, s2, t, maxt,
		s = s_cur

		if (stop) {			// stop
			onend(repv)
			return
		}

		while (s.noplay) {
			s = s.ts_next
			if (!s || s == s_end) {
				onend(repv)
				return
			}
		}
		t = stime + s.ptim / conf.speed		// start time

		// if speed change, shift the start time
		if (conf.new_speed) {
			stime = ac.currentTime -
					(ac.currentTime - stime) *
						conf.speed / conf.new_speed
			conf.speed = conf.new_speed
			conf.new_speed = 0
			t = stime + s.ptim / conf.speed
		}

		maxt = t + 2			// max time = now + 2 seconds
		timouts = []
		while (1) {
			switch (s.type) {
			case C.BAR:
				if (s.bar_type.slice(-1) == ':') // left repeat
					repv = 0
				if (s.rep_p) {			// right repeat
					if (!repn) {
						stime += (s.ptim - s.rep_p.ptim) /
								conf.speed
						s = s.rep_p	// left repeat
						t = stime + s.ptim / conf.speed
						repn = true
						break
					}
					repn = false
				}
				if (s.rep_s) {			// first variant
					s2 = s.rep_s[++repv]	// next variant
					if (s2) {
						stime += (s.ptim - s2.ptim) /
								conf.speed
						s = s2
						t = stime + s.ptim / conf.speed
						repn = false
					} else {		// end of tune
						s = s_end
						break
					}
				}
				while (s.ts_next && s.ts_next.type == C.BAR)
					s = s.ts_next
				break
			case C.GRACE:
				for (g = s.extra; g; g = g.next) {
					d = g.pdur / conf.speed
					for (m = 0; m <= g.nhd; m++) {
						note = g.notes[m]
						note_run(g,
							note.midi,
							t + g.ptim - s.ptim,
//fixme: there may be a tie...
							d)
					}
				}
				break
			case C.NOTE:
				d = s.pdur / conf.speed
				for (m = 0; m <= s.nhd; m++) {
					note = s.notes[m]
					if (note.ti2)
						continue
					note_run(s,
						note.midi,
						t,
						note.tie_ty ? 
							do_tie(s, note.b40, d) : d)
				}
				// fall thru
			case C.REST:
				d = s.pdur / conf.speed

				// follow the notes/rests while playing
				i = s.istart
				st = (t - ac.currentTime) * 1000
				timouts.push(setTimeout(onnote, st, i, true))
				setTimeout(onnote, st + d * 1000, i, false)
				break
			}
			while (1) {
				if (s == s_end || !s.ts_next) {
					setTimeout(onend,
						(t - ac.currentTime + d) * 1000,
						repv)
					s_cur = s
					return
				}
				s = s.ts_next
				if (!s.noplay)
					break
			}
			t = stime + s.ptim / conf.speed	// next time
			if (t > maxt)
				break
		}
		s_cur = s

		// delay before next sound generation
		timouts.push(setTimeout(play_next, (t - ac.currentTime)
			* 1000 - 300))		// wake before end of playing
	} // play_next()

	// wait for all resources, then start playing
	function play_start() {
		if (stop) {			// stop playing
			onend(repv)
			return
		}

		// all resources are there
		gain.connect(ac.destination)
		stime = ac.currentTime + .2		// start time + 0.2s
			- s_cur.ptim * conf.speed
		play_next()
	} // play_start()

	// Audio5 function

	init_b64d()			// initialize base64 decoding

	if (!conf.sfu)
		conf.sfu = "Scc1t2"	// set the default soundfont location

    // public methods
    return {

	// get outputs
	get_outputs: function() {
		return (window.AudioContext || window.webkitAudioContext) ?
				['sf2'] : null
	}, // get_outputs()

	// play the symbols
	play: function(i_start, i_end, i_lvl) {

		// get the callback functions
		if (conf.onend)
			onend = conf.onend
		if (conf.onnote)
			onnote = conf.onnote
		if (conf.errmsg)
			errmsg = conf.errmsg

		// play a null file to unlock the iOS audio
		// This is needed for iPhone/iPad/...
		function play_unlock() {
		    var buf = ac.createBuffer(1, 1, 22050),
			src = ac.createBufferSource()

			src.buffer = buf
			src.connect(ac.destination)
			src.noteOn(0)
		}

		// initialize the audio subsystem if not done yet
		if (!gain) {
			ac = conf.ac
			if (!ac) {
				conf.ac = ac = new (window.AudioContext ||
							window.webkitAudioContext)
				if (/iPad|iPhone|iPod/.test(navigator.userAgent))
					play_unlock()
			}
			gain = ac.createGain()
			gain.gain.value = conf.gain
		}

		s_end = i_end
		stop = false
		while (i_start.noplay)
			i_start = i_start.ts_next
		s_cur = i_start
		repv = i_lvl || 0
		w_instr++
		load_res()
		if (--w_instr == 0)		// all resources are there
			play_start()
	}, // play()

	// stop playing
	stop: function() {
		stop = true
		timouts.forEach(function(id) {
					clearTimeout(id)
				})
		play_next()
		if (gain) {
			gain.disconnect()
			gain = null
		}
	}, // stop()

	// set volume
	set_vol: function(v) {
		if (gain)
			gain.gain.value = v
	} // set_vol()
    } // returned object
} // Audio5()
Added util/sndgen.js.































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// sndgen.js - sound generation
//
// Copyright (C) 2019 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg-core is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

// This script generates the play data which are stored in the music symbols:
// - in all symbols
//	s.ptim = play time
// - in BAR
//	rep_p = on a right repeat bar, pointer to the left repeat symbol
//	rep_s = on the first repeat variant, array of pointers to the next symbols,
//						indexed by the repeat number
// - in NOTE and REST
//	s.pdur = play duration
//	s.instr = bank + instrument
// - in the notes[] of NOTE
//	s.notes[i].midi

function ToAudio() {
 return {
   clear: function() {		// useless - for compatibility
   },

   // generate the play data of a tune
   add: function(s,		// starting symbol
		voice_tb) {	// voice table
    var	C = abc2svg.C,
	p_time = 0,		// last playing time
	abc_time = 0,		// last ABC time
	play_fac = C.BLEN / 4 * 120 / 60, // play time factor - default: Q:1/4=120
	i, n, dt, d, v,
	rst = s,		// left repeat (repeat restart)
	rst_fac,		// play factor on repeat restart
	rsk,			// repeat variant (repeat skip)
	instr = []		// [voice] bank + instrument

	// generate the grace notes
	function gen_grace(s) {
	    var	g, i, n, t, d, s2,
		next = s.next

		// before beat
		if (s.sappo) {
			d = C.BLEN / 16
		} else if ((!next || next.type != C.NOTE)
			&& s.prev && s.prev.type == C.NOTE) {
			d = s.prev.dur / 2

		// on beat
		} else {
			d = next.dur / 12
			if (d & (d - 1) == 0)
				d = next.dur / 2	// no dot
			else
				d = next.dur / 3
			next.time += d
			next.dur -= d
		}
//fixme: assume the grace notes have the same duration
		n = 0
		for (g = s.extra; g; g = g.next)
			n++
		d /= n * play_fac
		t = p_time
		for (g = s.extra; g; g = g.next) {
			g.ptim = t
			g.pdur = d
			g.instr = instr[s.v]
			t += d
		}
	} // gen_grace()

	// add() main

	// set the MIDI pitches
	AbcMIDI().add(s, voice_tb)

	// loop on the symbols
	rst = s
	rst_fac = play_fac
	while (s) {
		if (s.noplay) {			// in display macro sequence
			s = s.ts_next
			continue
		}
		if (s.tempo) {				// tempo change
			d = 0
			n = s.tempo_notes.length
			for (i = 0; i < n; i++)
				d += s.tempo_notes[i]
			play_fac = d * s.tempo / 60
		}

		dt = s.time - abc_time
		if (dt > 0) {
			p_time += dt / play_fac
			abc_time = s.time
		}
		s.ptim = p_time
		v = s.v

		if (instr[v] == undefined)
			instr[v] = voice_tb[v].instr || 0

		switch (s.type) {
		case C.BAR:

			// right repeat
			if (s.bar_type[0] == ':') {
				s.rep_p = rst		// :| to |:
				n = s.text		// variants
				while (s.ts_next && s.ts_next.type == C.BAR) {
					s = s.ts_next
					s.ptim = p_time
					if (!n && s.text)
						n = s.text
				}
				if (rsk) {		// if in a variant
					if (!n)
						n = "a"		// last time
					n = n.match(/[1-8]-[2-9]|[1-9,.]|[^\s]+$/g)
					while (1) {
						d = n.shift()
						if (!d)
							break
						if (d[1] == '-')
							for (i = d[0]; i <= d[2]; i++)
								rsk.rep_s[i] = s
						else if (d >= '1' && d <= '9')
							rsk.rep_s[Number(d)] = s
						else
							rsk.rep_s.push(s)
					}
					play_fac = rst_fac
				    break
				}
			}

			// left repeat
			if (s.bar_type.slice(-1) == ':') {
				rst = s
				rst_fac = play_fac

			// 1st time repeat
			} else if (s.text && s.text[0] == '1') {
				rsk = s
				s.rep_s = [null, s]	// repeat skip
			}
			break
		case C.BLOCK:
			switch (s.subtype) {
			case "midictl":
				switch (s.ctrl) {
				case 0:			// MSB bank
					instr[v] = (instr[v] & 0x3fff) |
						(s.val << 14)
					break
				case 32:		// LSB bank
					instr[v] = (instr[v] & 0x1fc07f) |
						(s.val << 7)
					break
//				case 121:		// reset all controllers
//					instr = []
//					break
				}
				break
			case "midiprog":
				instr[v] = (instr[v] & ~0x7f) | s.instr
				break
			}
			break
		case C.GRACE:
			if (s.time == 0		// if before beat at start time
			 && abc_time == 0) {
				dt = 0
				if (s.sappo)
					dt = C.BLEN / 16
				else if (!s.next || s.next.type != C.NOTE)
					dt = d / 2
				abc_time -= dt
			}
			gen_grace(s)
			break
		case C.REST:
		case C.NOTE:
			d = s.dur
			if (s.next && s.next.type == C.GRACE) {
				dt = 0
				if (s.next.sappo)
					dt = C.BLEN / 16
				else if (!s.next.next || s.next.next.type != C.NOTE)
					dt = d / 2
				s.next.time -= dt
				d -= dt
			}
			d /= play_fac
			s.pdur = d
			s.instr = instr[v]
			break
		}
		s = s.ts_next
	} // loop
   } // add()
 } // return
} // ToAudio()

// nodejs
if (typeof module == 'object' && typeof exports == 'object')
	exports.ToAudio = ToAudio
Added util/sndmid.js.














































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
// sndmid.js - audio output using HTML5 MIDI
//
// Copyright (C) 2019 Jean-Francois Moine
//
// This file is part of abc2svg.
//
// abc2svg is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

// Midi5 creation

// @conf: configuration object - all items are optional:
//	onend: callback function called at end of playing
//		Argument:
//			repv: last repeat variant number
//	onnote: callback function called on note start/stop playing
//		Arguments:
//			i: start index of the note in the ABC source
//			on: true on note start, false on note stop

//  When playing, the following items must/may be set:
//	speed: (mandatory) must be set to 1
//	new_speed: (optional) new speed value

// Midi5 methods

// get_outputs() - get the output ports
//
// set_output() - set the output port
//
// play() - start playing
// @start -
// @stop: start and stop music symbols
// @level: repeat variant (optional, default = 0)
//
// stop() - stop playing

function Midi5(i_conf) {
    var	C = abc2svg.C,
	conf = i_conf,		// configuration
	onend = function() {},
	onnote = function() {},
	rf,			// get_outputs result function

// MIDI variables
	op,			// output port
	v_i = [],		// voice (channel) to instrument

	s_cur,			// current music symbol
	s_end,			// last music symbol / null
	stop,			// stop playing
	repn,			// don't repeat when true
	repv = 0,		// repeat variant number
	stime,			// start playing time in ms
	timouts = []		// note start events

	// create a note
	// @s = symbol
	// @k = MIDI key + detune
	// @t = audio start time (ms)
	// @d = duration adjusted for speed (ms)
	function note_run(s, k, t, d) {
	    var	j,
		a = (k * 100) % 100,	// detune in cents
		i = s.instr,
//fixme: problem when more than 16 voices
		c = s.v & 0x0f		// channel

		k |= 0			// remove the detune value

		if ((s.instr & ~0x7f) == 16384)	// if bank 128 (percussion)
//fixme: may conflict with the voice 9
			c = 9			// force the channel 10
		if (i != v_i[c]) {		// if program change

			// at channel start, reset and initialize the controllers
			if (v_i[c] == undefined) {
//fixme: does not work with fluidsynth
				op.send(new Uint8Array([0xb0 + c, 121, 0]))
				if (s.p_v.midictl) {
				    for (j in s.p_v.midictl)
					op.send(new Uint8Array([0xb0 + c,
								j,
								s.p_v.midictl[j]]))
				}
			}

			v_i[c] = i
			op.send(new Uint8Array([0xc0 + c, i & 0x7f]))	// program
		}
		if (a && Midi5.ma.sysexEnabled) {	// if microtone
// fixme: should cache the current microtone values
			op.send(new Uint8Array([
				0xf0, 0x7f,	// realtime SysEx
				0x7f,		// all devices
				0x08,		// MIDI tuning standard
				0x02,		// note change
				i & 0x7f,		// tuning prog number
				0x01,		// number of notes
					k,		// key
					k,		// note
					a / .78125,	// MSB fract
					0,		// LSB fract
				0xf7		// SysEx end
				]), t)
		}
		op.send(new Uint8Array([0x90 + c, k, 127]), t)		// note on
		op.send(new Uint8Array([0x80 + c, k, 0]), t + d - 20)	// note off
	} // note_run()

	// handle a tie
	function do_tie(s, b40, d) {
	    var	i, note,
		v = s.v,
		end_time = s.time + s.dur

		// search the end of the tie
		while (1) {
			s = s.ts_next
			if (!s)
				return d
			switch (s.type) {
			case C.BAR:
				if (s.rep_p) {
					if (!repn) {
						s = s.rep_p
						end_time = s.time
					}
				}
				if (s.rep_s) {
					if (!s.rep_s[repv + 1])
						return d
					s = s.rep_s[repv + 1]
					end_time = s.time
				}
				while (s.ts_next && s.ts_next.type == C.BAR)
					s = s.ts_next
			}
			if (s.time > end_time)
				return d
			if (s.type == C.NOTE && s.v == v)
				break
		}
		i = s.notes.length
		while (--i >= 0) {
			note = s.notes[i]
			if (note.b40 == b40) {
				note.ti2 = true		// the sound is generated
				d += s.pdur / conf.speed * 1000
				return note.tie_ty ? do_tie(s, b40, d) : d
			}
		}

		return d
	} // do_tie()

	// generate 2 seconds of music
	function play_next() {
	    var	d, i, st, m, note, g, s2,
		s = s_cur,
		t = stime + s.ptim / conf.speed * 1000,	// start time
		maxt = t + 2000			// max time = now + 2 seconds

		if (stop) {
			onend(repv)
			return
		}

		// if speed change, shift the start time
		if (conf.new_speed) {
			stime = window-performance.now() -
					(window.performance.now() - stime) *
						conf.speed / conf.new_speed
			conf.speed = conf.new_speed
			conf.new_speed = 0
			t = stime + s.ptim / conf.speed * 1000
		}

		timouts = []
		while (1) {
			switch (s.type) {
			case C.BAR:
				if (s.bar_type.slice(-1) == ':') // left repeat
					repv = 0
				if (s.rep_p) {			// right repeat
					if (!repn) {
						stime += (s.ptim - s.rep_p.ptim) /
								conf.speed * 1000
						s = s.rep_p	// left repeat
						t = stime + s.ptim / conf.speed * 1000
						repn = true
						break
					}
					repn = false
				}
				if (s.rep_s) {			// first variant
					s2 = s.rep_s[++repv]	// next variant
					if (s2) {
						stime += (s.ptim - s2.ptim) /
								conf.speed * 1000
						s = s2
						t = stime + s.ptim / conf.speed * 1000
						repn = false
					} else {		// end of tune
						s = s_end
						break
					}
				}
				while (s.ts_next && s.ts_next.type == C.BAR)
					s = s.ts_next
				break
			case C.BLOCK:
				if (s.subtype == "midictl") {
					i = s.v & 0x0f		// voice = channel
					if ((s.instr & ~0x7f) == 16384) // if percussion
						i = 9		// force the channel 10
					op.send(new Uint8Array([0xb0 + i,
								s.ctrl, s.val]),
						t)
				}
				break
			case C.GRACE:
				for (g = s.extra; g; g = g.next) {
					d = g.pdur / conf.speed * 1000
					for (m = 0; m <= g.nhd; m++) {
						note = g.notes[m]
						note_run(g,
							note.midi,
							t,
//fixme: there may be a tie...
							d)
					}
				}
				break
			case C.NOTE:
				d = s.pdur / conf.speed * 1000
				for (m = 0; m <= s.nhd; m++) {
					note = s.notes[m]
					if (note.ti2)
						continue
					note_run(s,
						note.midi,
						t,
						note.tie_ty ?
							do_tie(s, note.b40, d) : d)
				}
				// fall thru
			case C.REST:
				d = s.pdur / conf.speed * 1000

				// follow the notes/rests while playing
				i = s.istart
				st = t - window.performance.now()
				timouts.push(setTimeout(onnote, st, i, true))
				setTimeout(onnote, st + d, i, false)
				break
			}
			if (s == s_end || !s.ts_next) {
				setTimeout(onend,
					t - window.performance.now() + d,
					repv)
				s_cur = s
				return
			}
			s = s.ts_next
			t = stime + s.ptim / conf.speed * 1000 // next time
			if (t > maxt)
				break
		}
		s_cur = s

		// delay before next sound generation
		timouts.push(setTimeout(play_next,
					t - window.performance.now()
						- 300))	// wake before end of playing
	} // play_next()

	// MIDI output is possible,
	// return the possible ports in return to get_outputs()
	function send_outputs(access) {
	    var	o, os,
		out = []

		Midi5.ma = access	// store the MIDI access in the Midi5 function

		if (access && access.outputs.size > 0) {
			os = access.outputs.values()
			while (1) {
				o = os.next()
				if (!o || o.done)
					break
				out.push(o.value.name)
			}
		}
		rf(out)
	} // send_outputs()

// public methods
	return {

		// get outputs
		get_outputs: function(f) {
			if (!navigator.requestMIDIAccess) {
				f()			// no MIDI
				return
			}
			rf = f

			// open MIDI with SysEx
			navigator.requestMIDIAccess({sysex: true}).then(
				send_outputs,
				function(msg) {

					// open MIDI without SysEx
					navigator.requestMIDIAccess().then(
						send_outputs,
						function(msg) {
							rf()
						}
					)
				}
			)
		}, // get_outputs()

		// set the output port
		set_output: function(name) {
		    var o, os
			if (!Midi5.ma)
				return
			os = Midi5.ma.outputs.values()
			while (1) {
				o = os.next()
				if (!o || o.done)
					break
				if (o.value.name == name) {
					op = o.value
					break
				}
			}
		},

		// play the symbols
		play: function(i_start, i_end, i_lvl) {

			// get the callback functions
			if (conf.onend)
				onend = conf.onend
			if (conf.onnote)
				onnote = conf.onnote

			s_end = i_end
			s_cur = i_start
			repv = i_lvl || 0
if (0) {
// temperament
			op.send(new Uint8Array([
				0xf0, 0x7f,	// realtime SysEx
				0x7f,		// all devices
				0x08,		// MIDI tuning standard
				0x02,		// note change
				0x00,		// tuning prog number
				0x01,		// number of notes
					0x69,		// key
					0x69,		// note
					0x00,		// MSB fract
					0,		// LSB fract
				0xf7		// SysEx end
				]), t)
}

			v_i = []		// must do a reset of all channels
			stime = window.performance.now() + 200	// start time + 0.2s
			play_next()
		}, // play()

		// stop playing
		stop: function() {
			stop = true
			timouts.forEach(function(id) {
						clearTimeout(id)
					})
			onend(repv)
//fixme: op.clear() should exist...
			if (op && op.clear)
				op.clear()
		} // stop()
	} // returned object
} // end Midi5
Changes to util/toaudio.js.
︙
18
19
20
21
22
23
24
25
26
27
28
29
30
31


32
33
34
35
36
37
38
18
19
20
21
22
23
24


25
26
27
28

29
30
31
32
33
34
35
36
37







-
-




-
+
+







// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

// ToAudio creation
function ToAudio() {

  var	C = abc2svg.C,

	scale = new Uint8Array([0, 2, 4, 5, 7, 9, 11]),	// note to pitch conversion

	a_e,				// event array

	p_time,				// last playing time
	abc_time,			// last ABC time
	play_factor;			// play time factor
	play_factor,			// play time factor
	abcmidi = AbcMIDI()

// ToAudio
  return {

// clear the playing events and
// return the old ones as an array of Float32Array:
//	[0]: index of the note in the ABC source
︙
47
48
49
50
51
52
53
54

55
56
57
58
59

60
61
62
63
64
65
66
67
68
69
70
71

72
73
74



75
76







77
78
79
80
81
82

83
84

85
86

87

88
89
90
91
92
93
94
95
96
97
98









99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177




178
179
180
181
182
183
184
185
186

187
188
189
190
191
192
193

194
195
196
197
198
199
200
201
202

203
204
205
206


207
208


209
210
211

212
213
214
215
216
217
218
46
47
48
49
50
51
52

53





54

55
56
57


58

59
60
61

62
63
64
65
66
67
68


69
70
71
72
73
74
75
76
77
78
79
80

81


82

83
84

85











86
87
88
89
90
91
92
93
94

95
96
97









98
99
100



























































101



102
103
104
105



106



107

108
109
110
111
112
113


114
115
116
117
118
119
120
121
122

123
124
125


126
127
128

129
130
131


132
133
134
135
136
137
138
139







-
+
-
-
-
-
-
+
-



-
-

-



-
+



+
+
+
-
-
+
+
+
+
+
+
+





-
+
-
-
+
-

+
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-



-
-
-
-
-
-
-
-
-



-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-
-
-
+
+
+
+
-
-
-

-
-
-

-
+





-
-
+








-
+


-
-
+
+

-
+
+

-
-
+







	var a_pe = a_e;
	a_e = null
	return a_pe
    }, // clear()

// add playing events from the ABC model
    add: function(start,		// starting symbol
		 voice_tb) {		// voice table
		voice_tb) {		// voice table
	var	kmaps = [],		// accidentals per voice from key signature
		cmaps = [],		// current accidental table
		map,			// map of the current voice - 10 octaves
		temper,			// temperament
		i, n, dt, d, v,
	var	i, n, dt, d, v,
		top_v,			// top voice
		rep_st_s,		// start of sequence to be repeated
		rep_en_s,		// end ("|1")
		rep_nx_s,		// restart at end of repeat
		rep_st_transp,		// transposition at start of repeat sequence
		rep_st_map,		// and map
		rep_st_fac,		// and play factor
		transp,			// clef transposition per voice
		instr = [],		// instrument per voice
		s = start

	// set the accidentals, transpositions and instruments of the voices
	// set the accidentals and instruments of the voices
	function set_voices() {
	    var v, p_v, s, mi

		// reset the audio engine
		a_e.push(new Float32Array([
				0,
		temper = voice_tb[0].temper;	// (set by the module temper.js)
		transp = new Int8Array(voice_tb.length)
				0,	// (time)
				-1,	// MIDI control
				121,	// reset all controllers
				0,
				1,
				0]))

		for (v = 0; v < voice_tb.length; v++) {
			p_v = voice_tb[v];

			mi = p_v.instr || 0
			if (p_v.midictl) {
				p_v.midictl.forEach(function(val, i) {
				for (s = p_v.sym; s; s = s.next)
					switch(i) {
					case 0:		// bank MSB
					if (s.dur)	// search a note/rest
						mi += val * 128 * 128
						break
				if (!s)
					case 32:	// bank LSB
					continue	// no note in this voice
						mi += val * 128
						break
					default:	// generate a MIDI control
						a_e.push(new Float32Array([
							p_v.sym.istart,
							1,	// (time)
							-1,	// MIDI control
							i,
							val,
							1,
							v]))
				p_v.midictl.forEach(function(val, i) {
					a_e.push(new Float32Array([
						s.istart,
						0,	// (time)
						-1,	// MIDI control
						i,
						val,
						1,
						v]))
					}
				})
			}
			instr[v] = mi;			// MIDI instrument

			s = p_v.clef;
			transp[v] = (!s.clef_octave || s.clef_oct_transp) ?
					0 : s.clef_octave

			kmaps[v] = new Float32Array(70);
			cmaps[v] = new Float32Array(70);
			p_v.key.v = v;
			key_map(p_v.key)
		}
	} // set_voices()

	// define the accidentals of a voice
	function key_map(s) {
	    var i, bmap

	    if (s.k_bagpipe) {
		// detune for just intonation in A (C is C#, F is F# and G is Gnat)
//		bmap = new Float32Array([100-13.7, -2, 2, 100-15.6, -31.2, 0, 3.9])
//		for (i = 0; i < 7; i++)
//			bmap[i] = (bmap[i] + 150.6) / 100 // 'A' bagpipe = 480Hz
//				// 150.6 = (Math.log2(480/440) - 1)*1200
		bmap = new Float32Array([2.37, 1.49, 1.53, 2.35, 1.19, 1.51, 1.55])
	    } else {
		bmap = new Float32Array(7)
		switch (s.k_sf) {
		case 7: bmap[6] = 1
		case 6: bmap[2] = 1
		case 5: bmap[5] = 1
		case 4: bmap[1] = 1
		case 3: bmap[4] = 1
		case 2: bmap[0] = 1
		case 1: bmap[3] = 1; break
		case -7: bmap[3] = -1
		case -6: bmap[0] = -1
		case -5: bmap[4] = -1
		case -4: bmap[1] = -1
		case -3: bmap[5] = -1
		case -2: bmap[2] = -1
		case -1: bmap[6] = -1; break
		}
	    }
	    for (i = 0; i < 10; i++)
		kmaps[s.v].set(bmap, i * 7);
	    cmaps[s.v].set(kmaps[s.v])
	} // key_map()

	// convert ABC pitch to MIDI index
	function pit2mid(s, i) {
		var	note = s.notes[i],
			p = note.pit + 19,	// pitch from C-1
			a = note.acc

		if (transp[s.v])
			p += transp[s.v]
		if (a) {
			if (a == 3)		// (3 = natural)
				a = 0
			else if (note.micro_n)
				a = (a < 0 ? -note.micro_n : note.micro_n) /
						note.micro_d * 2;
			map[p] = a
		} else {
			a = map[p]
		}
		p = ((p / 7) | 0) * 12 + scale[p % 7] + a
		if (!temper || a | 0 != a)	// if equal temperament or micro-tone
			return p
		return p + temper[p % 12]
	} // pit2mid()

	// handle the ties
	function do_tie(s, note, d) {
		var	n,
			end_time = s.time + s.dur,
	function do_tie(s, b40, d) {
	    var	i, note,
		v = s.v,
		end_time = s.time + s.dur
			pit = note.pit,
			p = pit + 19,
			a = note.acc

		if (transp[s.v])
			p += transp[s.v]

		// search the end of the tie
		for (s = s.next; ; s = s.next) {
		for (s = s.ts_next; ; s = s.ts_next) {
			if (!s)
				return d

			// skip if end of sequence to be repeated
			if (s == rep_en_s) {
				var v = s.v;
				s = rep_nx_s.ts_next
				s = rep_nx_s
				while (s && s.v != v)
					s = s.ts_next
				if (!s)
					return d
				end_time = s.time
			}
			if (s.time != end_time)
				return d
			if (s.type == C.NOTE)
			if (s.type == C.NOTE && s.v == v)
				break
		}
		n = s.notes.length
		for (i = 0; i < n; i++) {
		i = s.notes.length
		while (--i >= 0) {
			note = s.notes[i]
			if (note.pit == pit) {
			if (note.b40 == b40) {
				note.ti2 = true	// don't generate sound anymore
				d += s.dur / play_factor;
				note.ti2 = true
				return note.ti1 ? do_tie(s, note, d) : d
				return note.tie_ty ? do_tie(s, b40, d) : d
			}
		}
		return d
	} // do_tie()

	// generate the grace notes
	function gen_grace(s) {
︙
270
271
272
273
274
275
276

277

278
279
280
281
282
283
284


285
286
287
288
289
290
291

292

293
294
295
296

297
298
299

300


301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329

330
331





332
333

334
335
336
337
338
339
340
341
342
343
344
345
346

347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
191
192
193
194
195
196
197
198

199
200
201
202
203
204


205
206
207
208
209
210
211
212
213
214

215
216
217
218

219
220
221

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244






245
246


247
248
249
250
251
252
253
254
255

256


257
258
259
260
261




262

263
264
265
266
267
268
269
270





271
272
273
274











275

276
277
278
279
280
281




282
283
284
285
286
287
288
289
290
291
292
293



294
295
296
297
298
299
300







+
-
+





-
-
+
+







+
-
+



-
+


-
+

+
+



















-
-
-
-
-
-


-
-
+


+
+
+
+
+

-
+
-
-





-
-
-
-

-
+







-
-
-
-
-




-
-
-
-
-
-
-
-
-
-
-

-






-
-
-
-












-
-
-







		}
	} // gen_grace()

	// generate the notes
	function gen_notes(s, t, d) {
		for (var i = 0; i <= s.nhd; i++) {
		    var	note = s.notes[i]

			if (note.ti2)
			if (note.ti2)		// tied note
				continue
			a_e.push(new Float32Array([
				s.istart,
				t,
				instr[s.v],
				pit2mid(s, i),
				note.ti1 ? do_tie(s, note, d) : d,
				note.midi,
				note.tie_ty ? do_tie(s, note.b40, d) : d,
				1,
				s.v]))
		}
	} // gen_note()

	// add() main

	// set the MIDI pitches
	set_voices();			// initialize the voice parameters
	abcmidi.add(start, voice_tb)

	if (!a_e) {			// if first call
		a_e = []
		abc_time = rep_st_t = p_time = 0;
		abc_time = p_time = 0;
		play_factor = C.BLEN / 4 * 120 / 60	// default: Q:1/4=120
	} else if (s.time < abc_time) {
		abc_time = rep_st_t = s.time
		abc_time = s.time
	}

	set_voices()			// initialize the voice parameters

	// loop on the symbols
	while (s) {
//		if (s.type == C.TEMPO
//		 && s.tempo) {
		if (s.tempo) {				// tempo change
			d = 0;
			n = s.tempo_notes.length
			for (i = 0; i < n; i++)
				d += s.tempo_notes[i];
			play_factor = d * s.tempo / 60
		}

		dt = s.time - abc_time
		if (dt > 0) {
			p_time += dt / play_factor;
			abc_time = s.time
		}

		if (s == rep_en_s) {			// repeat end
			s = rep_nx_s;
			abc_time = s.time
		}

		map = cmaps[s.v]
		switch (s.type) {
		case C.BAR:
//fixme: does not work if different measures per voice
			if (s.v != top_v)
			if (!s.seqst)
				break

			// end of repeat
			if (s == rep_en_s) {
				s = rep_nx_s
				abc_time = s.time

			// right repeat
			if (s.bar_type[0] == ':') {
			} else if (s.bar_type[0] == ':') {
				s.bar_type = '|' +
					 s.bar_type.slice(1); // don't repeat again
				rep_nx_s = s		// repeat next
				if (!rep_en_s)		// if no "|1"
					rep_en_s = s	// repeat end
				if (rep_st_s) {		// if left repeat
					s = rep_st_s
					for (v = 0; v < voice_tb.length; v++) {
						cmaps[v].set(rep_st_map[v]);
						transp[v] = rep_st_transp[v]
					}
					play_factor = rep_st_fac;
				} else {			// back to start
				} else {	// back to the beginning of the tune
					s = start;
					set_voices();
				}
				abc_time = s.time
				break
			}

			if (!s.invis) {
				for (v = 0; v < voice_tb.length; v++)
					cmaps[v].set(kmaps[v])
			}

			// left repeat
			if (s.bar_type[s.bar_type.length - 1] == ':') {
				rep_st_s = s;
				rep_en_s = null
				for (v = 0; v < voice_tb.length; v++) {
					if (!rep_st_map)
						rep_st_map = []
					if (!rep_st_map[v])
						rep_st_map[v] =
							new Float32Array(70)
					rep_st_map[v].set(cmaps[v]);
					if (!rep_st_transp)
						rep_st_transp = []
					rep_st_transp[v] = transp[v]
				}
				rep_st_fac = play_factor
				break

			// 1st time repeat
			} else if (s.text && s.text[0] == '1') {
				rep_en_s = s
			}
			break
		case C.CLEF:
			transp[s.v] = (!s.clef_octave || s.clef_oct_transp) ?
					0 : s.clef_octave
			break
		case C.GRACE:
			if (s.time == 0		// if before beat at start time
			 && abc_time == 0) {
				dt = 0
				if (s.sappo)
					dt = C.BLEN / 16
				else if (!s.next || s.next.type != C.NOTE)
					dt = d / 2;
				abc_time -= dt
			}
			gen_grace(s)
			break
		case C.KEY:
			key_map(s)
			break
		case C.REST:
		case C.NOTE:
			d = s.dur
			if (s.next && s.next.type == C.GRACE) {
				dt = 0
				if (s.next.sappo)
					dt = C.BLEN / 16
︙
419
420
421
422
423
424
425
426
427
428
429
430
431
432



433
434
435
436
437
438
439





440
441
442
443
444
445
446
447
448
449
450
312
313
314
315
316
317
318



319



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345







-
-
-

-
-
-
+
+
+







+
+
+
+
+











					p_time,
					0,
					0,
					d,
					0,
					s.v]))
			break
		case C.STAVES:
			top_v = s.sy.top_voice
			break
		case C.BLOCK:
			if (s.subtype != "midictl")
				break
			a_e.push(new Float32Array([	// generate a MIDI control
			switch (s.subtype) {
			case "midictl":
			    a_e.push(new Float32Array([	// generate a MIDI control
				s.istart,
				p_time,
				-1,			// MIDI control
				s.ctrl,
				s.val,
				1,
				s.v]))
				break
			case "midiprog":
				instr[s.v] = s.instr	// %%MIDI program
				break
			}
			break
		}
		s = s.ts_next
	}
    } // add()
  } // return
} // ToAudio

// nodejs
if (typeof module == 'object' && typeof exports == 'object')
	exports.ToAudio = ToAudio
Changes to util/toaudio5.js.
︙
51
52
53
54
55
56
57
58
59
60



61

62
63
64
65
66
67
68
69
70
71
72
73
74



75
76
77
78
79
80
81
51
52
53
54
55
56
57



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72



73
74
75
76
77
78
79
80
81
82







-
-
-
+
+
+

+










-
-
-
+
+
+







// play() - start playing
// @start_index -
// @stop_index: indexes of the play_event array
// @play_event: array of array
//		[0]: index of the note in the ABC source
//		[1]: time in seconds
//		[2]: if >= 0: MIDI instrument (MIDI GM number - 1)
//			else: MIDI control message (not treated here)
//		[3]: MIDI note pitch (with cents)
//		[4]: duration
//			else: MIDI control message
//		[3]: MIDI note pitch (with cents) / controller
//		[4]: duration			  / controller value
//		[5]: volume (0..1 - optional)
//		[6]: voice number
//
// stop() - stop playing
//
// set_vol() - set the current sound volume
// @volume: range [0..1] - undefined = return current value

    var	abcsf2 = []			// SF2 instruments

function Audio5(i_conf) {
	var	conf = i_conf,		// configuration
		onend = conf.onend || function() {},
		onnote = conf.onnote || function() {},
		errmsg = conf.errmsg || alert,
		onend = function() {},
		onnote = function() {},
		errmsg = alert,
		ac,			// audio context
		gain,			// global gain

	// instruments/notes
		params = [],		// [instr][key] note parameters per instrument
		rates = [],		// [instr][key] playback rates
		w_instr = 0,		// number of instruments being loaded
︙
234
235
236
237
238
239
240
241


242
243

244
245

246
247





















248
249
250




251
252
253
254
255
256
257
235
236
237
238
239
240
241

242
243
244

245
246

247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270



271
272
273
274
275
276
277
278
279
280
281







-
+
+

-
+

-
+


+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+







				if (--w_instr == 0)
					play_start(a_e)
			})
	} // load_instr()

	// start loading the instruments
	function load_res(a_e) {
		var i, e, instr
	    var	i, e, instr, v,
		bk = []				// bank number

		for (i = evt_idx; ; i++) {
		for (i = evt_idx; i < iend; i++) {
			e = a_e[i]
			if (!e || evt_idx >= iend)
			if (!e)
				break
			instr = e[2]
			v = e[6]
			if (bk[v] == undefined)
				bk[v] = 0
			if (instr < 0) {
				switch (e[3]) {		// controller
				case 0:			// MSB bank
					bk[v] = (bk[v] & 0x3fff) | (e[4] << 14)
					break
				case 32:		// LSB bank
					bk[v] = (bk[v] & 0x1fc07f) | (e[4] << 7)
					break
				case 121:		// reset all controllers
					bk = []
					break
				}
			} else {
				if (bk[v]) {
					instr &= 0x7f
					instr |= bk[v]
					e[2] = instr	// bank + program
				}
			if (instr >= 0 && !params[instr]) {
				params[instr] = [];
				load_instr(instr, a_e)
				if (!params[instr]) {
					params[instr] = [];
					load_instr(instr, a_e)
				}
			}
		}
	}

	// create a note
	// @e[2] = instrument index
	// @e[3] = MIDI key + detune
︙
395
396
397
398
399
400
401
402







403
404












405
406
407

408
409



410
411
412
413
414
415
416
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433


434
435
436
437
438
439
440
441
442
443
444
445
446
447

448
449
450
451
452
453
454
455
456
457
458
459
460








+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+


-
+


+
+
+








	// play the events
	play: function(istart, i_iend, a_e) {
		if (!a_e || istart >= a_e.length) {
			onend()			// nothing to play
			return
		}

		// get the callback functions
		if (conf.onend)
			onend = conf.onend
		if (conf.onnote)
			onnote = conf.onnote
		if (conf.errmsg)
			errmsg = conf.errmsg
		// initialize the audio subsystem if not done yet
		// (needed for iPhone/iPad/...)

		// play a null file to unlock the iOS audio
		// This is needed for iPhone/iPad/...
		function play_unlock() {
		    var buf = ac.createBuffer(1, 1, 22050),
			src = ac.createBufferSource();
			src.buffer = buf;
			src.connect(ac.destination);
			src.noteOn(0)
		}

		// initialize the audio subsystem if not done yet
		if (!gain) {
			ac = conf.ac
			if (!ac)
			if (!ac) {
				conf.ac = ac = new (window.AudioContext ||
							window.webkitAudioContext);
				if (/iPad|iPhone|iPod/.test(navigator.userAgent))
					play_unlock()
			}
			gain = ac.createGain();
			gain.gain.value = conf.gain
		}

		iend = i_iend;
		evt_idx = istart;
		load_res(a_e);
︙
Changes to util/tomidi5.js.
︙
50
51
52
53
54
55
56
57
58


59
60
61
62
63

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85



86
87
88
89
90
91
92
93

94
95
96
97
98
99
100
101
102
103
104
50
51
52
53
54
55
56


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83



84
85
86
87
88
89
90
91
92
93

94




95
96
97
98
99
100
101







-
-
+
+





+



















-
-
-
+
+
+







-
+
-
-
-
-







//		[5]: volume (0..1 - optional)
//		[6]: voice number
//
// stop() - stop playing

function Midi5(i_conf) {
    var	conf = i_conf,		// configuration
	onend = conf.onend || function() {},
	onnote = conf.onnote || function() {},
	onend = function() {},
	onnote = function() {},
	rf,			// get_outputs result function

// MIDI variables
	op,			// output port
	v_i = [],		// voice (channel) to instrument
	bk = [],		// bank of the voice

// -- play the memorized events --
	evt_idx,		// event index while playing
	iend,			// play array stop index
	stime,			// start playing time in ms
	timouts = []		// note start events

// create a note
// @e[2] = instrument index
// @e[3] = MIDI key + detune
// @e[6] = voice (channel) number
// @t = audio start time (ms)
// @d = duration adjusted for speed (ms)
    function note_run(e, t, d) {
    var	k = e[3] | 0,
	i = e[2],
	c = e[6] & 0x0f,	//fixme
	a = (e[3] * 100) % 100	// detune in cents

	if (i == 16384) {			// if bank 128
		c = 9				// channel 10 (percussion)
	} else if (i != v_i[c]) {		// if program change
	if (bk[c] == 128)			// if bank 128 (percussion)
		c = 9				// force the channel 10
	if (i != v_i[c]) {			// if program change

		// at channel start, reset all controllers
//fixme: does not work with fluidsynth
		if (v_i[c] == undefined)
			op.send(new Uint8Array([0xb0 + c, 121, 0]));

		v_i[c] = i
		op.send(new Uint8Array([
		op.send(new Uint8Array([0xc0 + c, i & 0x7f]))	// program
				0xb0 + c, 0, (i >> 14) & 0x7f,	// MSB bank
				0xb0 + c, 32, (i >> 7) & 0x7f,	// LSB bank
				0xc0 + c, i & 0x7f		// program
			]))
	}
	if (a && Midi5.ma.sysexEnabled) {	// if microtone
// fixme: should cache the current microtone values
		op.send(new Uint8Array([
			0xf0, 0x7f,	// realtime SysEx
			0x7f,		// all devices
			0x08,		// MIDI tuning standard
︙
114
115
116
117
118
119
120
121

122
123
124
125
126
127
128
111
112
113
114
115
116
117

118
119
120
121
122
123
124
125







-
+







	}
	op.send(new Uint8Array([0x90 + c, k, 127]), t);		// note on
	op.send(new Uint8Array([0x80 + c, k, 0]), t + d - 20) // note off
    } // note_run()

// play the next time sequence
    function play_next(a_e) {
    var	t, e, e2, maxt, st, d
    var	t, e, e2, maxt, st, d, c

	// play the next events
	if (a_e)			// if not stop
		e = a_e[evt_idx]
	if (!op || evt_idx >= iend || !e) {
		onend()
		return
︙
147
148
149
150
151
152
153

154
155















156
157
158
159
160
161
162
144
145
146
147
148
149
150
151


152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173







+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







			note_run(e, t + stime, d)

		// follow the notes while playing
			st = t + stime - window.performance.now();
			timouts.push(setTimeout(onnote, st, e[0], true));
			setTimeout(onnote, st + d, e[0], false)
		} else {				// MIDI control
			c = e[6] & 0x0f
			op.send(new Uint8Array([0xb0 + (e[6] & 0x0f),
						e[3], e[4]]), t + stime)
			op.send(new Uint8Array([0xb0 + c, e[3], e[4]]),
				t + stime)
			if (bk[c] == undefined)
				bk[c] = 0
			switch (e[3]) {
			case 0:			// MSB bank
				bk[c] = (bk[c] & 0x7f) | (e[4] << 7)
				break
			case 32:		// LSB bank
				bk[c] = (bk[c] & 0x3f80) | e[4]
				break
			case 121:		// reset all controllers
				bk = []
				break
			}
		}

		e = a_e[++evt_idx]
		if (!e || evt_idx >= iend) {
			setTimeout(onend,
				t + stime - window.performance.now() + d)
			return
︙
240
241
242
243
244
245
246







247
248
249
250
251
252
253
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271







+
+
+
+
+
+
+








	// play the events
	play: function(istart, i_iend, a_e) {
		if (!a_e || istart >= a_e.length) {
			onend()			// nothing to play
			return
		}

		// get the callback functions
		if (conf.onend)
			onend = conf.onend
		if (conf.onnote)
			onnote = conf.onnote;

		iend = i_iend;
		evt_idx = istart;
if (0) {
// temperament
	op.send(new Uint8Array([
			0xf0, 0x7f,	// realtime SysEx
			0x7f,		// all devices
︙