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

























- `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
  (the ABC sequences start on `X:` or `%abc` at start of line,
  and stop on any ML tag).  
  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`).













- `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 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),

  - 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
  rendered ABC music.  
  See [this page](http://moinejf.free.fr/abcm2ps-doc/au_clair.xhtml)
  for an example.









- `follow-1.js`   
  This script may be used after `play-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.


When looking at a ABC file in a web browser, you may also use
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
(create a bookmark and copy the link location into the address/location box).



##### 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
```
%%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
```

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

```
    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
```

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
```

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

```
    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
```

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):  



- `abcjs24` with `js24` (Mozilla JavaScript shell - Spidermonkey)
- `abcjs52` with `js52` (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`

- `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`






- `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`
































<
|

|








>
>
>
>
>
>
>
>
>
>
>
>


|



>













|




>
>
>
>
>
>
>
>

|
|




|
>

|
<
>
>
|
|
<
>
>





|
<
|
<













<



















<
<
|
<



<
<
|
<










<
|
|
<








<
|
<




<
|
<





<
|
<










>
>


>
















|








|
>
>
>
>
>









|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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 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 - 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 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,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` 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](http://moinejf.free.fr/js/edit-1.xhtml).

When looking at a ABC file in a web browser, you may also use a

<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

<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   

  `        %%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.  

  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`


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


`        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`


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`


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


  `        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`


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:   
  `        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`

- `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`

- `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
	process.exit(1)
}
abc2svg.readFile = function(fname) {
    var	fs = require("fs")

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







abc2svg.loadjs = function(fn, relay, onerror) {
	try {
		var geval = eval;
		geval(abc2svg.readFile(__dirname + sep + fn))
		if (relay)
			relay()







>
>
>
>
>
>







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
	process.exit(1)
}
abc2svg.readFile = function(fname) {
    var	fs = require("fs")

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







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();







>
>
>
>
>
>

















<







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.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
#!/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,
//		fout = ''
//	for (var i = 0; i < fin.length; i++) {
//		b1 = fin.charCodeAt(i)
//		if (b1 >= 128) {
//			b2 = fin.charCodeAt(++i);















//			fout += String.fromCharCode(((b1 & 0x3f) << 6) + (b2 & 0x7f))


//		} else {
//			fout += fin[i]
//		}
//	}
//	return fout
//}

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

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



}
abc2svg.quit = function() {
	quit(1)
}
abc2svg.readFile = function(fname) {
	return read(fname)
}









|



|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>












>
>
>







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, b3,
//		fout = ''
//	for (var i = 0; i < fin.length; i++) {
//		b1 = fin.charCodeAt(i)
//		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 & 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
	process.exit(1)
}
abc2svg.readFile = function(fname) {
    var	fs = require("fs");

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







abc2svg.loadjs = function(fn, relay, onerror) {
	try {
		var geval = eval;
		geval(abc2svg.readFile(progdir + fn))
		if (relay)
			relay()







>
>
>
>
>
>







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
  g_options  global ABC options\n\
  ABC_file   ABC file\n\
  f_options  file options');
		abc2svg.quit(1)
	}
	args.shift()
	args.shift()

	abc_cmd("abcnode", args)
EOF

node /tmp/toto.js "$@"







>




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
 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

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
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/abcemb2.js abcemb2-$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\
 util/sf2-parser.js util/tomidi5.js > play-$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







>







|










>

>




|

>
>





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
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 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
# rules for ninja (ninja-build)

# 'uglifyjs' may be disabled 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;$
   base64 abc2svg.ttf | sed 's/$$/\\/' >> font.js;$
   echo "\")'" >> font.js

rule jsc
  command = VERSION=`grep '"version"' package.json|cut -d'"' -f4`;$
   type fossil &> /dev/null;$
   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 $



	cat $in | uglifyjs >> $out;$

   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 $



	uglifyjs $in >> $out;$

   else $
	cat $in >> $out;$
   fi

rule edit
  command = if [ "x$$NOMIN" = "x" ]; then $



	sed "s+@MAJOR@+$MAJOR+" $in | uglifyjs > $out;$

   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 $


|
















|
|

|



|
<











>
>
>

>








>
>
>

>






>
>
>

>







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)

# 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/octet-stream;base64,\\" >> font.js;$
   base64 abc2svg.ttf | sed 's/$$/\\/' >> font.js;$
   echo "\") format(\"truetype\")'" >> font.js

rule jsc
  command = VERSION=`grep '"version"' package.json|cut -d'"' -f4`;$
   if command -v fossil >/dev/null 2>&1; 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
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 ambitus-$MAJOR.js: jscu modules/ambitus.js
build break-$MAJOR.js: jscu modules/break.js
build capo-$MAJOR.js: jscu modules/capo.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 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 abcemb2-$MAJOR.js: jscu util/abcemb2.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/sf2-parser.js util/tomidi5.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 $
 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


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







>
>
>
>




>







|










>

>





>
>
|







|
>
>
|
|




>












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 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/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 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

	// 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')

// treat a file
function do_file(fn) {



	var	file = user.read_file(fn)


	if (!file) {
		j = fn.lastIndexOf("/")
		if (j < 0)
			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 + "'"))



//	if (typeof(utf_convert) == "function")
//		file = utf_convert(file)








	// load the required modules
	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



	abc2svg.abc_init(args)

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

	while (1) {
		arg = args.shift()
		if (!arg)
			break







|



>
>
>
|
>

|
|
|
<
<
<
|
>
|
>
|
|
>
|
|
>
>
>



>
>
>
>
>
>
>
|














>
>
>




|
<
<







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				// (global for 'toxxx.js')

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

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

		// get the file extension
		ext = fn.slice((fn.lastIndexOf('.') - 1 >>> 0) + 2)
		if (!ext) {



			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 (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 {
		do_file("default.abc")


	} catch (e) {
	}

	while (1) {
		arg = args.shift()
		if (!arg)
			break
︙︙
107
108
109
110
111
112
113
114
115
116
117
		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
}







<



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.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
// 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 = {

// constants
    C: {
	BLEN: 1536,

	// symbol types
	BAR: 0,
	CLEF: 1,
	CUSTOS: 2,
	GRACE: 4,







|
>
|


|







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/>.

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

// constants
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

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

























































































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

    // constants
    var	C = abc2svg.C;

	// mask some unsafe functions
    var	require = empty_function,







|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|







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
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
	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_val: "Bad value in $1",
	bar_grace: "Cannot have a bar in grace notes",
	ignored: "$1: inside tune - ignored",
	misplaced: "Misplaced '$1' in %%staves",
	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_enough_n: 'Not enough notes/rests for %%repeat',
	not_enough_m: 'Not enough measures for %%repeat',
	not_ascii: "Not an ASCII character"

}

    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,

		line: new scanBuf()
	},

	psvg			// PostScript

// utilities
function clone(obj, lvl) {
	if (!obj)
		return obj
	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]
	    }







>



|



>


|
>











<
<




>
|

>






|







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 %%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_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


	parse = {
		ctx: {},
		prefix: '%',
		state: 0,
		ottava: [],
		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
	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
	}
	user.errmsg(h + txt, l, c)
}

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



	if (user.textrans) {
		tmp = user.textrans[msg]
		if (tmp)
			msg = tmp
	}
	if (arguments.length > 3)
		msg = msg.replace(/\$./g, function(a) {







>
>







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
// 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",
	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)
	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",
	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",







|









|





|







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 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: "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 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
	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 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",







|
|







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 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
//		de.y = s.y;
		return
	}
	if (s.multi)
		up = s.multi > 0
	else
		up = s.stem < 0
	if (up)
		y = s.ymx | 0



	else
		y = (s.ymn - dd.h) | 0

	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
		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







|

>
>
>
|
|
>







>
>

|

<







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) {
		y = s.ymx | 0
	} else if (dd.name[0] == 'w') {
		de.inv = true
		y = s.ymn
	} 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
	de.y = y

	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
				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;
			prev.beam_st = true;
//			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 */







|

|







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;
			prev.beam_st = true;
			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
			 || 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
			&& 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], "")
			}







|
<







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) {

			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
			} else if (dd.dd_st) {
				de.lden = true;
				de.defl.nost = true
			}
		}
	} // create_dh()

	// create all decoration 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++) {







|







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 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
//	- 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),
		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 */







|







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 + 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
			if (gch.type != 'g')
				continue
			gch2 = gch	// chord closest to the staff
			if (gch.y < 0)
				break
		}
		if (gch2) {
			w = gch2.w
			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)







|







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.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
			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)





					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;
			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)







|
>
>
>
>
>

>


<
<



|
|
|







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) {
				switch (s.type) {
				case C.TIMESIG:
				case C.CLEF:
				case C.KEYSIG:
				case C.FMTCHG:
				case C.STBRK:
					continue
				}
				break
			}



			// don't display the number twice
		     if (s.type != C.BAR || !s.bar_num) {
			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
					break
			}
			set_dscale(st)
			continue
		default:
			continue
		case C.BAR:
			if (!s.bar_num)
				continue
			break
		}

		bar_num = s.bar_num
		if (cfmt.measurenb == 0
		 || (bar_num % cfmt.measurenb) != 0







|







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 || 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
	}
	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);
		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;
		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);
			anno_stop(s);
			dosh >>= 1
		}
	}

	/* then, put the parts */
/*fixme: should reduce vertical space if parts don't overlap tempo...*/







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


















|













|



















|







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 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 = 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 + 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)
			}
			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
	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








<







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_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

	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,
		s = clone(s_orig);


	s.invis = true

	delete s.text
	delete s.a_gch
	delete s.a_ly
	delete s.a_dd;
	s.notes = clone(s_orig.notes)
	for (m = 0; m <= s.nhd; m++) {
		note = s.notes[m] = clone(s_orig.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) {
    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.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;







|
|
<

>

>




|

|















|
















|
<







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) {
    var	m, note


	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.notes)
	for (m = 0; m <= s.nhd; 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)
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.sls;

		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
			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.slur_end
			s.x += 12
			if (s.x < realwidth - 12)
				s.x = realwidth - 12;
			s2 = s;
			notes++
			break
		}







|
<







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.sls;

			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
			}
		}
		s.xs = s.x + x * scale;
		if (s == s2)
			break
	}

	// have flat beams when asked
	if (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
		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 */







|
|












|
|
|
<
<
<







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 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)
		a = cfmt.beamslope * a /
			(cfmt.beamslope + Math.abs(a)) // max steepness for beam




	// 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
			xygl(x, y, "acc" + acc + '_' + micro_n + '_' + micro_d)
			return
		}
	}
	xygl(x, y, "acc" + acc)
}

// draw helper lines between yl and yu
//fixme: double lines when needed for different voices

function draw_hl(x, yl, yu, st, hltype) {
    var	i, j,


	p_staff = staff_tb[st],













	staffb = p_staff.y,
	stafflines = p_staff.stafflines,
	top = (stafflines.length - 1) * 6,
	bot = p_staff.botline

	// no helper if no line
	if (!/[\[|]/.test(stafflines))
		return


	if (yl % 6)
		yl += 3
	if (yu % 6)
		yu -= 3
	if (stafflines.indexOf('-') >= 0	// if forced helper lines ('-')
	 && ((yl > bot && yl < top) || (yu > bot && yu < top)
	  || (yl <= bot && yu >= top))) {
		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
		}
	}
	for (; yl < bot; yl += 6)
		xygl(x, staffb + yl, hltype)
	for (; yu > top; yu -= 6)
		xygl(x, staffb + yu, hltype)














}

/* -- draw a key signature -- */

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)
		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








|

>
|
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
|


|
|
<
<
|

>
|
|
<
|
<
<
<
|
|
<
|
<
<
<
<
<






>
>
>
>
>
>
>
>
>
>
>
>
>
>



>







|
|







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
//fixme: double lines when needed for different voices
// (possible hook)
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,
	yl = bot,


	yu = top

	for (i = 0; i < hla.length; i++) {
		if (hla[i] < yl) {
			yl = (((hla[i] + 51) / 6) | 0) * 6 - 48;

			n--



		} else if (hla[i] > yu) {
			yu = ((hla[i] / 6) | 0) * 6;

			n--





		}
	}
	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])

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
			}
		}
	} else if (s.k_a_acc.length) {

		/* explicit accidentals */
		var	acc,
			last_acc = s.k_a_acc[0].acc,
			last_shift = 100






		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;

			draw_hl(x, shift, shift, st, "hl");
			last_shift = shift;
			draw_acc(x, staffb + shift,
				 acc.acc, acc.micro_n, acc.micro_d);
			x += 5.5
		}
	}
}







|
>
>
>
>
>












>
|







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,
			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;
			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
/* (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]

	/* if rest alone in the measure or measure repeat, center */

	if (s.dur == s.p_v.meter.wmeasure
	 || (s.rep_nb && s.rep_nb >= 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







|
|

|
>
|

>
>
>
>
>
>
>







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, yb, bx,
	p_staff = staff_tb[s.st]

	// 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
		anno_stop(s)
		return
	}

	set_scale(s);
	anno_start(s);




	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])

	/* output ledger line(s) when greater than minim */
	if (i >= 6) {
		j = y / 6
		switch (i) {
		default:
			switch (p_staff.stafflines[j + 1]) {







>
>
>








|







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 || 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
		x += 8;
		y += yb + 3
		for (i = 0; i < s.dots; i++) {
			xygl(x, y, "dot");
			x += 3.5
		}
	}

	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 = {}

	/* 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.next)
			break			/* (keep the last note) */
	}

	// if an acciaccatura, draw a bar 
	if (s.sappo) {
		g = s.extra







>






|
|
|













>
>







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, 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
				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 */

	 || !s.next
	 || s.next.type != C.NOTE)
		return
	last = g
	if (last.stem >= 0) {

		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)
			x3 -= 4;
		y3 = 3 * (s.notes[0].pit - 18) - 5;
		dy1 = (x3 - x0) * .4
		if (dy1 > 3)
			dy1 = 3;
		dy2 = dy1;
		bet1 = .2;







|
>




|
>















|







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
	 || slur				// explicit slur
	 || s.tie_s				// some tie
	 || !s.next
	 || s.next.type != C.NOTE)
		return
	last = g
	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 && 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
			dy2 = (y0 - y3) * .2;
			bet1 = 0
		} else if (y3 > y0 + 4) {
			y3 = y0 + 4;
			x0 = last.x + 2;
			y0 = last.y - 4
		}
	} else {
		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)
			x3 -= 2;
		y3 = 3 * (s.notes[s.nhd].pit - 18) + 5;
		dy1 = (x0 - x3) * .4
		if (dy1 < -3)
			dy1 = -3;
		dy2 = dy1;
		bet1 = .2;







|















|







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 {				// 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 && 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
	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,
		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







|







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, 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
		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)







<
<







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)


	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
		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

		while (--dots >= 0) {
			xygl(dotx, doty, "dot");
			dotx += 3.5
		}
	}

	/* draw the accidental */
	if (note.acc) {
		x -= note.shac * stv_g.scale







>
|
|







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, (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

	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"

	} else {
		hltype = "hl"
		switch (s.head) {
		case C.OVALBARS:
		case C.OVAL:
			x_hl = x - .3;
			hltype = "hl1"
			break
		case C.SQUARE:
			x_hl = x - 2;
			hltype = "hl1"
			break
		}
	}
	draw_hl(x_hl, 3 * (s.notes[0].pit - 18), 3 * (s.notes[s.nhd].pit - 18),
		s.st, 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)







|
>





|



|




<
|







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";
		x_hl += 1.5
	} else {
		hltype = "hl"
		switch (s.head) {
		case C.OVALBARS:
		case C.OVAL:
			x_hl -= .3;
			hltype = "hl1"
			break
		case C.SQUARE:
			x_hl -= 2;
			hltype = "hl1"
			break
		}
	}

	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
			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;







<
<







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;


		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

	/* 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 -- */
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 -- */
function slur_direction(k1, k2) {
	var s, some_upstem, low


















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





	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)

			break
	}
	if (!some_upstem && !low)
		return 1
	return -1
}








|
<
<
<
<
<
<
<
<
<
<
<
<


















|

|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>
>
>
>











|
>







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 start a long decoration












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 (same voice) -- */
function slur_direction(k1, k2) {
    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.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
//		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

	if (!dotted)
		output += '<path class="fill" 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) + ' ' +







|
|
>

|







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.st < 0 ? stv_g.scale : 1
	var scale_y = 1			// (see set_dscale())
	if (!dotted)
		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
			((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 -- */
/* (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

	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
	default:
		dir = slur_multi(k1, k2)
		if (!dir)
			dir = slur_direction(k1, k2)
		break
	}


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

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

		k = k1.next
		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 == 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
	if (k1_o != k2) {
		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 (m1 >= 0) {
		y1 = 3 * (k1.notes[m1].pit - 18) + 5 * dir

	} else {
		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 */







<
<
<
<
<
<
<
<
<
<
<
<
<
|



|
>
>
|
|
|
|
<
<
<
<
|
|
<
|
|
|
|
|
>






|
>
|
<
|
|
|
|
|
|
|
|
<
<
<
|
<




|
|
|
<
|
|
|
<
<
<
<
<
<
|
|
<
|
>
|







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'
}














// draw a slur between two chords / notes
/* (the staves are not yet defined) */
/* (delayed output) */
/* (not a pretty routine, this) */
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,




	dir = (ty & 0x07) == C.SL_ABOVE ? 1 : -1,
	n = path.length,

	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)

	for (i = 1; i < n; i++) {
		k = path[i]

		if (k.type == C.NOTE || k.type == C.REST) {
			nn++
			if (k.st != upstaff) {
				two_staves = true
				if (k.st < upstaff)
					upstaff = k.st
			}
		}



	}

/*fixme: KO when two staves*/
if (two_staves) error(2, k1, "*** multi-staves slurs not treated yet");

	/* fix endpoints */
	x1 = k1.x
	if (k1.notes && k1.notes[0].shhd)
		x1 += k1.notes[0].shhd;

	x2 = k2.x
	if (k2.notes)
		x2 += k2.notes[0].shhd







	if (not1) {					// start on a note

		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
					}
//				} else {
//					y1 = k1.y - 8
				}
			}
		}
	}
	if (m2 >= 0) {

		y2 = 3 * (k2.notes[m2].pit - 18) + 5 * dir

	} else {
		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







|
>
|
>
|







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 (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
		else
			y2 = y1
		if (k1 != k2)
			x2 = k2.x - k2.wl * .3
	}

	if (nn >= 3) {

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

	a = (y2 - y1) / (x2 - x1)		/* slur steepness */







>
|
|

|



|




|
|
|

|



|







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 (k.type != C.BAR
		 && k.x < x1 + 48) {
			if (dir > 0) {
				y = k.ymx - 2
				if (y1 < y)
					y1 = y
			} else {
				y = k.ymn + 2
				if (y1 > y)
					y1 = y
			}
		}
		k = path[i2 - 1]
		if (k.type != C.BAR
		 && k.x > x2 - 48) {
			if (dir > 0) {
				y = k.ymx - 2
				if (y2 < y)
					y2 = y
			} else {
				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
		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) {

		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







|
>







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 (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
	} 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);
//	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) {
		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) {
			dx = x2
			if (k2.sl1)
				dx -= 5

		} else {
			dx = k.next.x
		}
		if (k != k1)
			x1 = k.x;


		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





















	while (1) {
		if (!s || s == last) {
			if (!gr1
			 || !(s = gr1.next)
			 || s == last)
				break
			gr1 = null
		}
		if (s.type == C.GRACE) {
			gr1 = s;
			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
			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
					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
				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
				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 (gr2
				 && gr2.next) {
					gr2.next.prev.next = null;
					gr2.next.prev = gr2
				}
			}
			if (s1 == last)
				break
			s1 = s1.next
		}
		if (!s1) {
			k = next_scut(s)
		} else if (!k) {
			s = s1
			if (s == last)
				break
			continue
		}

		/* 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)
					break
			slur_type = s.notes[m1].sl1 & 0x0f;
			s.notes[m1].sl1 >>= 4;
			s.sl1--
		}
		m2 = -1;
		cont = 0
		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)
						break
				k.notes[m2].sl2--;
				k.sl2--
			}
		} 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 (!k.p_v.slur_start)
				k.p_v.slur_start = 0;
			k.p_v.slur_start <<= 4;
			k.p_v.slur_start += slur_type
		}

		/* 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,
			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



	// check if some slurs and treat 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
				}
			}
			continue
		}
		if (s2.slur_start || s2.slur_end /* if slur start/end */
		 || s2.sl1 || s2.sl2)
			some_slur = true
		if (s2.st < upstaff)
			upstaff = s2.st
		if (lvl == 0) {
			if (s2.tp1)
				draw_tuplet(s2, 1)
			if (s2.te0)
				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)

		// 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 (s2.slur_end || s2.sl2)
			return
	}

	if (lvl == 0) {
		p = s1.tp0;
		s1.tp0 = 0;
		q = s1.tq0
	} else {
		p = s1.tp1;
		s1.tp1 = 0
		q = s1.tq1
	}

	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 (!) */

		nb_only = true
	} else if (s1.tf[1] == 1) {			/* 'what' == slur */
		nb_only = true;
		draw_slur(s1, s2, -1, -1, dir)
	} else {

		/* search if a bracket is needed */
		if (s1.tf[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







|






|
>
>
|
>
>
>
>
|







|


|
>

|

|
|
>
>



<



|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|
|




|
|



<
<
|
|
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
|
<
<
|
|
<
<
|
<
<
<

|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
<
<
|
|
<
|
|
<
<
<
<
<
|
|
<
<
|
|
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
|
<
<
<
<
<
|
<
<
<
|
|
<
<
<
<








|
|
<
|
|
>
|
>
>

|






|
|




<
|
|


<
|
|
|
|
<
<

<


<
<
<
<



<
<
<
|
<
<
<
<
<
<
<
|
|
<
|
|
|
|
<
<
|
|
|
<
<
<
|
<
<
|
<
<
<

|
>

|

|



|
|
<
<







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, 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
//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 (i == i2 - 1) {
			dx = x2
			if (k2.sl1)
				dx -= 5;
			y -= height / 3
		} else {
			dx = path[i + 1].x
		}
		if (i != 0)
			x1 = k.x
		else
			y -= height / 3
		dx -= x1;
		y_set(upstaff, dir > 0, x1, dx, y)
	}

}

/* -- draw the slurs between 2 symbols --*/
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		// if end of grace notes
			 || !(s = gr1.next)	// return to normal notes
			 || s == last)
				break
			gr1 = null
		}
		if (s.type == C.GRACE) {	// if start of grace notes
			gr1 = s;		// continue in the grace note sequence
			s = s.extra
			continue
		}


		if (s.sls) {			// slurs from the chord
			sls = s.sls









			s.sls = null


















			nsls = []








			for (i = 0; i < sls.length; i++) {












				if (!draw_sls(s, sls[i]))






					nsls.push(sls[i])


			}
			if (nsls.length)


				s.sls = nsls



		}
		if (s.sl1) {			// slurs from the note heads



















			for (m = 0; m <= s.nhd; m++) {
				note = s.notes[m]


				if (note.sls) {
					sls = note.sls

					note.sls = null
					nsls = []





					for (i = 0; i < sls.length; i++) {
						if (!draw_sls(s, sls[i], note))


							nsls.push(sls[i])
					}









					if (nsls.length)





						note.sls = nsls





				}



			}
		}




		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 'tp.f' */
function draw_tuplet(s1, tp) {

    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

	// 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.sls || g.sl1)
						draw_slurs(g)
				}
			}
			continue
		}

		if (s2.sls || s2.sl1)
			draw_slurs(s2)
		if (s2.st < upstaff)
			upstaff = s2.st

		if (s2.tp)
			draw_tuplet(s2)
		if (s2.tpe)
			break


	}

	if (!s2) {
		error(1, s1, "No end of tuplet in this music line")




		return
	}




	s2.tpe--








	if (tp.f[0] == 1)			/* if 'when' == never */

		return

	dir = tp.f[3]				// 'where'
	if (!dir) {				// if auto


		s3 = s1
		while (s3.type != C.NOTE)
			s3 = s3.next



		dir = s3.stem > 0 ? C.SL_ABOVE : C.SL_BELOW


	}




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

		/* search if a bracket is needed */
		if (tp.f[0] != 2		// if 'when' != always
		 && s1.type == C.NOTE && s2.type == C.NOTE) {


			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
				}
			}
		}
	}

	/* if number only, draw it */
	if (nb_only) {
		if (s1.tf[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);
		b = s1.ys - a * s1.x;
		yy = a * xm + b
		if (dir == C.SL_ABOVE) {
			ym = y_get(upstaff, 1, xm - 4, 8)
			if (ym > yy)
				b += ym - yy;
			b += 2
		} else {
			ym = y_get(upstaff, 0, xm - 4, 8)
			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)
		else
			out_bnum(xm, ym, p + ':' +  q)





		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 */
		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;







|


<
<
<
|
<
<
|
|
<
<
<
|
|
<
<
<
|
<
<
<
<






|
|
|

|
>
>
>
>
>













|
<



<
<
<







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 (tp.f[2] == 1)		/* if 'which' == none */
			return
		xm = (s2.x + s1.x) / 2



		a = s2.x - s1.x			// width around the middle


		if (dir == C.SL_ABOVE)
			ym = y_get(upstaff, 1, xm - a / 2, a) + 2



		else
			ym = y_get(upstaff, 0, xm - a / 2, a) - 10








		if (s1.stem * s2.stem > 0) {
			if (s1.stem > 0)
				xm += 1.5
			else
				xm -= 1.5
		}

		if (tp.f[2] == 0)		/* if 'which' == number */
			out_bnum(xm, ym, tp.p)
		else
			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
	}

	// here, what is square bracket


/*fixme: two staves not treated*/
/*fixme: to optimize*/



    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
		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 */
	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;







|







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 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
			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 */
		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);

	yy = .5 * (y1 + y2)
	if (dir == C.SL_ABOVE)
		y_set(upstaff, true, xm - 3, 6, yy + 9)
	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;
		dir = (k1.notes[m1].ti1 & 0x07) == C.SL_ABOVE ? 1 : -1;


		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;

		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
		}

		st = k1.st
		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)
//(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
		}






		y = 3 * (p - 18)

		h = (.04 * (x2 - x1) + 10) * dir;
//		anno_start(k1, 'slur');
		slur_out(x1, staff_tb[st].y + y,
			 x2, staff_tb[st].y + y,
			 dir, h, k1.notes[m1].ti1 & C.SL_DOTTED)
//		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,
		mhead1 = [],
		mhead2 = [],
		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)




		}
		draw_note_ties(k1, k2 || k1, mhead3, mhead3, job)

		return
	}

	/* set up list of ties to draw */
	for (i = 0; i <= nh1; i++) {
		if (!k1.notes[i].ti1)
			continue
		tie2 = -1;
		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
				break
			}
			break
		}
		if (tie2 >= 0) {		/* 1st or 2nd choice */
			mhead1.push(i);
			mhead2.push(tie2)
		} else {
			mhead3.push(i)		/* 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 */


	k3 = k1.ts_next


	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

				if (pit2 == pit) {
					mhead1.push(j);

					mhead2.push(m1);

					mhead3[i] = mhead3.pop()
					break
				}
			}
		}
		if (mhead1.length > 0) {
			draw_note_ties(k1, k3,
					mhead1, mhead2,
					job == 1 ? 1 : 0)
			if (mhead3.length == 0)
				return
		}
		k3 = k3.ts_next
	}

	if (mhead3.length != 0)
		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

	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)
					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


	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

			}
			if (s1.bar_type == '|')
				continue		// not a repeat
			for (s2 = s1.next; s2; s2 = s2.next)
				if (s2.type == C.NOTE)
					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
			if (s2.type == C.BAR && s2.text) {	// repeat bar


				if (s2.text[0] != '1')
					break
				s_rtie = s1		/* 1st repeat bar */
			}
		}
		if (!s2) {
			for (s2 = s1.ts_next; s2; s2 = s2.ts_next) {
				if (s2.st != s1.st)
					continue
				if (s2.time < time)
					continue
				if (s2.time > time) {
					s2 = null
					break
				}

				if (s2.dur)
					break
			}
			if (!s2) {







				draw_ties_g(s1, null, 2);
				p_voice.s_tie = s1
				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
			}
		}
		for (s3 = s1.ts_next; s3; s3 = s3.ts_next) {
			if (s3.st != s1.st)
				continue
			if (s3.time > time)
				break
			if (s3.type == C.CLEF) {
				clef_chg = true
				continue
			}
		}

		/* ties with clef or staff change */
		if (clef_chg || s1.st != s2.st) {
			clef_chg = false;
			dx = (s2.x - s1.x) * .4;







|




|

<

|





|
|
|
|
>
|
|
|
|
<
>

|
|
>
|
|
|
|
|
|
|
|
|
|

<
|
>
>
>
|
|
|
|
|
|
|
|
|
|
|
|

<
|
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

>
>
>
>
>
|

|
|
|
<
<
|
<








|
<
<
|
|
<




|
|
>
>
>
>
|
<
>



|

|
<
|
<
<
<
<
<
<
<
<
<
|
|
|
<
<
<
<
|
<
|
|
|
|
|
<
<
<
<




>

>
>








<
<

|
|
|
|
>

|
>
|
>
|




<
<
<
<
|
|
<



|



<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


|


<
<

|
|







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


>
>

|
|
|
|
|
|
|
|
|
|
|
>

|
<
<
<
|
<
<
<
<
<
<
<
<
<
<




|
|
>
>
|
>
|
|
>
|
<
>
>
|

<

|
<
|
|
|
|
<
<
|


>
|


|
>
>
>
>
>
>
>
|
<
|
|
<
<
<
<
<
|
<
<
<
<
<
<
>
|
<
<
|
|

|

|

|







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 (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,
		tp.f[2] == 0 ? tp.p.toString() : tp.p + ':' +  tp.q);


	if (dir == C.SL_ABOVE)
		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(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,

	sh = not1.shhd			// head shift

	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


	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
	}


	switch (job) {
	case 0:

		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 (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 = staff_tb[st].y + 3 * (p - 18)

	h = (.03 * (x2 - x1) + 16) * dir
//	anno_start(k1, 'slur')
	slur_out(x1, y, x2, y, dir, h, not1.tie_ty & C.SL_DOTTED)


//	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, not1, not3, time, pit, pit2,


	mhead3 = [],
	nh1 = k1.nhd


	/* half ties from last note in line or before new repeat */
	if (job == 2) {
		for (i = 0; i <= nh1; 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
			}

		}
		return
	}

	// draw the ties or memorize the ones without ending
	for (i = 0; i <= nh1; i++) {
		not1 = k1.notes[i]

		if (!not1.tie_ty)









			continue
		if (!not1.s)
			not1.s = k1




		if (not1.tie_n)

			draw_note_ties(not1, job)
		else
			mhead3.push(not1)	/* no match */
	}





	/* 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
		}


		for (i = mhead3.length; --i >= 0; ) {
			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) {
					not1.tie_n = not3
					if (!not3.s)
						not3.s = k3
					draw_note_ties(not1, job)
					mhead3.splice(i, 1)
					break
				}
			}
		}




		if (!mhead3.length)
			break

		k3 = k3.ts_next
	}

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





















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

	function draw_ties_g(s1, s2, job) {


		if (s1.type == C.GRACE) {
			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()



































	/* search the start of ties */
//	clef_chg = false
	s_next = p_voice.sym
	set_color(s_next.color)
	while (1) {
		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.tie_s)



				break










		}
		if (!s1)
			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

				}
				s = s.next
				if (s == s2)
					break

			}
		} else {

			s_next = s1.next
			s = s1
			while (1) {
				if (!s.ts_next) {


					s = null
					break
				}
				s = s.ts_next
				if (s == s2)
					break
			}
			if (!s) {
				s2 = s1
				while (s2.next)
					s2 = s2.next
			}
		}

		if (!s) {				// end of line
			draw_ties_g(s1, s2, 2);

			break
		}












		// check if some clef changes (may occur in an other voice)
		time = s1.time + s.dur


		for (s = s1.ts_next; s != s2; s = s.ts_next) {
			if (s.st != s1.st)
				continue
			if (s.time > time)
				break
			if (s.type == C.CLEF) {
				clef_chg = true
				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
				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
 * - 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;

	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;








<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<









<

|








|
<

>







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)
	}




































































}

/* -- draw the symbols near the notes -- */
/* (the staves are not yet defined) */
/* order:
 * - scaled
 *   - beams
 *   - decorations near the notes
 *   - measure bar numbers

 *   - decorations tied to the notes
 *   - 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 = 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
			else
				y -= 5
			if (s.ymn > y)
				s.ymn = y;
			y_set(s.st, false, s.x, 0, y)
		}
	}



	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 */
		for ( ; s; s = s.next) {
			if (s.tp0)
				draw_tuplet(s, 0)
		}
		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)
		}
	}


	/* 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)







>
>













|

|
|
<
<
<
<
<
|
|


>















<
<







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 slurs and tuplets
		for ( ; s; s = s.next) {
			if (s.tp)
				draw_tuplet(s)





			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
		}
	}



	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

	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 = []

	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)
			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)
			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");


	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) {
			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))
				n++
		}
		if (n != st)
			y -= (staff_tb[st].y - staff_tb[n].y) * .5
		for (n = 0; n < a_p.length; n++) {
			p = a_p[n];
			xy_str(indent, y, p, "c");
			y -= 18
		}
	}
}

// -- 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,







|
|











|















|









<
<
<
<
<
<
<








>
>








|
|
|
|
<
<
<
|
<
<
|
|



|







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, 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 || !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 || !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 (!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
			+ h2 * (a_p.length - 2)

		// if instrument with 2 staves, center the voice name
		if ((cur_sy.staves[st].flags & OPEN_BRACE)



		 && (cur_sy.staves[st + 1].flags & CLOSE_BRACE)


		 && !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 -= 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
/* -- 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 = [],
		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++) {







|
|
|
<
<







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 = [],
		ba = [],		// bars [symbol, bottom, height]
		sb = [],
		thb = []



	/* -- 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
		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) {

			xygl(x1, staff_tb[st].y, 'stdef' + cfmt.fullsvg)
			return
		}
		for (i = 0; i < l; i++, y -= 6) {
			if (stafflines[i] == '.')
				continue
			dy = 0







|
>







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 | 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
						'h' + ws.toFixed(1);
					dy = 0
					continue
				}
				if (ty != undefined)
					ln += '"/>\n';
				ty = stafflines[i]
				ln += '<path class="stroke ' +
					(ty == '[' ? 'slthW' : 'slW') +
					'" d="m0 ' + y + 'h' + ws.toFixed(1);
				dy = 0
			}
			ln += '"/>\n'
		}
		y = staff_tb[st].y
		if (!cache
		 && w == get_lwidth()) {
			cache = {
				st_l: stafflines,
				st_ws: ws
			}
			i = 'stdef' + cfmt.fullsvg;



			glyphs[i] = '<g id="' + i + '">\n' + ln + '</g>';
			xygl(x1, y, i)
			return
		}
		out_XYAB('<g transform="translate(X, Y)">\n' + ln + '</g>\n', x1, y)
	} // draw_staff()

	// draw a measure bar
	function draw_bar(s, bot, h) {
	    var	i, s2, yb,
		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)



		// 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"])
			} else {
				gl.push([x, yb, st, "mrep2"])
				if (s.v == cur_sy.top_voice)
					rn.push([x, yb + p_staff.topbar - 9,
						st, s.bar_mrep.toString()])
			}
		}

		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
					sb.push(new Float32Array([x, bot, h]))

				break
			default:
//			case "[":
//			case "]":
				x -= 3;




				thb.push(new Float32Array([x + 1.5, bot, h]))
				break
			case ":":
				x -= 2;

				gl.push([x + 1, yb - 12, st, "rdots"])
				break
			}
			x -= 3
		}
		set_sscale(-1);
		anno_stop(s)
	} // draw_bar()

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













		set_sscale(-1)

		if (l) {			// single bars [x, y, h]
			output += '<path class="stroke 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="'
			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++)







|




|



|


|


>
>
>
|



|




|














|

>
>












|

|

|
|









|
|
|
>
>
>
>
>
>
|

>





>
>
>
>
|



>
|




|





|
|

>
>
>
>
>
>
>
>
>
>
>
>

>

|







<
<
<
<
<
<
<
<
<
<
<


|






<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







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="' +
					(ty == '[' ? 'slthW' : 'slW') +
					'" d="m0 ' + y + 'h' + ws.toFixed(1);
				dy = 0
			}
			ln += '"/>'
		}
		y = staff_tb[st].y
		if (!cache
		 && w > get_lwidth() - 10) {
			cache = {
				st_l: stafflines,
				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 + '\n</g>';
			xygl(x1, y, i)
			return
		}
		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, 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;

		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)
					;
				xygl(s2.x, yb, "mrep")
			} else {
				xygl(x, yb, "mrep2")
				if (s.v == cur_sy.top_voice)
					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) {
					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]))
				break
			case ":":
				x -= 2;
				set_sscale(st);
				xygl(x + 1, yb - 12, "rdots")
				break
			}
			x -= 3
		}
		set_color();
		anno_stop(s)
	} // draw_bar()

	// output all the bars
	function out_bars() {
	    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="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 = thb.length
		if (l) {			// thick bars [x, y, h]
			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'
		}

























	} // 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
				draw_staff(st, x, x2)
				if (sy.st_print[st])
					xstaff[st] = x2
			}
			cur_sy = sy;
			bar_set()
			continue
		case C.BAR:
			if (s.second || s.invis || !s.bar_type)
				break
			st = s.st;
			draw_bar(s, bar_bot[st], bar_height[st])
			break
		case C.STBRK:
			if (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)

							nv++
					}
					for (s2 = s.ts_next; s2; s2 = s2.ts_next) {
						if (s2.type != C.STBRK)
							break
						nv--
					}







|


<
|


|
>





|
>







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:		// display the bars after the staves
			if (s.second || s.invis || !s.bar_type)
				break

			ba.push([s, bar_bot[s.st], bar_height[s.st]])
			break
		case C.STBRK:
			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]
						  && 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
		if (x < 0 || x >= realwidth)
			continue
		draw_staff(st, x, realwidth)
	}

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

/* -- draw remaining symbols when the staves are defined -- */
// (possible hook)
function draw_symbols(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) {







|




|







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)
}

/* -- draw remaining symbols when the staves are defined -- */
// (possible hook)
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
			if (s.second
			 || s.invis
			 || !staff_tb[s.st].topbar)
				break
			set_color();
			set_sscale(s.st);
			anno_start(s);
			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");







|







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);
			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
	}

	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

	for (s = sym; s; s = s.next) {
		if (!s.ti1)
			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))
					continue
				s.notes[i].ti1 = (ti & 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) {
				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
					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)
						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 (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)
					continue
				if ((ti & 0x07) == C.SL_AUTO)
					s.notes[i].ti1 =
						(ti & 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 (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)
					continue
				if (ntie == i)
					dir = C.SL_ABOVE
				if ((ti & 0x07) == C.SL_AUTO)

					s.notes[i].ti1 = (ti & 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 */







|
|

|
|






|
|

|








|










|
|
|
|
|













|
|





|
|
|









|
|

|
|
|











|










|
|



|
>
|







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(s) {
    var i, ntie, dir, sec, pit, ty

	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++) {
				ty = s.notes[i].tie_ty
				if (!((ty & 0x07) == C.SL_AUTO))
					continue
				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].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++) {
				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++) {
					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 ((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++) {
				ty = s.notes[i].tie_ty
				if (!ty)
					continue
				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].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++) {
				ty = s.notes[i].tie_ty
				if (!ty)
					continue
				if (ntie == i)
					dir = C.SL_ABOVE
				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
		if (!s)
			continue
		s = s.next
		if (!s)
			continue
		set_tie_dir(s)
		for ( ; s; s = s.next) {
			if (!s.ti1)
				continue
			if (s.notes[0].pit < 20

			 && (s.notes[0].ti1 & 0x07) == C.SL_BELOW)
				;
			else if (s.notes[s.nhd].pit > 24
			      && (s.notes[s.nhd].ti1 & 0x07) == C.SL_ABOVE)

				;
			else
				continue
			s2 = s.next
			while (s2 && s2.type != C.NOTE)
				s2 = s2.next
			if (s2) {







|


>
|


|
>







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.tie_s)
				continue
			if (s.notes[0].pit < 20
			 && s.notes[0].tie_ty
			 && (s.notes[0].tie_ty & 0x07) == C.SL_BELOW)
				;
			else if (s.notes[s.nhd].pit > 24
			      && 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
// 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_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 },
	aligncomposer: 1,

//	botmargin: .7 * IN,		// != 1.8 * CM,
	breaklimit: .7,
	breakoneoln: true,
	cancelkey: true,
	composerfont: { name: "serifItalic", 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 },
	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,
	maxstaffsep: 2000,
	maxsysstaffsep: 2000,
	measurefirst: 1,
	measurefont: {name: "serifItalic", size: 10},
	measurenb: -1,
	musicspace: 6,
//	notespacingfactor: 1.414,
	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},
	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},
	vocalspace: 10,
	voicefont: {name: "serifBold", size: 13},
//	voicescale: 1,
	writefields: "CMOPQsTWw",
	wordsfont: {name: "serif", size: 16},
	wordsspace: 5
}

function get_bool(param) {







>











|

>




|














|












|


<
|


|





<
<
<
<
<
<
<
<
<















|











|

|







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 },
	aligncomposer: 1,
	beamslope: .4,			// max slope of a beam
//	botmargin: .7 * IN,		// != 1.8 * CM,
	breaklimit: .7,
	breakoneoln: true,
	cancelkey: true,
	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: "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,		// nice scores
	maxstaffsep: 2000,
	maxsysstaffsep: 2000,

	measurefont: {name: "serif", style: "italic", size: 10},
	measurenb: -1,
	musicspace: 6,
//	notespacingfactor: 1.33
	partsfont: {name: "serif", size: 15},
	parskipfac: .4,
	partsspace: 8,
//	pageheight: 29.7 * CM,
	pagewidth: 21 * CM,









	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: "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: "serif", weight: "bold", size: 13},
	vocalspace: 10,
	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

	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

	// "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 = {
			name: "sans-serif",
			size: 12

		}
	}
	font = Object.create(font);
	font.fid = font.used = undefined;
	cfmt[xxxfont] = font;

	// fill the values
	a = param.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])
	}

	a = param.match(/\s+class=(.*?)(\s|$)/)
	if (a) {
		font.class = a[1];
		param = param.replace(a[0], a[2])
	}
	a = param.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
		}






















		param = param.replace(a[0], a[2])
	}





	a = info_split(param)
	if (!a) {
		syntax(1, errs.bad_val, "%%" + xxxfont)
		return
	}

	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');
		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
			}
		}
	}

}

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

	switch (param.slice(-2)) {







|
|












>
>

|
|
>


<
<



|





|


|


|

|
















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

>
>
>
>
|
|
|
|
|

>
|
|
|
|
|
|
<
<

<
<
<
<
<
<
<
<
|
<
<
<
>







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, 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: font.name,
			size: font.size,
			box: font.box
		}
	}


	cfmt[xxxfont] = font;

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

	a = p.match(/\s+class=(.*?)(\s|$)/)
	if (a) {
		font.class = a[1];
		p = p.replace(a[0], a[2])
	}
	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"
		p = p.replace(a[0], '')
	}
	a = p.match(/[- ]?[iI]talic/)
	if (a) {
		font.style = "italic"
		p = p.replace(a[0], '')
	}
	a = p.match(/[- ]?[oO]blique/)
	if (a) {
		font.style = "oblique"
		p = p.replace(a[0], '')
	}
	switch (p) {
	case "":
	case "*": return
	case "Times-Roman":
	case "Times":	p = "serif"; break
	case "Helvetica": p = "sans-serif"; break
	case "Courier": p = "monospace"; break


	}








	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
}

/* -- 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/unset the fields to write
function set_writefields(parm) {
	var	c, i,
		a = parm.split(/\s+/)








|







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"
	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
			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 = '*'







<







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

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
		img.width = img.lm + img.rm + 150
	}
	set_posx()
} // set_page()

// set a format parameter
// (possible hook)
function set_format(cmd, param, lock) {
	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)
		break
	case "bgcolor":

	case "dblrepbar":
	case "titleformat":
		cfmt[cmd] = param
		break

	case "breaklimit":			// float values
	case "lineskipfac":
	case "maxshrink":
	case "pagescale":
	case "parskipfac":
	case "scale":
	case "slurheight":







|



<
<
<
<
<









<

















|


>




>







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)
Abc.prototype.set_format = function(cmd, param) {
	var f, f2, v, i

//fixme: should check the type and limits of the parameter values





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

	switch (cmd) {
	case "aligncomposer":
	case "barsperstaff":
	case "infoline":

	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
		}
		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
	case "dynalign":
	case "flatbeams":
	case "graceslurs":
	case "graceword":
	case "hyphencont":
	case "keywarn":
	case "linewarn":

	case "rbdbstop":
	case "singleline":
	case "squarebreve":
	case "splittune":
	case "straightflags":
	case "stretchstaff":
	case "timewarn":







>







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
		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")
			break
		}
//fixme: should check only alpha, num and '_' characters
		cfmt[cmd] = param
		break
	case "gracespace":
		v = param.split(/\s+/)







|







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, 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
		else
			cfmt[cmd] = v
		break
	case "combinevoices":
		syntax(1, "%%combinevoices is deprecated - use %%voicecombine instead")
		break
	case "voicemap":
		self.set_v_param("map", param)
		break
	case "voicescale":
		self.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 = '',
	a = fn.match(/-?[bB]old/)

	if (a) {
		r += "bold ";
		fn = fn.replace(a[0], '')
	}
	a = fn.match(/-?[iI]talic/)
	if (a) {
		r += "italic ";
		fn = fn.replace(a[0], '')
	}
	a = fn.match(/-?[oO]blique/)
	if (a) {
		r += "oblique ";
		fn = fn.replace(a[0], '')

	}

	return 'font:' + r + font.size.toFixed(1) + 'px ' + fn
}
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







|


|











|
|
|
<

|
|
<
<
<
|
|
<
<
<
|
<
|
>
|
>
|







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":
		set_v_param("map", param)
		break
	case "voicescale":
		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 st_font(font) {
    var	n = font.name,
	r = ""


	if (font.weight)
		r += font.weight + " "



	if (font.style)
		r += font.style + " "



	if (n.indexOf(' ') > 0)

		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
		}
		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];
































	use_font(font)
	return font
}







|
>
>
|
|
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



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(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
// 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/>.





// translation table from the ABC draft version 2.2
var abc_utf = {
	"=D": "Đ",
	"=H": "Ħ",
	"=T": "Ŧ",
	"=d": "đ",







>
>
>
>







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
	"5": "&#x1d12b;"
}

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

	while (1) {
		i = src.indexOf('\\', j)
		if (i < 0)
			break
		dst += src.slice(j, i);
		c = src[++i]







|







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

	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
				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
				j = i + 1
				continue
			}
			codeUnits = [j]
			if (j >= 0xd800 && j <= 0xdfff) {	// surrogates
				j = Number("0x" + src.slice(i + 7, i + 11));
				if (isNaN(j))
					break		// bad surrogate
				codeUnits.push(j);
				j = i + 11
			} else {
				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



			switch (c) {
			case '`':
				dst += src[++i] + "\u0300"	// grave
				j = i + 1
				continue
			case "'":
				dst += src[++i] + "\u0301"	// acute
				j = i + 1
				continue
			case '^':
				dst += src[++i] + "\u0302"	// circumflex
				j = i + 1
				continue
			case '~':
				dst += src[++i] + "\u0303"	// tilde
				j = i + 1
				continue
			case '=':
				dst += src[++i] + "\u0304"	// macron
				j = i + 1
				continue
			case '_':
				dst += src[++i] + "\u0305"	// overline
				j = i + 1
				continue
			case '.':
				dst += src[++i] + "\u0307"	// dot
				j = i + 1
				continue
			case '"':
				dst += src[++i] + "\u0308"	// dieresis
				j = i + 1
				continue
			case 'o':
				dst += src[++i] + "\u030a"	// ring
				j = i + 1
				continue
			case 'H':
				dst += src[++i] + "\u030b"	// hungarumlaut
				j = i + 1
				continue
			case 'v':
				dst += src[++i] + "\u030c"	// caron
				j = i + 1
				continue
//			case ',':
//				dst += src[++i] + "\u0326"	// comma below
//				j = i + 1
//				continue
			case 'c':
				dst += src[++i] + "\u0327"	// cedilla
				j = i + 1
				continue
			case ';':
				dst += src[++i] + "\u0328"	// ogonek
				j = i + 1
				continue
			}
			break
		}
		dst += '\\' + c;
		j = i + 1
	}
	// cleanup for XML treatment
	return (dst + src.slice(j)).replace(/<|>|  |&.*?;|&/g, function(c) {
			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
	}

	parse_sav = clone(parse);
	tosvg(fn, file);

	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,
		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)







|



<
<
<
|
<
<
<
<
|
<
<














>
>
>


|
|


|
|


|
|


|
|


|
|


|
|


|
|


|
|


|
|


|
|


|
|


|
|


|
|


|
|







<
|
<
<
<
<
<
<
<
<
<
<
<
















<





>


>








|



<
<







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 accent
				j = i + 1
				continue
			}



			dst += String.fromCharCode(j);




			j = i + 5


			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 += c2 + "\u0300"	// grave
				j = i + 2
				continue
			case "'":
				dst += c2 + "\u0301"	// acute
				j = i + 2
				continue
			case '^':
				dst += c2 + "\u0302"	// circumflex
				j = i + 2
				continue
			case '~':
				dst += c2 + "\u0303"	// tilde
				j = i + 2
				continue
			case '=':
				dst += c2 + "\u0304"	// macron
				j = i + 2
				continue
			case '_':
				dst += c2 + "\u0305"	// overline
				j = i + 2
				continue
			case '.':
				dst += c2 + "\u0307"	// dot
				j = i + 2
				continue
			case '"':
				dst += c2 + "\u0308"	// dieresis
				j = i + 2
				continue
			case 'o':
				dst += c2 + "\u030a"	// ring
				j = i + 2
				continue
			case 'H':
				dst += c2 + "\u030b"	// hungarumlaut
				j = i + 2
				continue
			case 'v':
				dst += c2 + "\u030c"	// caron
				j = i + 2
				continue
//			case ',':
//				dst += c2 + "\u0326"	// comma below
//				j = i + 2
//				continue
			case 'c':
				dst += c2 + "\u0327"	// cedilla
				j = i + 2
				continue
			case ';':
				dst += c2 + "\u0328"	// ogonek
				j = i + 2
				continue
			}
			break
		}
		dst += '\\' + c;
		j = i + 1
	}

	return dst + src.slice(j)











}

// 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
	}

	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, eol, end,
		select,
		line0, line1,
		last_info, opt, text, a, b, s,


		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
		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;


		init_tune()
		img.chg = true;
		set_page();
	} // end_tune()





























































































	// 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







<


















|
|
|
|
|
|
|
>
>





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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) {

		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 = 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
			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)

			if (!text || text[0] == '%')
				continue
			a = text.split(/\s+/, 2)
			switch (a[0]) {
			case "abcm2ps":
			case "ss-pref":
				parse.prefix = a[1]	// may contain a '%'
				continue
			case "abc-include":
				do_include(uncomment(a[1]))
				continue
			}

			// beginxxx/endxxx
			if (a[0].slice(0, 5) == 'begin') {
				b = a[0].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]);
					parse.eol = eof
					continue
				}
				self.do_begin_end(b, uncomment(a[1]),
					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]) {
			case "select":
				if (parse.state != 0) {
					syntax(1, "%%select ignored")
					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")
					continue
				}
				select = uncomment(text.slice(6))

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

				/* 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
				} 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
				}




				parse.eol = bol - 1
				continue















			}
			self.do_pscom(uncomment(text, true))
			continue
		}

		// music line (or free text)
		if (line1 != ':' || !/[A-Za-z+]/.test(line0)) {







<
<
<
<
<
<
<
<
<

>
|

<
|


|


|




|
|




|



|








|


|















<
<
<

|


|

|

|
<
<
<




|

<
<
|
|
|
<
<
|
|
|
<












|
|
>
>
>
>
>
>

|


<
<
<
<
<
<



>
>
>
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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:









			text = file.slice(bol, eol)
			a = text.match(/([^\s]+)\s*(.*)/)
			if (!a || a[1][0] == '%')
				continue

			switch (a[1]) {
			case "abcm2ps":
			case "ss-pref":
				parse.prefix = a[2]	// may contain a '%'
				continue
			case "abc-include":
				do_include(uncomment(a[2]))
				continue
			}

			// beginxxx/endxxx
			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[1]);
					parse.eol = eof
					continue
				}
				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[1]) {
			case "select":
				if (parse.state != 0) {
					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":



				if (parse.state != 0) {
					syntax(1, errs.not_in_tune, "%%tune")
					continue
				}
				select = uncomment(a[2])

				// if void %%tune, free all tune options
				if (!select) {
					parse.tune_opts = {}



					continue
				}
				
				if (select == "end")
					continue	// end of previous %%tune



				if (!parse.tune_opts)
					parse.tune_opts = {};
				parse.tune_opts[select] = opt = {


						t_opts: []
//						v_opts: {}
					};

				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]+)\s*(.*)/)
					switch (a[1]) {
					case "tune":
						break
					case "voice":
						do_voice(uncomment(a[2],
								true), true)
						continue
					default:
						opt.t_opts.push(
							uncomment(text, true))
						continue






					}
					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
				eol = file.indexOf('\nX:', parse.eol)
				if (eol < 0)
					eol = eof;
				parse.eol = eol
				continue
			}

			cfmt_sav = 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);
			info.X = text;
			parse.state = 1			// tune header


			continue
		case 'T':
			switch (parse.state) {
			case 0:
				continue
			case 1:

				if (info.T == undefined)	// (keep empty T:)
					info.T = text
				else
					info.T += "\n" + text
				continue
			}
			s = new_block("title");







|
<
|
|
|
|
|
|


>
>






>







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
			}

			sav.cfmt = clone(cfmt);

			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
			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
			break

		// info fields in tune body only
		case 's':
			if (parse.state != 3
			 || cfmt.writefields.indexOf(line0) < 0)
				break







<
<
<






|







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
			}



			a = text.match(/(.*?)[= ]+(.*)/)
			if (!a || !a[2]) {
				syntax(1, errs.bad_val, "m:")
				continue
			}
			mac[a[1]] = a[2];
			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
				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_music_line()
			continue
		default:
			if ("ABCDFGHNOSZ".indexOf(line0) >= 0) {
				if (parse.state >= 2) {
					syntax(1, errs.ignored, line0)
					continue







|







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 = 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
	}
}

// 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

		/* main chord */
		n = note_names.indexOf(p[0])

		if (n < 0) {
			if (p[0] != '/')
				return p
		}

		ip = 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]
		}

		ip2 = p.indexOf('/', ip)	// skip 'm'/'dim'..
		if (ip2 < 0)
			return new_txt + p.slice(ip);

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


		new_txt += p.slice(ip, ip2);
		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

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

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

// -- build the chord indications / annotations --
// (possible hook)
function gch_build(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,







|
|
|

|
|
>
|
<
<
<
|
|
<









<
<
<
<
<
|
<
|
<
<
<
|
<
<
<
|
|
>
|
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
|
|



|
<

|


|





|







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, transp) {
	    var	i, o, n, a, ip, b40,
		csa = p.split('/')

		for (i = 0; i < csa.length; i++) {	// main and optional bass
			p = csa[i];
			o = p.search(/[ABCDEFG]/);
			if (o < 0)



				continue		// strange chord symbol!
			ip = o + 1

			a = 0
			while (p[ip] == '#') {
				a++;
				ip++
			}
			while (p[ip] == 'b') {
				a--;
				ip++
			}





			n = note_names.indexOf(p[o]) + 16

			b40 = (abc2svg.pab40(n, a) + transp + 200) % 40



			b40 = abc2svg.b40k[b40]



			csa[i] = p.slice(0, o) +
					note_names[abc2svg.b40_p[b40]] +
					acc_name[abc2svg.b40_a[b40] + 2] +
					p.slice(ip)






		}










		return csa.join('/')
	} // gch_tr1

function gch_transp(s) {
    var	gch,
	i = s.a_gch.length


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

// -- build the chord indications / annotations --
// (possible hook)
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
			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,
				function(x) {
					switch (x) {
					case '##': return "&#x1d12a;"
					case '#': return "\u266f"
					case '=': return "\u266e"
					case 'b': return "\u266d"

					}
					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)
			wh[1] += 4
		switch (gch.type) {
		case '@':
			break
		case '^':			/* above */
			xspc = wh[0] * GCHPRE
			if (xspc > 8)







|






>



<
<

<
<
<











|







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,
				function(x) {
					switch (x) {
					case '##': return "&#x1d12a;"
					case '#': return "\u266f"
					case '=': return "\u266e"
					case 'b': return "\u266d"
					case '  ': return '  '
					}
					return "&#x1d12b;"
				});


		} else {



			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.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
	}
}

// -- 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) {
    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)
	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;
		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)







|













|
|











|







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)
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 - 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.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
	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),
		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,







|







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+ 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
// 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/>.



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' },

		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' },







>
>
|


>








>







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) {
	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
	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
	]),
	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]







|
>
|
>
>
|







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([
// 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
var w_note = new Float32Array([
	3.5,		// FULL
	3.7,		// EMPTY
	5,		// OVAL
	6,		// OVALBARS
	7		// SQUARE
])














































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)
		return			// single note

	/* set the head shifts */
	dx = dx_head * .78
	if (s.grace)
		dx *= .5
	if (dir >= 0) {







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







|







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)
		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
		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 (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
			}







|







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) {
			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
		}
	}
	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

	// 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;







|
|







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, 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
			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
		}


























		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







|
|
>
>
|
|
>
>
>
|
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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 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
		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;

	// search the notes with accidentals at the same time
	s = tsfirst
	while (s) {
		if (s.type != C.NOTE
		 || s.invis) {
			s = s.ts_next







|







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, 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
		}
		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: []
		}
		for ( ; s != s2; s = s.ts_next)
			st.notes = st.notes.concat(s.notes);
		sort_pitch(st);
		acc_shift(st.notes, dx_head)
	}
}

// link a symbol before an other one
function lkvsym(s, next) {	// voice linkage
	s.next = next;
	s.prev = next.prev







<
<
<


<
|
<

|
|
|







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]




		// build a pseudo chord and shift the accidentals

		notes = []

		for ( ; s != s2; s = s.ts_next)
			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
}

// 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
function set_w_chs(s) {
    var	i, ch, ch0, s0, dw, x,

	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
					while (1) {
						s0 = s0.ts_next
						if (s0.shrink)
							s0.shrink += dw
						if (s0 == s)
							break
					}
				}
			}
			s0 = s;
			ch0 = ch;
			n = 0;
//			x = ch.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
			if (w > wlw)
				wlw = w
			break
		case '>':		/* right */
			w = gch.w + 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) {
    var	s2, i, m, xx, w, wlnote, wlw, acc,
	bar_type, meter, last_acc, n1, n2, esp, tmp






	switch (s.type) {
	case C.NOTE:
	case C.REST:

		/* set the note widths */
		s.wr = wlnote = hw_tb[s.head]







|

|
>













|
|
|










|

|















|




|
















|


>
>
>
>
>







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 symbols under the staff are ignored
function set_w_chs(s) {
    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 (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;
			w0 = ch.wh[0];
			n = 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.wh[0] + wlnote
			if (w > wlw)
				wlw = w
			break
		case '>':		/* right */
			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)
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
			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
				break
			case C.EMPTY:
				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)







<
<


















<
<

<
|

|







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
			}


		}

		/* 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:


			case C.OVALBARS:

				s.xmx += 3
				break
			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
				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 (wlw < 14)
						wlw = 14
				}
				break
			case C.CLEF:		/* extra space at start of line */
				if (s2.second
				 || s2.clef_small)







|







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.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
		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







<
<







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:


		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
			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:






		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







>
>
>
>
>
>







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
		s.wr = wlw + 6
		return
	case C.MREST:
		s.wl = 6;
		s.wr = 66
		return
	case C.GRACE:


		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
		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)
				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:







>
>

















|
<
<
<
<
<
|
<
<
<
<
<
<
<







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 build the tempo string





		tempo_build(s)







		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
		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 < 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

		}
	}
	return space
}

/* -- set the natural space -- */
function set_space(s) {


	var	s2, space,




		prev_time = s.ts_prev.time,
		len = s.time - prev_time		/* time skip */

	if (len == 0) {
		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) {







|





|
>





|

>
>
|
>
>
>
>
|
|

|







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) {
		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 / ((C.BLEN / 16 / 8) << i)
		}
	}
	return space
}

// set the natural space
function set_space(s) {
    var	space, prev_time, len,
	s2 = s.ts_prev

	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) {
		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
				return space
			continue
		}
		break
	}

	/* reduce spacing within a beam */



	if (!s.beam_st)
		space *= .9			// ex fnnp








	/* 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








>
>
>
|
|
>
>
>
>
>
>
>







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 (!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

// 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

	// 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)







|







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.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
		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
 * 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

		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) {
			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--;
			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
			s = s.ts_next
			if (s.tp0)
				ntup++
			if (s.te0)
				ntup--
		} while (ntup != 0);




		set_sp_tup(s2, s)

		do {			// search next tuplet
			s = s.ts_next
		} while (s && !s.tp0)
	} while (s)
}

/* change a symbol into a rest */
function to_rest(s) {







	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
/*fixme: should set many parameters for set_width*/
//	set_width(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

	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 */







|
<










<
|
>




















|















|
|
|
|
>
>
|
|














|
|
<
|
|
|
|
|
>
>
>





|



|
|
>
>
>
>
>
>
>




|
|
|
|


>




|
|
|
|
|







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

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 */

	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 && !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.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 = 0
		while (1) {		// search the end of the tuplet sequence

			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.tp)
	} while (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.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

	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
		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) {


					unlksym(s2)
					if (s2.v == v
					 && s2.dur)
						i--
				}
				s2 = s2.ts_next
			}
			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
			}







>
>
|






|




<
<







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)
						s2.seqst = false
					if (s2.v == v
					 && s2.dur)
						i--
				}
				s2 = s2.ts_next
			}
			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)


			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
	/* 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.v == v
			 && s2.type == C.BAR)
				break




			unlksym(s2)
		}
		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.v == v
			 && s2.type == C.BAR)
				break




			unlksym(s2)
		}
		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.v == v
			 && s2.type == C.BAR)
				break




			unlksym(s2)
		}
		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
	}







>
|
<
|
>
>
>
>
|

|


<
<

<
<




>
|
<
|
>
>
>
>
|

|



<
<
<
<









>
|
<
|
>
>
>
>
|

|
>


<
<
<
<







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)

					break
				continue
			}
			s2.invis = s2.play = true
			if (s2.seqst && s2.ts_next.seqst)
				s2.seqst = false
		}
		s3 = to_rest(s3)
		s3.dur = s3.notes[0].dur = dur;
		s3.invis = true


		s2.bar_mrep = 2


		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)

					break
				continue
			}
			s2.invis = s2.play = true
			if (s2.seqst && s2.ts_next.seqst)
				s2.seqst = false
		}
		s3 = to_rest(s3)
		s3.dur = s3.notes[0].dur = dur;
		s3.invis = true;
		self.set_width(s3)




		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)

					break
				continue
			}
			s2.invis = s2.play = true
			if (s2.seqst && s2.ts_next.seqst)
				s2.seqst = false
		}
		s3 = to_rest(s3)

		s3.dur = s3.notes[0].dur = dur;
		s3.beam_st = true




		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
			dur: C.BLEN / 4
		}
	}
	new_s.stemless = true
}

/* -- define the beginning of a new music line -- */
function set_nl(s, eoln) {
    var	s2, s3, p_voice, done

	// set the end of line marker and ...
	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
				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







|


|



<
<
<
<
|
<
<
<







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) {
    var	s2, s3, p_voice, done

	// set the end of line marker
	function set_eol(s) {
		if (cfmt.custos && voice_tb.length == 1)
			custos_add(s)




		s.nl = true



	} // 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
	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)



					continue

			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







|
>
>
>

>







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
				 || (!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
				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 */







<
<
<
<







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 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
	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


function get_width(s, last) {
	var	shrink, space,
		w = 0,

		sp_fac = (1 - cfmt.maxshrink)

	do {
		if (s.seqst) {
			shrink = s.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)


	return w;
}

/* -- 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;




	for ( ; last; last = last.ts_next) {
		if (last.eoln)
			break
	}


	/* calculate the whole size of the piece of tune */
	wwidth = get_width(s, last) + indent





	/* loop on cutting the tune into music lines */

	while (1) {
		nlines = Math.ceil(wwidth / lwidth)
		if (nlines <= 1) {
			if (last)
				last = set_nl(last)
			return last
		}







>
>

|
|
>
|




>











>
>
|







|
|

>
>
>
|
|
<
|
>

|
|
>
>
>
|
>

>







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,
	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, 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, 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
	 && last.next.type == C.KEY)
		last = last.next


	ws = get_width(s, last)		// 2 widths: nice and shrinked

	// 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
		indent = 0
	}
	return s
}

/* -- cut the tune into music lines -- */
function cut_tune(lwidth, indent) {
	var	s, s2, s3, i, xmin, lwx,
//fixme: not usable yet
//		pg_sav = {
//			leftmargin: cfmt.leftmargin,
//			rightmargin: cfmt.rightmargin,
//			pagewidth: cfmt.pagewidth,
//			scale: cfmt.scale
//		},
		s = tsfirst

	// take care of the voice subnames
	if (indent != 0) {
		i = set_indent()
		lwidth -= i;
		indent -= i;
	}

	/* adjust the line width according to the starting clef
	 * and key signature */







|







|


|







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	s2, i,
//fixme: not usable yet
//		pg_sav = {
//			leftmargin: cfmt.leftmargin,
//			rightmargin: cfmt.rightmargin,
//			pagewidth: cfmt.pagewidth,
//			scale: cfmt.scale
//		},
	s = tsfirst

	// take care of the voice subnames
	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
				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
		xmin += s.shrink
		if (xmin > lwx) {		// overflow
			s2 = set_lines(s2, s, lwidth, indent)
		} 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
					s2 = set_nl(s, true)
			} else {
				s2 = set_nl(s, true)
			}
		}
		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;







<
<
















|
|
<
<
<
<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
|
<
|
|
<
<









<







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 */


	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.ts_next)
			s = null




		else if (!s.eoln)
			continue












		else

			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
		}

		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
	default:
//		s.ymx = top;
		s.ymx = 24;
		s.ymn = 0
		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 */







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
 *	- 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 }
		sy = cur_sy,
		mid = []

	// create the staff table
	staff_tb = new Array(nstaff)
	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)
			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

	}
	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (sy.voices[v].range < 0
		 || 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,







|




|













|











|
<
|
|
>



|







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 + 1),	// st -> { clef, autoclef }
		sy = cur_sy,
		mid = []

	// create the staff table
	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])
			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 (!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]
		 || 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

		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)
					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
				 || 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...







|




















|







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])
					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]
				 || 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
		}
		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)
			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:







|







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])
			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
	[6, 4],
	[10, 0],
	[10, 4],
	[10, 10]
]

// (possible hook)
function set_pitch(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
				 && !s.p_v.key.k_drum) {
					for (m = 0; m <= g.nhd; m++) {
						note = g.notes[m];

						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)







|



<

<
<


<
<
<
<
<
<
<
<













<
<
<




<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<













<




|
|



>







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)
Abc.prototype.set_pitch = function(last_s) {
	var	s, s2, g, st, delta, pitch, note,
		dur = C.BLEN,
		m = nstaff + 1,

		staff_delta = new Int16Array(new Array(m * 2)),	// delta clef


		sy = cur_sy









	// 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
		}



	}

	for (s = tsfirst; s != last_s; s = s.ts_next) {
		st = s.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
			}

			set_yval(s)
			break
		case C.GRACE:
			for (g = s.extra; g; g = g.next) {
				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
//				s.ymn = 12 - 8
				s.ymx = 24;
				s.ymn = 0
				break
			}
			// fall thru
		case C.NOTE:
			delta = staff_d[st]
			if (delta != 0
			 && !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
				}
			}
			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;







|
|


|
>
|







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_delta[st]
			if (delta
			 && !s.p_v.key.k_drum) {
				for (m = s.nhd; m >= 0; m--) {
					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
	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() {
	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,







|







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)
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
			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_gch && s.ts_next.a_gch)
				 || s.invis)
					break


				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







>



>
>







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
			s.xmx = dx
			continue
		}
	}
}

/* -- create a starting symbol -- */

function new_sym(type, p_voice,
			last_s) {	/* symbol at same time */
	var s = {
		type: type,
		fname: last_s.fname,
//		istart: last_s.istart,
//		iend: last_s.iend,
		v: p_voice.v,
		p_v: p_voice,
		st: p_voice.st,
		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;

	lktsym(s, last_s)
	if (s.ts_prev.type != type)
		s.seqst = true
	if (last_s.type == 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)
			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
		 && !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
		 || (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







>
|
<
|
<
<
<
<
|
|
<
|
<
|
>
>
|
|
|
|
>
|


|

|



<









|
















|











|







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(s, p_v, last_s) {

	s.p_v = p_v




	s.v = p_v.v
	s.st = p_v.st

	s.time = last_s.time


	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 != s.type)
		s.seqst = true
	if (last_s.type == s.type && s.v != last_s.v) {
		delete last_s.seqst;
		last_s.shrink = 0
	}

}

/* -- 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])
			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]
		 && !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]
		 || (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
// (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
		 || 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 = new_sym(C.KEY, p_voice, last_s);
			s.k_sf = s2.k_sf;
			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
			 || cur_sy.voices[v].second
			 || !cur_sy.st_print[cur_sy.voices[v].st]
			 || s2.a_meter.length == 0)
				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 = new_sym(C.METER, 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 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
		 || !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);
		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,
	 * including the first (old) sequence */
	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 (w_tb[s3.type] != 0) {
					shr += s3.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
	}













}

/* -- set a pitch in all symbols and the start/stop of the beams -- */

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) {







|












>
|
<
|
<
<
<
<
<
<
<
<
<








|


|






>
|
<
<
<
<



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
















|



<
<
<
<
<
<
|
<
<
<
<
<
<
|
|
<
|
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








|
<
|
<
<
<









>
>
>
>
>
>
>
>
>
>
>
>
>



>







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]
		 || 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)
			new_sym(s, p_voice, last_s)

			s.k_old_sf = s2.k_sf	// no key cancel









		}
	}

	/* 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]
			 || cur_sy.voices[v].second
			 || !cur_sy.st_print[cur_sy.voices[v].st]
			 || !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)
			new_sym(s, p_voice, last_s)




		}
		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]
		 || !cur_sy.st_print[cur_sy.voices[v].st])
			continue







		new_sym(s2, p_voice, last_s)






	}


	// compute the spacing of the added symbols

	self.set_pitch(last_s);




















	s = tsfirst
	while (1) {
		s2 = s;
		shrmx = 0
		do {
			self.set_width(s);
			shr = s.wl
			if (s.prev)

				shr += s.prev.wr



			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
				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.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)







>
>
>
>







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
			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);
			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)







|
|







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)
				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
	}
	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

	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;
						s2 = null
					}
					break
				}
				if (n == cfmt.rbmin)
					s2 = s
			}
			if (s2) {
				s2.rbstop = 1;
				mx = cfmt.rbmin
			}
		}
		while (s) {

			/* check repbra shifts (:| | |2 in 2nd staves) */
			if (s.rbstart != 2) {
				s = s.next
				if (!s)
					break
				if (s.rbstart != 2) {
					s = s.next
					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
				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  */







|
|






<
<
<
<
<
|
|
|
|
|
|
|
>
|
|
<
<
|
|
|
|
<
<
<
|
<
<
<
<
<
<
<
|
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
|
|
<
<
<
<







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, n,
	s = p_voice.sym

	while (s) {
		if (s.type != C.BAR || !s.rbstart || s.norepbra) {
			s = s.next
			continue
		}





		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


				break
			}
			if (n == cfmt.rbmin)
				s2 = s



			if (n == cfmt.rbmax) {







				if (s2)


					s = s2;












				s.rbstop = 1	// right repeat without end
				break




			}
		}
	}
}

/* -- 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
// (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();




	// 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)
			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)







>
>
>














|







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])
			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
		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

	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 */







|
|







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, 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
			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)


						continue

					if (g.stem || g.multi) {
						s.stem = g.stem || g.multi
						break
					}



























					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 {
//--fixme: equal: check all notes of the beam
						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)
				}
//				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







|
>
|
|
|
>
>

>
|



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
>
>







<
|
|
>
>
|

<







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;

				// 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 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 < 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 (!s.stem)
					s.stem = laststem
			} else {				// no beam
				n = (s.notes[s.nhd].pit + s.notes[0].pit) / 2
				if (n == mid_p) {

					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
				}

				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
			if (s1.shiftunison && (s1.shiftunison & 2))
				return false
			head = s2.dur >= C.BLEN / 2 ? 2 : 1
		} else {
			return false
		}
	}
	if (head == 0)
		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++)







|







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)
		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

	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


	// 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)


			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







|
|
|
>













|
>
>

>







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,
	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) {
			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
				break
		}
		if (!s2)
			continue
		s1 = s

		/* set the dot vertical offset */
		if (cur_sy.voices[s1.v].range < cur_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



















		/* 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







|











>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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 (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
		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) {

					t = -1
					break
				}
				if (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) {







|
>



|
>
>

>





<
<
<
<









<




<
<
<


<







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
				 && !s1.notes[i1].acc && !s2.notes[i2].acc) {
					t = -1
					break
				}
				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:




				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
					}
				}

				break
			case -2:
				if (s1.dots && s2.dots
				 && !(s1.notes[i1].pit & 1)) {



					s1.dot_low = false;
					s2.dot_low = false

					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
		}

		sd = 0;
		if (s1.dots) {
			if (s2.dots) {
				if (!t)			/* if no dot clash */
					sd = 1		/* align the dots */
//fixme:dots

			}
		} 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)







|
>




<







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 */
			} else {
				v_invert()		// shift the first voice
			}
		} else if (s2.dots) {
			if (d2 + dr < d + dr2)
				sd = 1		/* align the 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
			s1.xmx = s2.xmx		// align the dots
	}
}

/* -- set the stem height -- */
/* this routine is called only once per tune */
// (possible hook)
function set_stems() {
	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







|







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)
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
			  || !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)
				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)
				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

	/* 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







|




















|









|
|







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].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].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, 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
	}
	bar_type = s.bar_type
	if (bar_type == ":")
		return
	if (bar_type.slice(-1) != ':')		// if not left repeat bar
		return


	if (!p_voice.bar_start)

















		p_voice.bar_start = clone(s)





























	if (bar_type[0] != ':') {		// 'xx:' (not ':xx:')
		if (bar_type == "||:") {
			p_voice.bar_start.bar_type = "[|:";
			s.bar_type = "||"
			return true
		}
		p_voice.bar_start.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)
	} 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)
	}
	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) {







>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|



|
















|



|







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
	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 == "||:") {
			s_bs.bar_type = "[|:"
			s.bar_type = "||"
			return true
		}
		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--;
		s_bs.bar_type = '[|' + bar_type.slice(i + 1)
	} else {
		i = (bar_type.length / 2) |0;			// '::::' !
		s.bar_type = bar_type.slice(0, 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
		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);
		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,







|













<
<
<







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((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



	}
}

/* -- 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
				}
			}
		}
	} // set_brace()

	// set the top and bottom of the staves
	function set_top_bot() {
		var st, p_staff, i, l, hole

		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

			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

















				}
			}
		}
	} // set_top_bot()

	/* reset the staves */
	nstaff = nst = sy.nstaff







|










|
|
>
>
>
>
|
>








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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, 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++) {
				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
//				blocks.push(s);
//				unlksym(s);
//				s = s.ts_next
//			}
			break
		}
		if (!s.ts_next)
			last = s		// keep the last symbol
		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:

			blocks.push(s);
			unlksym(s)
			if (last)
				last = s.ts_prev
			continue
		}
		st = s.st
		if (non_empty[st])
			continue
		switch (s.type) {
		default:







|
















>
|
<
<
<







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 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)



			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
		 && last.type != C.KEY)
			s.space = s.shrink
	}
}

/* -- position the symbols along the staff -- */
// (possible hook)
function set_sym_glue(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







|







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)
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
			} else {
				xs += s.shrink
			}
		}
	}

	// can occur when bar alone in a staff system
	if (xx == 0) {
		realwidth = 0
		return
	}

	// last line?
	ll = !tsnext ||			// yes
		tsnext.type == C.BLOCK	// no, but followed by %%command

		|| 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),







|






|
>







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) {
		realwidth = 0
		return
	}

	// last line?
	ll = !tsnext ||			// yes
		(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
		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

	// 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)



			s.prev = null



	}
}

// set the left offset the images
function set_posx() {
	posx = img.lm / cfmt.scale
}







|
|


|
|
>
>
|
|
|
>
>
>

>
>
>







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_v, s,
	v = voice_tb.length

	// set the first symbol of each voice
	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
			return
		default:
			continue
		case C.STAVES:
			cur_sy = s.sy
			break
		case C.BLOCK:


			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

	gen_init()
	if (!tsfirst)
		return

	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 */








>
>












|
|




>







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)
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
	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);
		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)
	}







	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)
				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) {
				posx -= indent;
				insert_meter &= ~2	// no more indentation
			}
			while (blocks.length != 0)
				block_gen(blocks.shift())
		}

		tsfirst = tsnext
		if (cfmt.splittune)
			blk_flush()
		else
			svg_flush()


		if (!tsnext)
			break



		// next line
		gen_init()
		if (!tsfirst)
			break

		tsfirst.ts_prev = null;
		set_sym_line();
		lwidth = get_lwidth()	// the image size may have changed
		indent = set_indent()
	}










}

/* -- reset the generator -- */
function reset_gen() {
	insert_meter = cfmt.writefields.indexOf('M') >= 0 ?
				3 :	/* insert meter and indent */
				2	/* indent only */
}







|








>
>
>
>
>
>




|
|







|



|



<




>
>


>
>





>





>
>
>
>
>
>
>
>
>
>








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)[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) {
			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) {
				posx -= indent;
				insert_meter &= ~2	// no more indentation
			}
			while (blocks.length)
				block_gen(blocks.shift())
		}


		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
//
// 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
			//	-> 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
}







|
<
<
<
<


<







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 - see set_map()




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
		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_drum = true	// no transpose
		break
	default:
		syntax(1, "Unknown clef '$1'", clef_def)
		return //undefined
	}
	if (clef_def[i] >= '1' && clef_def[i] <= '9') {







>
>
>







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
	case '-':
		s.clef_octave = -7
		break
	}
	return s
}

var note_pit = new Int8Array([0, 2, 4, 5, 7, 9, 11])

// get a transposition value
function get_transp(param,
			type) {		// undefined or "instr"
	var	i, val, tmp, note,
		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.buffer = param

	for (i = 0; i < 2; i++) {
		note = tmp.buffer[tmp.index] ? parse_acc_pit(tmp) : null
		if (!note) {

			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
	}

	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 val - 3
}

// set the linebreak character
function set_linebreak(param) {
	var i, item

	for (i = 0; i < 128; i++) {







<
|
<
|
<
|
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|

>



>
|
|
|
<
<
<
<
|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<


<
|
<







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
}


// convert an interval to a base-40 interval

function get_interval(param, score) {

    var	i, val, tmp, note, pit









































	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
			}




			pit[i] = 23			// 'c' (C5)
		} else {
			pit[i] = abc2svg.pab40(note.pit, note.acc)













		}
	}

	return pit[1] - pit[0]

}

// 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
		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(/(.*?)[= ]*([!"].*[!"])/)

	if (!a) {
		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 '!':

		if (char_tb[k].length > 1)
			break
		// fall thru
	default:
		syntax(1, "Bad user character '$1'", c)
		return
	}







|


|




<
<
<
<




















>







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(/(.)[= ]*(\[I:.+\]|".+"|!.+!)$/)

	if (!a) {
		syntax(1, 'Lack of starting [, ! or " in U: / %%user')
		return
	}
	c = a[1];
	v = a[2]




	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
		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)
	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
}

// 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);
	curvoice = voice_s
	return s
}

// set the voice parameters
// (possible hook)
function set_vp(a) {
    var	s, item, pos, val, clefpit

	while (1) {
		item = a.shift()
		if (!item)
			break
		if (item[item.length - 1] == '='
		 && a.length == 0) {
			syntax(1, errs.bad_val, item)
			break
		}
		switch (item) {
		case "clef=":
			s = a.shift()		// keep last clef
			break







<
<





|








<
<
<
|












<
<
|
<





|






|
|







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) {


	if (!param)
		return
	if (/^[\]\[|.-]+$/.test(param))
		return param.replace(/\]/g, '[')

    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



	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()


	sym_link(s)

	return s
}

// set the voice parameters
// (possible hook)
Abc.prototype.set_vp = function(a) {
    var	s, item, pos, val, clefpit

	while (1) {
		item = a.shift()
		if (!item)
			break
		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
			else
				curvoice[item.slice(0, -1)] = val
			break
		case "cue=":
			curvoice.scale = a.shift() == 'on' ? .7 : 1
			break
		case "instrument=":













			curvoice.transp = get_transp(a.shift(), 'instr')
			break
		case "map=":			// %%voicemap
			curvoice.map = a.shift()
			break
		case "name=":
		case "nm=":
			curvoice.nm = a.shift()







>
>
>
>
>
>
>
>
>
>
>
>
>
|







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 = 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
			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=":



			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())
			break
		case "sound=":
		case "transpose=":		// (abcMIDI compatibility)




			if (!cfmt.sound)
				break
			curvoice.transp = get_transp(a.shift())
			break
		case "subname=":
		case "sname=":
		case "snm=":
			curvoice.snm = a.shift()
			if (curvoice.snm[0] == '"')
				curvoice.snm = curvoice.snm.slice(1, -1);







>
>
>


<
<
<



|



>
>
>
>
|
<
|







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



			curvoice.transp = get_transp(item)
			break
		case "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)

				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
		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)
		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)
		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,
			k_delta: 0,
			dur:0
		}




























































	set_ref(s);

	// tonic
	i = 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;
			i++
			break
		default:
			syntax(1, "Unknown bagpipe-like key")

			break
		}
		break
	case 'P':
		syntax(1, "K:P is deprecated");

		s.k_drum = true;
		key_end = true
		break
	case 'n':				// none
		if (param.indexOf("none") == 0) {
			s.k_sf = 0;
			s.k_none = true;
			i = 4

		}
		// fall thru
	default:
		key_end = true
		break
	}


	if (!key_end) {
		switch (param[i]) {
		case '#': s.k_sf += 7; i++; break
		case 'b': s.k_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;
			mode = 5
			break
		case "dor": s.k_sf -= 2;
			mode = 1
			break
		case "ion":
		case "maj": break
		case "loc": s.k_sf -= 5;
			mode = 6
			break
		case "lyd": s.k_sf += 1;
			mode = 3
			break
		case "mix": s.k_sf -= 1;
			mode = 4
			break
		case "phr": s.k_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
		}
		c = param[0]
		if (c == '^' || c == '_' || c == '=') {
			s.k_a_acc = [];
			tmp = new scanBuf();
			tmp.buffer = param
			do {
				var note = parse_acc_pit(tmp)
				if (!note)
					return [s, null]
				s.k_a_acc.push(note);
				c = param[tmp.index]
				while (c == ' ')
					c = param[++tmp.index]
			} while (c == '^' || c == '_' || c == '=');


			param = param.slice(tmp.index)
		} else if (s.k_exp && param.indexOf("none") == 0) {
			s.k_sf = 0;
			param = param.replace(/\w+\s*/, '')
		}
	}


	s.k_delta = cgd2cde[(s.k_sf + 7) % 7];











	s.k_mode = mode




	return [s, info_split(param)]
}

// M: meter
function new_meter(p) {
	var	s = {
			type: C.METER,







|






|












|
>
|
|
|
<
|
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




>

<
<
<
<
<
<
<





|




>





>





|


>



<
|

>



|
|













|


|




|


|


|


|











|




|




|





>
>

|
|




>
|
>
>
>
>
>
>
>
>
>
>
>


>
>
>







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)
		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)
		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, exp,
	sf = "FCGDAEB".indexOf(param[0]) - 1,
	mode = 0,
	s = {
		type: C.KEY,

		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 'H':				// bagpipe
		switch (param[1]) {
		case 'P':
		case 'p':
			s.k_bagpipe = param[1];
			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) {
			sf = 0;
			s.k_none = true;
			i = 4
			break
		}
		// fall thru
	default:

		return [s, info_split(param)]
	}
    }

	if (!key_end) {
		switch (param[i]) {
		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": sf -= 3;
			mode = 5
			break
		case "dor": sf -= 2;
			mode = 1
			break
		case "ion":
		case "maj": break
		case "loc": sf -= 5;
			mode = 6
			break
		case "lyd": sf += 1;
			mode = 3
			break
		case "mix": sf -= 1;
			mode = 4
			break
		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'");
			exp = true
		}
		c = param[0]
		if (c == '^' || c == '_' || c == '=') {
			s.k_a_acc = [];
			tmp = new scanBuf;
			tmp.buffer = param
			do {
				var note = parse_acc_pit(tmp)
				if (!note)
					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 (exp && param.indexOf("none") == 0) {
			sf = 0
			param = param.replace(/\w+\s*/, '')
		}
	}

	s.k_sf = sf;

	// 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
					m2 = 4
				}
				break
			case 'c':
			case 'o':
				meter.top = p[i++]
				if (!m1) {
					if (p[-1] == 'c') {
						m1 = 2;
						m2 = 4
					} else {
						m1 = 3;
						m2 = 4
					}
					switch (p[i]) {
					case '|':
						m2 /= 2
						break
					case '.':
						m1 *= 3;
						m2 *= 2
						break
					}
				}
				break
			case '.':
			case '|':

				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);







|

|


|



|



|






>







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[i - 1] == 'c') {
						m1 = 2;
						m2 = 4	// c = 2/4
					} else {
						m1 = 3;
						m2 = 4	// o = 3/4
					}
					switch (p[i]) {
					case '|':
						m2 /= 2	// c| = 2/2, o| = 3/2
						break
					case '.':
						m1 *= 3;
						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
			sym_link(s)
		}
	}
}

/* Q: tempo */
function new_tempo(text) {
	var	i = 0, j, c, nd, tmp,
		s = {
			type: C.TEMPO,
			dur: 0
		}

















	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) {
			syntax(1, "Unterminated string in Q:")
			return
		}
		s.tempo_str1 = text.slice(1, i);


		i++

		while (text[i] == ' ')
			i++



	}

	/* beat */
	tmp = new scanBuf();

	tmp.buffer = text;
	tmp.index = i
	while (1) {
//		c = tmp.char()
		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])
		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. ';
			tmp.index += 4;
//			c = text[tmp.index]
		}
		if (text[tmp.index + 1] != '/') {

			s.tempo = tmp.get_int()
		} else {
			nd = parse_dur(tmp);
			s.new_beat = C.BLEN * nd[0] / nd[1]
		}
		c = text[tmp.index]
		while (c == ' ')
			c = text[++tmp.index]
	}

	/* string after */
	if (c == '"') {
		tmp.index++;
		i = text.indexOf('"', tmp.index + 1)
		if (i < 0) {
			syntax(1, "Unterminated string in Q:")
			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







|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








|
|



|
>
>
|
>
|
<
>
>
>



|
>
|
|
|
|
<
|
|
|
>
>
|
|
|
<
<
<
<
<
<

<
<
<
<
<
<
<
|
|
|
|
<
|

|
>
|
|
|
|
|
<
<
<
<
|
<
<
<
<
|
|
|
|
<
>







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, 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] == '"') {
		c = text.match(/"([^"]*)"/)		// "
		if (!c) {
			syntax(1, "Unterminated string in Q:")
			return
		}
		s.tempo_str1 = c[1]
		text = text.slice(c[0].length).replace(/^\s+/,'')
	}

	// string after
	if (text.slice(-1) == '"') {

		i = text.indexOf('"')
		s.tempo_str2 = text.slice(i + 1, -1)
		text = text.slice(0, i).replace(/\s+$/,'')
	}

	/* beat */
	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()

			if (!c)
				break
			nd = get_nd(c)
			if (!nd)
				return
			if (!s.tempo_notes)
				s.tempo_notes = []
			s.tempo_notes.push(nd)






		}








		// tempo value
		if (text.slice(0, 4) == "ca. ") {
			s.tempo_ca = 'ca. '

			text = text.slice(4)
		}
		i = text.indexOf('/')
		if (i > 0) {
			nd = get_nd(text)
			if (!nd)
				return
			s.new_beat = nd
		} else {




			s.tempo = Number(text)




			if (!s.tempo || isNaN(s.tempo)) {
				syntax(1, "Bad tempo value")
				return
			}

		}
	}

	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
			break
		new_tempo(text)
		break
	case 'V':
		get_voice(text)
		break

	// key signature at end of tune header on in tune body
	case 'K':
		if (parse.state == 0)
			break
		get_key(text)
		break

	// info in any state
	case 'N':
	case 'R':







|

|







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 or in tune body
	case 'K':
		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
	}
}

// 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;

	/* 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


	/* 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
			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;
		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
					 * curvoice.wmeasure / auto_time;
	}
	curvoice.time = s.time = time
}

/* -- parse a bar -- */
function new_bar() {
	var	s2, c, bar_type,







|














>







|
















|
|




|
<







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, 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 * 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 *= 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 *= fac

	}
	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
	}
	if (bar_type[0] == ':') {
		if (bar_type.length == 1) {	// ":" alone
			bar_type = '|';
			s.bar_dotted = true
		} else {
			s.rbstop = 2		// right repeat with end


		}
	}

	// 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) {
		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
	}

	/* 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





























	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 == "["
			|| 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







>
>












|














|



















<
<










<











<
<
<
<


<




















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





<
<
|







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.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
		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') {


		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) == '[') {

		s.text = ""
		while (1) {
			c = line.next_char()
			if (!c) {
				syntax(1, "No end of repeat string")
				return
			}
			if (c == '"') {
				line.index++
				break
			}




			s.text += c
		}

		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 (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
		err = false,
		flags = 0,
		brace = 0,
		bracket = 0,
		parenth = 0,
		flags_st = 0,
	e,
	a = p.match(/\w+|[^\s]/g)

	if (!a) {
		syntax(1, errs.bad_val, "%%staves")
		return // null
	}
	while (1) {
		e = a.shift()
		if (!e)
			break
		switch (e) {







|


|







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(/[^[\]|{}()*+\s]+|[^\s]/g)

	if (!a) {
		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
		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
			}

			/* get / create the voice in the voice table */
			vid = e
			while (1) {
				e = a.shift()
				if (!e)
					break
				switch (e) {
				case ']':







|
|
>
|
|
|
<
<
|







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
		case ']':
		case '}':
		case ')':
			syntax(1, "Bad voice ID in %%score");
			err = true
			break


		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
			if (!e)
				break
			a.unshift(e)
			break
		}
	}
	if (flags_st != 0) {
		syntax(1, "'}', ')' or ']' missing in %%staves");
		err = true
	}
	if (err || a_vf.length == 0)
		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;








|


|

















<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







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 %%score");
		err = true
	}
	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
}




















































// 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
	while (c == ',') {
		pit -= 7;
		c = line.next_char()
	}
	note = {
		pit: pit,
		shhd: 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
}


// convert a note pitch to ABC text


function note2abc(note) {
    var	i,
	abc = 'abcdefg'[(note.pit + 77) % 7]

//fixme: treat microtone


	if (note.acc)
		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 += ","
	return abc
}

/* set the mapping of a note */
function set_map(note) {
    var	map = maps[curvoice.map],	// never null
	nn = note2abc(note)


	if (!map[nn]) {
		nn = 'octave,' + nn.replace(/[',]/g, '')	// octave '
		if (!map[nn]) {
			nn = 'key,' +			// 'key,'
				'abcdefg'[(note.pit + 77 -
						curvoice.ckey.k_delta) % 7]

			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
		note.acc = note.map[1].acc
	}





}

/* -- 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)







|
<











>
|
>
>
|
<
|
|
|
>
>
|
<
<
<
<
|
<
<
|
<


|
>


|

<
<
|
>








|
|


>
>
>
>
>







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

	}
	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 */
//
// The global 'maps' object is indexed by the map name.
// Its content is an object ('map') indexed from the map type:
// - normal = b40 pitch

// - 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)




//	[2] color


//	[3] play (b40)

function set_map(note) {
    var	map = maps[curvoice.map],	// never null
	n = note.b40,
	nn = n.toString()

	if (!map[nn]) {
		nn = 'o' + (n % 40).toString()		// octave
		if (!map[nn]) {


			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]) {				// 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
	}
	nd = parse_dur(line);
	note.dur = ulen * nd[0] / nd[1]
	return note
}

function parse_vpos() {
	var	c,
		line = parse.line,
		ti1 = 0

	if (line.buffer[line.index - 1] == '.' && !a_dcn)
		ti1 = C.SL_DOTTED
	switch (line.next_char()) {
	case "'":
		line.index++
		return ti1 + C.SL_ABOVE
	case ",":
		line.index++
		return ti1 + C.SL_BELOW
	}
	return ti1 + 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])

/* transpose a note / chord */
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



	if (t < 0 && dp != 0)
		dp -= 7;
	dp += ((t / 3 / 12) | 0) * 7
	for (i = 0; i <= m; i++) {
		note = s.notes[i];






		// pitch

		n = note.pit;
		note.pit += dp;




		// accidental
		i1 = cde2fcg[(n + 5 + 16 * 7) % 7];	/* fcgdaeb */
		a = note.acc
		if (!a) {
			if (!curvoice.okey.a_acc) {
				if (sf_old > 0) {
					if (i1 < sf_old - 1)
						a = 1	// sharp
				} else if (sf_old < 0) {
					if (i1 >= sf_old + 6)
						a = -1	// flat
				}

			} else {
				for (j = 0; j < curvoice.okey.a_acc.length; j++) {
					acc = curvoice.okey.a_acc[j]
					if ((n + 16 * 7 - acc.pit) % 7 == 0) {
						a = acc.acc

						break

					}











				}



			}
		}
		i3 = i1 + i2
		if (a && a != 3)				// ! natural
			i3 += a * 7;


		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

		}
		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) {
					n -= d / 2;
					note.micro_n = n;
					a = i1
				} else {
					a = -i1
				}
				break
			case 2:			// double sharp
				if (n > d / 2) {

					note.pit += 1;
					n -= d / 2
				} else {
					n += d / 2
				}
				a = i1;
				note.micro_n = n
				break
			case -2:		// double flat
				if (n >= d / 2) {

					note.pit -= 1;
					n -= d / 2
				} else {
					n += d / 2
				}


				a = i1;
				note.micro_n = n
				break
			}
		}







		note.acc = a



	}
}

/* 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)
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,







<
|
|


|



|


|

|


|
|
|

|
<
<
<
|
|
|
|
>

>
|
|
|
<
|
>
>
>
>
>

|
>
|
|
>
>
>

|
<
|
<
<
<
<
|
<
<
|
|
>
|
<
<
|
<
>
|
>
|
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
|
|
<
<
<
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
>
>
>
|
|
|
>
|
<
<
<
<
<
<
<
<
<
|
<
|
|
<
|
|
|
|
<
>
|
<
<
<
|
|
|
<
<
|
>
|
|
|
|
<
>
>
|
|
|
|
|
>
>
>
>
>
>
>
|
>
>
>
|
|
|
<
<
<
<
<
>
|
|
<
<
<
<
<
<







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	line = parse.line,
		ty = 0

	if (line.buffer[line.index - 1] == '.' && !a_dcn)
		ty = C.SL_DOTTED
	switch (line.next_char()) {
	case "'":
		line.index++
		return ty + C.SL_ABOVE
	case ",":
		line.index++
		return ty + C.SL_BELOW
	}
	return ty + C.SL_AUTO
}

// on end of slur, create the slur
function slur_add(enote, e_is_note) {
    var	i, s, sl, snote, s_is_note

	// go back and find the last start of slur



	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 (snote.s != enote.s) {
			sl.note = enote
			if (e_is_note)

				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)

			// 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

			// set a flag if the slur ends on a grace note

			if (enote.s.grace)




				enote.s.sl2 = true


			return
		}
	}



	if (enote.grace) {

		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
		}
	}



	syntax(1, "Lack of starting slur")
}
















// (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










	if (!grace

	 && curvoice.tie_s) {		// if tie from previous note / grace note
		tie_s = curvoice.tie_s

		curvoice.tie_s = null
	}

	// handle the ties

	function do_ties(s, tie_s) {
	    var	m, note




		for (m = 0; m <= s.nhd; m++) {
			note = s.notes[m]


			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)

					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
					}
				}
			}





			s.ti2 = tie_s	// pointer to the tie start
		}
	} // do_ties()







	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
		s.invis = true;
		s.dur = 0;
		c = line.next_char()
		if (c >= '0' && c <= '9')
			s.width = line.get_int()
		else
			s.width = 10


		break
	case 'x':
		s.invis = true
	case 'z':
		s.type = C.REST;
		line.index++;
		nd = parse_dur(line);







>
>







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
		}]
		break
	case '[':			// chord
		in_chord = true;
		c = line.next_char()
		// fall thru
	default:			// accidental, chord, note




		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();
						c = line.char()
						continue
					case '!':
						if (!a_dcn)
							a_dcn = []
						if (type.length > 1) {
							a_dcn.push(type.slice(1, -1))







>
>
>
>




















<
|







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.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
						curvoice.ulen)
			if (!note)
				return //null

			// transpose
			if (curvoice.octave)
				note.pit += curvoice.octave * 7

			if (sl1) {


				note.sl1 = sl1
				if (s.sl1)
					s.sl1++

				else







					s.sl1 = 1;



				sl1 = 0













			}
			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++
					else
						note.sl2 = 1
					if (s.sl2)
						s.sl2++
					else
						s.sl2 = 1;
					c = line.next_char()
					continue
				case '-':
					note.ti1 = parse_vpos();
					s.ti1 = true;










					c = line.char()
					continue
				case '.':
					c = line.next_char()

					if (c != '-') {



						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
			}
		}


















		// 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")
		return //null
	}

	if (s.notes) {				// if note or rest
		if (!s.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;







>
|
>
>
|
|
<
>
|
>
>
>
>
>
>
>
|
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>














<
|
<
|
<
<
<
<



|
|
>
>
>
>
>
>
>
>
>
>




>
|
>
>
>
|
|
<
<
















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






|




|







<







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

			// get the explicit or implicit accidental
			// and keep the absolute pitch in base-40
			i = note.acc
			if (i) {

				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 ')':

					note.s = s

					slur_add(note, true)




					c = line.next_char()
					continue
				case '-':
					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) {
					case '-':
					case '(':
						continue
					}
					syntax(1, "Misplaced dot")
					break


				}
				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, errs.bad_grace)
		return //null
	}

	if (s.notes) {				// if note or rest
		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

			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
				s.notes[i].dur /= div;
			s.dur /= div;
			s.dur_orig /= div
			if (grace.stem)
				s.stem = grace.stem
		}


		// adjust the duration















		dur = s.dur_orig

		if (curvoice.ulen < 0) {



			dur = C.BLEN
		} else if (s.type == C.REST




			/* 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



	}

	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) {
		s.ottava = parse.ottava
		delete parse.ottava
	}
	if (parse.stemless)
		s.stemless = true
	s.iend = parse.bol + line.index
	return s
}




























// characters in the music line (ASCII only)
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,







>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|
>
>
>
|
<
>
>
>
|
<
<
<
<
>
|
>
|
|
>
>
>
|
>
>
|
>
|
>
|
|
>
>
>




<
<
<
<
<
<
<
<
<
<











|

|







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|







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

		// 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 = []

						curvoice.acc_tie[apit] =
							curvoice.acc[apit] ||
								acc_tie[apit]
					}




				}
				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 (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.length) {
		s.ottava = 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",
    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
	"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,
		tpn = -1,
		tp_fact = 1,
		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








|
|

|
<







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, tps,
		tp = [],
		tpn = -1,
		sls = [],

		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
			while (line.buffer[j + 1] == ',') {
				b -= 7;
				j++
			}
		}
		line.index = j
		return b

	}















	// expand a transposing macro
	function expand(m, b) {
	    var	c, d,
		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)
				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,
		line_sav = line,
		istart_sav = parse.istart;

		parse.line = line = new scanBuf();
		parse.istart += line_sav.index;








































		line.buffer = b ? expand(m, b) : m;
		parse_seq(true);


		parse.line = line = line_sav;
		parse.istart = istart_sav
	}

	// parse a music sequence
	function parse_seq(in_mac) {
	    var	c, idx, type, k, s, dcn, i, n, text

		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]) {
				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);
					n = 1
					break
				}
				if (n)
					continue
			}








			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

				 && curvoice.last_sym)
					curvoice.last_sym.eoln = true
				break
			case '&':			// voice overlay
				if (grace) {
					syntax(1, errs.bad_char, c)
					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




				    var	pplet = line.get_int(),
					qplet = qplet_tb[pplet],
					rplet = pplet,
					c = line.char()

					if (c == ':') {
						c = line.next_char()







>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>



|






|
<
<
<
<
<
<
<
<
<
<








|
|



|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>
>
|

|



|

















<
<
<
<
<
<
<

|















|






>
>
>
>
>
>
>














|
>





|












>
>
>
>







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, i,
		r = "",				// result
		n = m.length

		for (i = 0; i < n; i++) {
			c = m[i]
			if (c >= 'h' && c <= 'z') {
				r += n2n(b + c.charCodeAt(0) - 'n'.charCodeAt(0))










			} else {
				r += c
			}
		}
		return r
	} // expand()

	// parse a macro
	function parse_mac(k, m, b) {
	    var	te, ti, curv, s,
		line_sav = line,
		istart_sav = parse.istart;

		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
		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, 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
				}
			}








			// check if start of a macro
			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(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]
				 && 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_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
								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;
					tp.fact	= tp_fact * qplet / pplet;
					tp_fact = tp.fact

					continue
				}
				if (c == '&') {		// voice overlay start
					if (grace) {
						syntax(1, errs.bad_char, c)
						break
					}
					get_vover('(')
					break
				}
				slur_start <<= 4;
				line.index--;
				slur_start += parse_vpos()
				continue
			case ')':			// slur end
				if (curvoice.ignore)
					break
				s = curvoice.last_sym
				if (s) {
					switch (s.type) {





					case C.NOTE:
					case C.REST:

					case C.SPACE:




						break
					default:
						s = null
						break
					}
				}
				if (!s) {
					syntax(1, errs.bad_char, c)
					break
				}
				if (s.slur_end)
					s.slur_end++
				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 {







<
|
|
>
|
|
|
>
|
<
<
>




|





<

|







>
>
>
>
>


>
|
>
>
>
>










|
|
<
<







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;

					if (tpn < 0)
						tpn = tp.length	// new tuplet
					tp.push({
						p: pplet,
						q: qplet,
						r: rplet,
						ro: rplet,
						f: cfmt.tuplets


					})
					continue
				}
				if (c == '&') {		// voice overlay start
					if (grace) {
						syntax(1, errs.bad_grace)
						break
					}
					get_vover('(')
					break
				}

				line.index--;
				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.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
				}
				s.notes[0].s = s
				slur_add(s.notes[0])


				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
					}
					if (!c) {
						line.index = i;
						syntax(1, "No end of decoration")
						break
					}
				}
				if (ottava[dcn] != undefined)
					parse.ottava = ottava[dcn]

				else
					a_dcn.push(dcn)

				break
			case '"':




				parse_gchord(type)
				break
			case '-':
			    var tie_pos = 0

				if (!curvoice.last_note
				 || 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] == ':') {




					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)
				if (!s)
					continue
				if (s.type == C.NOTE) {
					if (slur_start) {
						s.slur_start = slur_start;
						slur_start = 0
					}
				}

				if (grace) {
//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
//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;
							s.tf = tp_a[0].f;
							tp_a[0].p = 0

						}
						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) {

						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 {
							if (s.tp1)	// if one note
								s.tp1 = false
							else
								s.te1 = true;
							tp = tp_a[0]

							if (tp.r == 0) {
								tpn--;
								s.te0 = true;
								tp_fact = 1;
								curvoice.time = Math.round(curvoice.time);
								s.dur = curvoice.time - s.time
							} else {
								tp_fact = tp.fact
							}




						}
					}




				}
				continue
			case '<':				/* '<' and '>' */
				if (!curvoice.last_note) {
					syntax(1, "No note before '<'")
					break
				}







|
|
>
|

>


>
>
>
>


|
<
|
|
<
<


<
<
<
<
<
<
<
<
<
<
<
<
<












>
>
>
>
















|


<
<
<
<
|
<
>
|
<
<
<

|
<
<
|
|
<
|
|
|
|
<
|
>
|
<
<
<
<
<
<
|
>
|
>
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
|
>
|
<
<
|
<
<
<
<
|
>
>
>
>
|
<
>
>
>
>







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) {
					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 '[':

				if (type.length > 1) {	// U: [I:xxx]
					self.do_pscom(type.slice(3, -1))


					break
				}













			    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, sls)
				if (!s)
					continue






				// handle the tuplets
				if (grace || !s.notes)



					continue



				if (tpn >= 0) {		// new tuplet
					s.tp = tp.slice(tpn)

					tpn = -1
					if (tps)
						s.tp[0].s = tps	// if nested
					tps = s

				} else if (!tps) {
					continue	// no tuplet active
				}







				k = tp[tp.length - 1]
				if (--k.r > 0)
					continue	// not end of tuplet yet















				while (1) {




					tp_adj(tps, k.q / k.p)
					i = k.ro	// number of notes of this tuplet
					if (k.s)


						tps = k.s  // start of upper tuplet





					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
				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 (!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()
	}







	parse_seq()

	if (tpn >= 0) {
		syntax(1, "No end of tuplet")
		for (s = curvoice.last_note; s; s = s.prev) {
			if (s.tp1)
				s.tp1 = 0
			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].range == 0
	 && curvoice.last_sym)
		curvoice.last_sym.eoln = true
//--fixme: cfmt.alignbars
}







<
















|
<

















>
>
>
>
>
>



|
<
<
|
|
<
<
<
<
<













>



<

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");

				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 "\\":
				if (!line.buffer[line.index + 1]) {

					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 (tp.length) {


		parse.tp = tp
		parse.tps = tps





	}
	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

}
Changes to core/subs.js.
︙︙
92
93
94
95
96
97
98










99
100
101
102
103
104
105
		h = font.size,
		w = 0,
		n = str.length,
		i0 = 0,
		i = 0

		el.style.font = style_font(font).slice(5);











		while (1) {
			i = str.indexOf('$', i)
			if (i < 0)
				break
			c = str[i + 1]
			if (c == '0') {







>
>
>
>
>
>
>
>
>
>







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

// 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;
					use_font(n_font)
				} else if (c[1] >= '1' && c[1] <= '9')
					n_font = get_font("u" + c[1])
				else
					return c
				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) + '">'


		})
	if (c_font != o_font) {
		output += "</tspan>";
		gene.curfont = c_font	// keep current font for next paragraph
	}
}








|
>
>
>
>
>
>
>
>
>
>
|
|
<
|


|










>
>







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){
			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

				else if (c[1] >= '1' && c[1] <= '9')
					n_font = get_font("u" + c[1])
				else
					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
			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)
				if (!glovar.tempo.del) {
					if (align != 'l') {
						var w = tempo_width(glovar.tempo)


						if (align == 'c')
							w *= .5;
						x -= w
					}
					write_tempo(glovar.tempo, x, -y)
				}
			} else if (str) {
				xy_str(x, -y, str, align)
			}

			if (c == 'T') {
				font_name = info_font.T = "subtitle";







|


|
>





|







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 */
				self.set_width(glovar.tempo)
				if (!glovar.tempo.del) {
					if (align != 'l') {
						tempo_build(glovar.tempo)
						w = glovar.tempo.tempo_wh[0]

						if (align == 'c')
							w *= .5;
						x -= w
					}
					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
// 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}\

\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}',

	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,
		g: 0
//		color: undefined
	},
	block = {}		/* started & newpage */

// glyphs in music font
var tgls = {







|
>

|
|
|
|
|
>
















|







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 = '\
\ntext, tspan{fill:currentColor}\
\n.stroke{stroke:currentColor;fill:none}\
\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: -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
  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"},

  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"},







>







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
  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
  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"},







|







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:"\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
}

// 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]

			if (!m.x && !m.y)
				return m.c
			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








>
|

|
|
>
|







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)
				return m.c
//			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
	// 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)


			output += staff_tb[stv_g.st].scale_str
		else
			output += voice_tb[stv_g.v].scale_str


	}
	if (stv_g.color) {
		if (stv_g.scale != 1)
			output += ' ';

		output += 'style="color:' + stv_g.color + '"'
	}
	output += ">\n";
	stv_g.started = true
}

/* set the color */
function set_color(color) {







|
>
>


|
>
>




>
|







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)
			output += voice_tb[stv_g.v].scale_str
		else if (stv_g.v < 0)
			output += staff_tb[stv_g.st].scale_str
		else
			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 +
			'" 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
	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;

	set_g()
}

/* -- set the voice or staff scale -- */
function set_scale(s) {

	var	new_scale = s.p_v.scale

	if (new_scale == 1) {
		set_sscale(s.st)
		return
	}



/*fixme: KO when both staff and voice are scaled */

	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.v = s.v;
	set_g()
}

// -- set the staff output buffer and scale when delayed output
function set_dscale(st, no_scale) {
	if (output) {







>





>
|





>
>
>
|
>



|
|







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,
	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;
		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 = 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

// 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) +
					') scale(' +
					 staff_tb[st].staffscale.toFixed(2) +
					')">\n' +
				staff_tb[st].sc_out +
				'</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' +







<
|
<
<
<
|
<







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 ' + staff_tb[st].scale_str + '>\n' +



				staff_tb[st].sc_out + '</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
}
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
}
Abc.prototype.sy = sy;
Abc.prototype.sh = function(h) {
	if (stv_g.st < 0)
		return h / stv_g.scale
	return h
}







|
|
|







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.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
	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) {

	out_XYAB('<path class="A" d="mX Y\n', x, y, fill ? "fill" : "stroke")


}
Abc.prototype.xypath = xypath

// output a glyph
function xygl(x, y, gl) {
// (avoid ps<->js loop)
//	if (psxygl(x, y, gl))
//		return




	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)
		return
	}
	if (!glyphs[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







>
|
>
>








>
>
>
>
|
|
|
|
|
|
|
|
|
<
|
<
|
<
|
<
<
>







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 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) {
			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)

		} else {

			error(1, null, 'no definition of $1', 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
}

// 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\
	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,







<
|







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 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
	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
		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="'
				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="'
//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);







<









|
|
|











|












|







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]

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.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 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 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
		}
	} else {				// down
		if (!straight) {
			if (!grace) {
				xygl(x, y, "fld" + nflags)
				return
			} else {		// grace
				output += '<path class="fill" 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="'
			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)
	while (1) {
		output += 'l9 -3v3l-9 3z'
		if (--ntrem <= 0)
			break
		output += 'm0 5.4'
	}
	output += '"/>\n'







|












|

















|







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 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 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 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
	}
}
function out_lped(x, y, val, defl) {
	y += 4;
	if (!defl.nost)
		xygl(x, y, "ped");
	if (!defl.noen)
		xygl(x + val, 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);







|







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 + 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
	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)
}


























































































































// 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"',
	g = ''







	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\
.music {font:24px music;fill:currentColor}\n\
@font-face {\n\
  font-family:"music";\n\
  src:' + musicfont + '}';
			} else {
				head += '\n\
.music {font:24px '+ musicfont +';fill:currentColor}'
			}
		}
		head += '\n</style>\n'
	}
	defs += fulldefs
	if (defs)
		head += '<defs>' + defs + '\n</defs>\n'







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>













|


>
>
>
>
>
>



















|
<




|
<







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="',
	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 += '\nsvg{font:24px music}\n\

@font-face {\n\
  font-family:"music";\n\
  src:' + musicfont + '}';
			} else {
				head += '\nsvg{font:24px '+ musicfont +'}'

			}
		}
		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
// 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() {







<
<
<
<





<



<
<
<

<


<







<







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/>.





// initialize
	init_tune()

// Abc functions used by the modules
Abc.prototype.add_style = function(s) { style += s };

Abc.prototype.cfmt = function() { return cfmt };
Abc.prototype.clone = clone;
Abc.prototype.deco_cnv = deco_cnv;



Abc.prototype.draw_note = draw_note;

Abc.prototype.errs = errs;
Abc.prototype.font_class = font_class;

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_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
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.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.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








<




<


|
|
<



<
<

<
<
<

<

<
<




>







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.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.param_set_font = param_set_font;
Abc.prototype.parse = parse;
Abc.prototype.psdeco = empty_function;
Abc.prototype.psxygl = empty_function;

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_scale = set_scale;



Abc.prototype.set_tsfirst = function(s) { tsfirst = s };

Abc.prototype.set_v_param = set_v_param;


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
		}
		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
}







<
<








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()



// 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
	curvoice,
	staves_found,
	vover,		// voice overlay
	tsfirst

/* apply the %%voice options of the current voice */
function voice_filter() {
	var opt, sel, i

	for (opt in parse.voice_opts) {
		if (!parse.voice_opts.hasOwnProperty(opt))
			continue
		sel = new RegExp(opt)

		if (sel.test(curvoice.id)
		 || sel.test(curvoice.nm)) {




			for (i in parse.voice_opts[opt])

			    if (parse.voice_opts[opt].hasOwnProperty(i))


				self.do_pscom(parse.voice_opts[opt][i])
		}
	}






}

/* -- link a ABC symbol into the current voice -- */
function sym_link(s) {
	if (!s.fname)
		set_ref(s)
	if (!curvoice.ignore) {







|

|
<
|

>


>
>
>
>
|
>
|
>
>
|
|
|
>
>
>
>
>
>







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

	function vfilt(opts, opt) {

	    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])
		}
	}

	// 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
	/* 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
	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


	for (v = 0; v < nv; v++)
		vtb.push(voice_tb[v].sym)





	/* initialize the voice order */










	var	fl = 1,				// start a new time sequence

		new_sy = cur_sy



	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]) {
					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 */







|




















|
|
|
|
|
|
>




>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
|
>
|
>

>







|
<
<
<

<

<
<







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([
	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, 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++) {
		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])



					continue

				ir = sy.voices[v].range


				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
				// 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])
						continue
					ir = new_sy.voices[v2].range
					if (ir < 0
					 || sy.voices[v2].range >= 0)
						continue
					vn[ir2++] = v2
				}
			}
			if (fl) {
				fl = 0;
				s.seqst = true
			}
			s.ts_prev = prev
			if (prev)
				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() {
	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







|
<
<
<
|









<
|
<
<









|







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]



					 || sy.voices[v2])
						continue
					vn[ir2++] = v2
				}
			}
			if (fl) {
				fl = 0;
				s.seqst = true
			}
			s.ts_prev = prev

			prev.ts_next = s


			prev = s

			vtb[v] = s.next
		}
		fl = wmin		/* start a new sequence if some width */
	}
}

// adjust some voice elements
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
				s.next.prev = s;
			p_voice.sym = s
		}
	}

	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
























		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) {







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
	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) {
		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() {
	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) {







|









<
<
<











|







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]) {
		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;
	    }



	}
	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)
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
		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);




			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







<
<


















>
>
>
>
|
>







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.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++

			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

// 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)

	if (a.length < 3) {
		syntax(1, "Not enough parameters in %%map")
		return
	}
	ns = a[1]



	if (ns.indexOf("octave,") == 0
	 || ns.indexOf("key,") == 0) {		// remove the octave part

		ns = ns.replace(/[,']+$/m, '').toLowerCase(); //'
		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)

	}

	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 */
	if (!a[2])
		return
	i = 2
	if (a[2].indexOf('=') < 0) {
		if (a[2][0] != '*') {
			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(',');
			i++
		}
	}

	for (; i < a.length; i++) {
		switch (a[i]) {
		case "heads=":




			map[0] = a[++i].split(',')
			break
		case "print=":
			if (cfmt.sound == "play")


				break

			tmp = new scanBuf();
			tmp.buffer = a[++i];
			map[1] = parse_acc_pit(tmp)
			break
//		case "transpose=":
//			switch (a[++i][0]) {
//			case "n":
//				map[2] = false
//				break
//			case "y":
//				map[2] = true
//				break
//			}
//			break
		case "color=":




			map[2] = a[++i]
			break
		}
	}
}

// set the transposition in the previous or starting key
function set_transp() {
	var	s, transp, vtransp

	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");





	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
		curvoice.key = clone(curvoice.okey);
		key_transp(curvoice.key);
		curvoice.ckey = clone(curvoice.key)
		if (curvoice.key.k_none)
			curvoice.key.k_sf = 0
		return
	}

	// set the transposition in the previous K:
	while (1) {
		if (s.type == C.KEY)
			break

		if (!s.prev) {
			s = curvoice.key
			break
		}
		s = s.prev
	}


	key_transp(s);


	curvoice.ckey = clone(s)
	if (curvoice.key.k_none)
		s.k_sf = 0
}



































































































































































/* -- process a pseudo-comment (%% or I:) -- */
// (possible hook)
function do_pscom(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()








	switch (cmd) {
	case "center":
		if (parse.state >= 2) {


			s = new_block("text");
			s.text = cnv_escape(param);
			s.opt = 'c'
			return
		}
		write_text(cnv_escape(param), 'c')
		return
	case "clef":
		if (parse.state >= 2) {
			if (parse.state == 2)
				goto_tune();
			s = new_clef(param)
			if (s)







|
|


|



>
>
>
|
|
>
|
<
|
|
|
>
>
>
|
|
|
|
|
|
|
|
|
>









|





|







|







>
>
>
>
|


|
>
>

>
|
|
|
<
<
|
<
|
<
|
|
<
<
|

>
>
>
>
|





|

|







>
>
>
>
|
|
|
>
>
>
|
|
>
|
|
|
|
|
|
<
<
<
<
|
<
|
|
|
<
|
|
>
|
|
|
|
<
|
>
>
|
>
>




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



|
|
<

<
<
<
<





>
>
>
>
>
>
>
>



>
>

|



|







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, ty,
	a = info_split(text)

	if (a.length < 3) {
		syntax(1, not_enough_p)
		return
	}
	ns = a[1]
	if (ns[0] == '*' || ns.indexOf("all") == 0) {
		ns = 'all'
	} else {
		if (ns.indexOf("octave,") == 0	// remove the octave part
		 || ns.indexOf("key,") == 0) {
			ty = ns[0]
			ns = ns.split(',')[1]

			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
	if (!a[2])
		return
	i = 2
	if (a[2].indexOf('=') < 0) {
		if (a[2][0] != '*') {
			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(',')	// 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(',')
			break
		case "print=":
		case "play=":
			if (!a[++i]) {
				syntax(1, not_enough_p)
				break
			}
			tmp = new scanBuf;
			tmp.buffer = a[i];
			note = parse_acc_pit(tmp)


			if (a[i - 1][1] == 'r')

				map[1] = note

			else
				map[3] = abc2svg.pab40(note.pit, note.acc)


			break
		case "color=":
			if (!a[++i]) {
				syntax(1, not_enough_p)
				break
			}
			map[2] = a[i]
			break
		}
	}
}

// set the transposition in the previous or first key signature
function set_transp() {
    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= / 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 = s = clone(curvoice.okey)

	} else {
		s = curvoice.last_sym
		while (1) {	// set the transposition in the previous K:

			if (s.type == C.KEY)
				break
			s = s.prev
			if (!s) {
				s = curvoice.key
				break
			}

		}
	}
	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)
Abc.prototype.do_pscom = function(text) {
    var	h1, val, s, cmd, param, n, k, b






	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 = param
			s.opt = 'c'
			return
		}
		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
	case "maxsysstaffsep":
		if (parse.state == 3) {
			par_sy.voices[curvoice.v].maxsep = get_unit(param)
			return
		}
		break
	case "multicol":
		generate()
		switch (param) {
		case "start":
			blk_out();
			multicol = {
				posy: posy,
				maxy: posy,
				lmarg: cfmt.leftmargin,







|







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(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
			goto_tune()
		}
		n = parseInt(param)
		if (isNaN(n) || n < -2 || n > 2) {
			syntax(1, errs.bad_val, "%%ottava")
			return
		}

		parse.ottava = n
		return
	case "repbra":
		if (parse.state >= 2) {
			if (parse.state == 2)
				goto_tune();
			curvoice.norepbra = !get_bool(param)
		}







>
|







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.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
		}
		if (h1 < 1)
			h1 = 14
		if (h2 < 1)
			h2 = h1
		if (len < 1)
			len = 90
		if (parse.state >= 2) {
			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)
			syntax(1, "Bad %%setbarnb value")


		else if (parse.state >= 2)

			glovar.new_nbar = val
		else
			cfmt.measurefirst = val
		return
	case "staff":
		if (parse.state != 3) {
			if (parse.state != 2)
				return
			goto_tune()
		}







|

















|

>
>
|
>
|
<
<







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 == 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) {
			syntax(1, "Bad %%setbarnb value")
			break
		}
		if (parse.state == 2)
			goto_tune()
		glovar.new_nbar = 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
				s.stbrk_forced = true
		}
		sym_link(s)
		return
	case "stafflines":
	case "staffscale":
	case "staffnonote":
		self.set_v_param(cmd, param)
		return
	case "staves":
	case "score":
		if (parse.state == 0)
			return





		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) {


			s = new_block(cmd);
			s.text = cnv_escape(param);
			s.opt = cfmt.textoption
			return
		}
		write_text(cnv_escape(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:


			cfmt.transp = (cfmt.transp || 0) + get_transp(param)
			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)
		return
	case "tune":
//fixme: to do
		return
	case "user":
		set_user(param)
		return







|





>
>
>
>
>
|
>










>
>

|



|










>
>
|

<
<
<















|







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":
		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)
		}
		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 = param
			s.opt = cfmt.textoption
			return
		}
		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) + val
			return



		}
		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 + ' 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
		return
	case "vskip":
		val = get_unit(param)
		if (val < 0) {
			syntax(1, "%%vskip cannot be negative")
			return
		}
		if (parse.state >= 2) {
			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 == 3) {			// tune body

			s = new_block(cmd);
			s.param = param
			return
		}
		if (cmd == "newpage") {
			blk_flush();
			block.newpage = true;
			return
		}
		break
	}
	self.set_format(cmd, param, lock)
}

// treat the %%beginxxx / %%endxxx sequences
// (posible hook)
function do_begin_end(type,
			opt,
			text) {
	var i, j, action, s

	switch (type) {
	case "js":
		js_inject(text)
		break
	case "ml":
		if (parse.state >= 2) {
			s = new_block(type);
			s.text = text
		} else {
			svg_flush();
			if (user.img_out)
				user.img_out(text)
		}







|














>
|
>











|



|
|









|







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 == 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 == 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)
}

// treat the %%beginxxx / %%endxxx sequences
// (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 == 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
			defs_add(text.slice(i + 6, j))
		}
		break
	case "text":
		action = get_textopt(opt);
		if (!action)
			action = cfmt.textoption



		if (parse.state >= 2) {


			s = new_block(type);
			s.text = cnv_escape(text);
			s.opt = action
			break
		}
		write_text(cnv_escape(text), action)
		break
	}
}

/* -- generate a piece of tune -- */
function generate() {
	var v, p_voice;









	if (vover) {
		syntax(1, "No end of voice overlay");
		get_vover(vover.bar ? '|' : ')')
	}

	if (voice_tb.length == 0)
		return
	voice_adj();
	dupl_voice();
	sort_all()			/* define the time / vertical sequences */
	if (!tsfirst)
		return
	self.set_bar_num()
	if (!tsfirst)
		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 */



	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.clef.time = 0;
		delete p_voice.have_ly;

		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) {
	var	t = (curvoice.vtransp / 3) | 0,
		sf = (t & ~1) + (t & 1) * 7 + s_key.k_sf

	switch ((curvoice.vtransp + 210) % 3) {
	case 1:
		sf = (sf + 4 + 12 * 4) % 12 - 4	/* more sharps */
		break
	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]
}

/* -- 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 = []

	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
	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
		}
	}

}

/*
 * 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,
		s = curvoice.last_sym.prev

	if (!s)
		return //undefined;

	time = s.time

	for (; 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)
					return s.notes[i].acc
			}
			return //undefined
		case C.NOTE:
			for (i = 0; i <= s.nhd; i++) {
				if (s.notes[i].pit == pitch)
					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();
		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
	} 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) {
				curvoice = voice_tb[v]
				break
			}
		}
		curvoice.time = maxtime;
		s = {
			type: C.STAVES,







>
>
>

>
>

|



|





|
|
>
>
>
>
>
>
>
>






|







|
<








|
>
>
>




|
|


>


<
<
<






|
<
<
|
<
<
<
|
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
|
|
<
<
<
<
<
<
<
<
<
<
|
|
<
<
<
|
<
|
|
<
<
|
<
<
|
<
|
<
<
<
|
<
>
|
<
<
|
<
|
<
|
<
<
<
<
<
<
<
<
>






|
|
<
<
<
<
<


|

















|
|





|

















|
|
















|
|








|







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 = text
			s.opt = action
			break
		}
		write_text(text, action)
		break
	}
}

/* -- generate a piece of tune -- */
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)
		return
	voice_adj();
	dupl_voice();
	sort_all()			/* define the time / vertical sequences */
	if (!tsfirst)
		return
	self.set_bar_num()
	pit_adj()


	// 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()

	// 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.clef.time = 0;
		delete p_voice.have_ly;
		p_voice.sls = [];
		p_voice.hy_st = 0;
		delete p_voice.bar_start



	}
	staves_found = 0			// (for compress/dup the voices)
}

// transpose a key
//fixme: transpose of the accidental list is not done
function key_transp(sk) {


	if (sk.k_a_acc || sk.k_none)		// same displayed key



		return










    var	d,






	k_b40 = sk.k_b40,
	n_b40 = (k_b40 + 200 + sk.k_transp) % 40











	d = abc2svg.b40k[n_b40] - n_b40



	if (d) {

		if (sk.k_transp > 0)
			sk.k_transp += d


		else


			sk.k_transp -= d

		n_b40 += d



	}

	sk.k_b40 = n_b40



   var sf = abc2svg.b40sf[n_b40]

	sk.k_old_sf = sk.k_sf

	sk.k_sf = sf








	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(s, pit) {
    var	i,





	time = s.time

	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 == 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 == 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) {
		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)) {
		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]) {
				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
		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) {
			p_voice2 = clone(p_voice);

			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


		}
		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 */







|

>











>
>







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]) {
			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
	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) {
			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;



		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()






	/* treat the end of overlay */
	if (curvoice.ignore)
		return
	if (type == '|'
	 || type == ')')  {
		if (!curvoice.last_note) {







|













<
<
<
<
<














>
>













>
>
>
>
>







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]) {
			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 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
		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)

				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;







|
>







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]
			 && 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
			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











}

// 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)







>
>
>
>
>
>
>
>
>
>
>







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
	// 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,
//		[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

		}
		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)
			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)
		set_kv_parm(a);

	if (!curvoice.ckey.k_bagpipe && !curvoice.ckey.k_drum)



	    transp = (cfmt.transp || 0) +
		(curvoice.transp || 0) +
		(curvoice.shift || 0)





	if (s_key.k_sf == undefined) {
		if (!s_key.k_a_acc
		 && !transp)

			return


		s_key.k_sf = curvoice.okey.k_sf
	}

	curvoice.okey = clone(s_key)
	if (transp) {
		curvoice.vtransp = transp;
		key_transp(s_key)
	}



	s_key.k_old_sf = curvoice.ckey.k_sf;	// memorize the key changes



	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)
	}
}

// get / create a new voice
function new_voice(id) {
	var	p_voice, v, p_v_sav,
		n = voice_tb.length







|





<
<
<
<






>








|




<
<
<
<
<





|


|
>
>
>



>
>
>
>



|
>
|
>
>




|

|

>
>


>
>







<
<
<
<
<
<
<
<
<
<
<
<
<

|







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, sndtran,
//		[s_key, a] = new_key(parm)	// KO with nodejs
		a = new_key(parm),
		s_key = a[0];

	a = a[1]





	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)
			memo_kv_parm('*', a)
		if (!glovar.ulen)
			glovar.ulen = C.BLEN / 8;
		parse.state = 2;		// in tune header after K:





		return
	case 2:					// K: at start of tune body
		goto_tune(true)
		break
	}
	if (a.length)
		set_kv_parm(a);

	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 == 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 != undefined) {
		curvoice.vtransp = transp;
		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













	} else {
		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
		wmeasure: glovar.meter.wmeasure,
		clef: {
			type: C.CLEF,
			clef_auto: true,
			clef_type: "a",		// auto
			time: 0
		},


		hy_st: 0
	}

	voice_tb.push(p_voice);

	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;







>
>





|
|
<
>







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

//	}

	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
		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();

	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)
			memo_kv_parm(vid, a)
		if (vid != '*' && parse.state == 1)
			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.staves[nstaff] = {
				stafflines: curvoice.stafflines || "|||||",
				staffscale: 1
			}
		}
	
		if (par_sy.voices[v].range < 0) {
//			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) {

		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
		}











	parse.state = 3;			// in tune body

	// if no voice yet, create the default voice
	if (voice_tb.length == 0) {
		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]







|
|
|










|


|



















|
>
|
>






|









|
>

















>
>
>
>
>
>
>
>
>
>




|







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, 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)
			memo_kv_parm(vid, a)
		if (vid != '*' && parse.state == 1)
			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,
				range: v
			}
			par_sy.staves[nstaff] = {
				stafflines: curvoice.stafflines || "|||||",
				staffscale: 1
			}
		}
	
		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.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) {
		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

	// 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;

			par_sy.staves[v] = {
				stafflines: p_voice.stafflines || "|||||",
				staffscale: 1
			}
		}
		par_sy.nstaff = nstaff
	}







|
|
>
|
>







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 = 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
	else
		alert("window error: " + msg)
	return false
}

var	abc_images,			// image buffer
	abc_fname = ["noname.abc", ""],	// file names

	abc,				// Abc object
	srcend,				// source symbol end index

	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(/.*\//) || ''


		})()

// -- 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(' +
				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) {
		abc_images += str
	},
	// -- optional methods
	// annotations
	anno_stop: function(type, start, stop, x, y, w, h) {
		if (["beam", "slur", "tuplet"].indexOf(type) >= 0)
			return
		srcend[start] = stop;	// source index of the end of the element

		// 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')







>

|
>









|
>
>
|
>
>











|
|
|
|
<






|




|


|







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
	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 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
	errbld: function(sev, txt, fn, idx) {
	    var	msg = sev + ' ' + clean_txt(txt)
		if (idx >= 0)
			elt_ref.diverr.innerHTML += '<b onclick="gotoabc(-1,' + idx +

				')" style="cursor: pointer; display: inline-block">' +
				msg + "</b><br/>\n"
		else
			elt_ref.diverr.innerHTML += msg + "<br/>\n"
	},
	// image output
	img_out: function(str) {
		abc_images += str
	},
	// -- optional methods
	// annotations
	anno_stop: function(type, start, stop, x, y, w, h, s) {
		if (["beam", "slur", "tuplet"].indexOf(type) >= 0)
			return
		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
//	}
	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;
		elt_ref["s" + srcidx].value = abc_fname[srcidx];
		src_change()
	}

	// Read the file as text
	reader.readAsText(files[0], "UTF-8")
}







<
<
|
>
|







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	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
}

// 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







<







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;


	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
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;







	abc = new abc2svg.Abc(user);
	abc_images = '';
	abc.tosvg('edit', '%%bgcolor white');

//	document.body.style.cursor = "wait";
	srcend = []
	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







|
>
|
>
>
>
>
>
>






|







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

	// 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";
	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
			idx = s.value.length - 1;
			c = 0
			break
		}
	}
	c = Number(c) + idx;
	s.focus();
	s.setSelectionRange(c, srcend[c] || c + 1)
}

// click in the target
function selsvg(evt) {
    var	v,
	elt = evt.target,
	cl = elt.getAttribute('class'),
	ctxMenu = document.getElementById("ctxMenu");

	play.loop = false;

	evt.stopImmediatePropagation();
	evt.preventDefault()

	// remove the context menu if active







|






|
<







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, syms[c].iend || c + 1)
}

// click in the target
function selsvg(evt) {
    var	v,
	elt = evt.target,
	cl = elt.getAttribute('class')


	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
		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])
	} else {
		s.setSelectionRange(0, 0)
	}
	s.blur();
	s.focus()
}








|







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, 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
		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 (!s) {
				if (is >= start)
					s = is
			} else if (ie <= 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");


	elt_ref["s" + srcidx].value =
		link.download =
			abc_fname[srcidx] =
				prompt(texts.fn, abc_fname[srcidx]);
	link.innerHTML = "Hidden Link";
	link.href = uriContent;


	// open in a new tab
	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";







|
|



|



















<
<




>
|
<
|
|
|
|
>


|







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 (syms) {
		syms.forEach(function(sym, is) {
			if (!s) {
				if (is >= start)
					s = is
			} 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,



	// create a link for our script to 'click'
		link = document.createElement("a");

	if (abc_fname[srcidx] == "noname.abc")
		elt_ref["s" + srcidx].value =

			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';

	// 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
	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() {
	if (play.loop) {
		play.abcplay.play(play.si, play.ei, play.a_pe)
		return
	}
	play.playing = false;


	// redisplay the selection
	selx[0] = selx[1] = 0;
	setsel(0, selx_sav[0]);
	setsel(1, selx_sav[1])
}

// start playing
//	-1: All
//	0: Tune
//	1: Selection
//	2: Loop
//	3: Continue
function play_tune(what) {





	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) {
			s = pa[i][0]
			if (s < si)
				continue
			if (s == si) {
				ci = i
				break
			}
			if (s < sih) {
				ci = i;



				sih = s





			}
		}

		// 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
			}
		}


		return ci + 1
	} // get_se()

	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
		if (si >= pa[pa.length - 1][0])
			return pa.length

		si = go_note(si)

		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
			}


		}

		// go to after the last voice at this time
		if (ci > 0) {
			tim = pa[ci++][1]
			for ( ; ci < pa.length; ci++) {
				if (pa[ci][1] != tim)
					break

			}


		}


		return ci
	} // get_ee()

	// start playing
	function play_start(si, ei) {


		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 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
		user.img_out = null	// get the schema and stop SVG generation
		user.get_abcmodel = play.abcplay.add // inject the model in the play engine



		abc = new abc2svg.Abc(user);


		play.abcplay.clear();
		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 all
	if (what < 0) {
		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)
	} else if (what != 0 && selx[0]) {	// if selection without end
		si = get_se(selx[0]);
		i = s.indexOf('\nX:', selx[0]);
		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 = i < 0 ? 0 : get_se(i);
		ei = get_ee(selx[1], si)
	} else {				// no selection => tune
		si = play.click.svg.getElementsByClassName('abcr');
		elt = play.click.svg		// (dummy)
		for (i = 0; ; i++) {
			if (!si[i]) {
				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!
		}

		si = get_se(si);
		ei = ei < 0 ? play.a_pe.length : get_ee(ei, si)
	}

	if (what != 3) {		// if not continue
		play.si = si;
		play.ei = ei;
		play.loop = what == 2

	}

	play_start(si, ei)
}

// set the version and initialize the playing engine
function edit_init() {







|

|



>








|





>
>
>
>
>








|
|
>
|
>
|
>
|
|
>
>
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|
|
>
>
|
|
>
>
>
>
>
>
>
>
|
|
<
|
<
<
<
|
|
|
|
>
>
>
|
>
>
>
>
>
|
<
<
<
<
<
<
<
|

<
>
>
|
<
|
<
<
<
<
<
|
<
|
<
<
|
|
|
<
<
<
<
<
<
<
|
<
<
>
|
|
>
>
|
|
<
<
|
<
<
|
>
|
>
>
|
>
>
|




>
>






|



<
<
<
<
<
<
<
<
<
<
<
<
<
<
<



|
<
<

>
>
|
|
>
|
<
<
<
<
<
<
<
<

<

|
|
<
<
<
<
|
<
<
<
<









>






|


|
<

|
<
|

<

|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|

|
|
|






>







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(repv) {
	if (play.loop) {
		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 (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 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) {

					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_prev) {
				if (!loop)
					return gnrn(sym, 1)

				return sym





			}

			sym = sym.ts_prev


		}
		// not reached
	}










	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)


			sym = sym.ts_prev


		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.repv)
	}

	// play tune()















	ctxMenu.style.display = "none";	// remove the play menu

	play.playing = true;
	if (tunes.length) {		// if new display



		// generate the play data of all tunes
		while (1) {
			elt = tunes.shift()
			if (!elt)
				break
			play.abcplay.add(elt[0], elt[1])








		}


		play.si = play.ei = null
		play.stop = 0




		play.loop = false




	}

	// 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])
	} else if (what != 0 && selx[0]) {	// if selection without end
		si = get_se(selx[0]);
		ei = null

	} else if (what != 0 && selx[1]) {	// if selection without start
		si = gsot(selx[1])

		ei = get_ee(selx[1])
	} else {				// no selection => tune

		elt = play.click.svg		// (dummy)
		si = elt.getElementsByClassName('abcr') // symbols in the SVG
		if (!si.length) {

























			play.playing = false
			return			// not in a tune!
		}
		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
		if (v) {
			elt_ref.source.style.fontSize =
				elt_ref.src1.style.fontSize =
					v + "px";
			document.getElementById("fontsize").value =
					Number(v)
		}



		v = storage(true, "lang");













		loadlang(v || 'en', true)
	}

	document.getElementById("abc2svg").innerHTML =
		'abc2svg-' + abc2svg.version + ' (' + abc2svg.vdate + ')'

	// keep references on the page elements
	var a = ["diverr", "source", "src1", "s0", "s1", "target"]







>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
|







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, 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
		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() {
			play.abcplay = AbcPlay({
					onend: endplay,
					onnote:notehlight,
					});
			document.getElementById("playdiv1").style.display =
				document.getElementById("playdiv3").style.display =
				document.getElementById("playdiv4").style.display =







|







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("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

			// 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.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







|







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
			}

			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
<!-- 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/*"
				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>







|







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="*"
				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

<!-- right - needed for the scrollbars -->
<div id="dright">

<!-- context menu -->
<div id="ctxMenu" title="Play">
	<ul class="droplinks">

	<li id="cmpa" onclick="play_tune(-1)">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>








>
|







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)" 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
</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>
</div>

<!-- javascript must be enabled -->

<div id="okjs" class="popup" style="width:50%;background:#ff8080;visibility:visible">
Please enable Javascript.
</div>
<script>
document.getElementById('okjs').style.visibility = 'hidden'
</script>

</body>
</html>







|



>



|
<
<



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-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>
</noscript>



</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


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",
    "',' 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 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 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",
    "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 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",
    "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 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",

    "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",







|
















<



<









>
>










>





|


|













|
|



|



















>


<







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 %%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",

    "%%staffwidth too big": "%%staffwidth zu groß",
    "%%staffwidth too small": "%%staffwidth zu klein",
    "%%tune not treated yet": "%%tune noch nicht behandelt",

    "%%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 %%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 '$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 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 %%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 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
//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",
	"',' 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":












|
|







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 %%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
	"%%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)",
	"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 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 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",
	"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 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 "',
	"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 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":







<



<




|





>
>










>






|


<
|

















<
|
|




|







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",

	"%%staffwidth too big": "%%staffwidth trop grand",
	"%%staffwidth too small": "%%staffwidth trop petit",
	"%%tune not treated yet": "%%tune n'est pas encore traité",

	"%%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 %%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 %%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 '$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 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 %%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
	"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",

	"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",







>


<
<







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 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
//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",
	"',' 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":












|
|







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 %%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
	"%%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 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 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",
	"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 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 "',
	"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 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)",

	"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",







<



<










>
>










>






|


<
|

















<
|
|




|




















>


<
<







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",

	"%%staffwidth too big": "%%staffwidth troppo grande",
	"%%staffwidth too small": "%%staffwidth troppo piccolo",
	"%%tune not treated yet": "%%tune non ancora gestito",

	"%%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 %%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 '$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 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 %%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 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
//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",
	"',' 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":











|
|







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 %%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
	"%%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 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 tie": "Ligadura de prolongamento incorreta",
	"Bad transpose value": "Valor incorreto de transposição",
	"Bad user character '$1'": "Caractere 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",
	"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 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 "',
	"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 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":







<



<








|
|
>
>










>


|



|


<
|
















<
|
|




|







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",

	"%%staffwidth too big": "%%staffwidth muito grande",
	"%%staffwidth too small": "%%staffwidth muito pequeno",
	"%%tune not treated yet": "%%tune ainda não é tratado",

	"%%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:": "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'": "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 %%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 '$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 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 %%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
				"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",

	"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 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",







>


<
<
|







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 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
// 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\
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\
AeUi5SXlLeUx5TnlZ+Vp5W3lguXQ5eLmEOYS5hTmGOYk5jDmUOZV6RLpFekY6SDpJeld6gLqpP//\
AAAAAAAg4ADgIuAw4DjgQ+BF4FDgXOBi4GngeuCA4JTgoOCp4LPhAeG54bvh5+JA4mDigOSg5KLk\


pOSo5KzkwOTO5OHk7uUA5SDlJOUp5S/lOeVm5WnlbOWC5dDl4uYQ5hLmFOYY5iTmMOZQ5lXpEOkU\
6RjpIOkl6V3qAuqk//8AA//kIAUf5B/ZH9IfyR/IH8Efth+xH6sfmx+ZH5IfiB+EH3sfLh53HnYe\
Sx3zHd0dwhumG6UbpBuhG54bixt+G2wbaRtYGzobORs2GzUbLhsCGwEa/xrrGp4ajRpgGl8aXhpb\
GlAaRRomGiIXaBdnF2UXXhdaFyMWfxXeAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEGAAADAAAAAAAAAAECAAAA\

AgAAAAAAAAAAAAAAAAAAAAEAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAIgKIAAAAKgAqACoANgA+AG4AfACKAJgApACwAL4A3AFQAawCHAJe\
AvADggPkA/gElAUmBX4FvAXeBfIGRAaUBrQG+gc6B3QHwAgACEYIoAi0CNoJAAkyCU4JcAmcCbIJ\
zAoCChAKHAooCjoKVApuCqIK1gsqC3QL1AwyDKgNHA1CDVwNjA22Dg4ONA6ODq4O7A8UDygPNA9C\
D1IPeg+gD6wPuA/ED9AP8hAYEE4QnBD8EXARhhGsEdoSPhK2ExQTXBO4FRIWHhbYF5gYaBkEGeIa\
/hwAHEIchhzQHOodDh0iHTYdZh12HYgdpB26Hd4eDh7wH6wf1B/yICQgYiCUINYg6CD2IRIhLCFU\
AAIAIgAAATICqgADAAcALrEBAC88sgcEAO0ysQYF3DyyAwIA7TIAsQMALzyyBQQA7TKyBwYB/Dyy\
AQIA7TIzESERJzMRIyIBEO7MzAKq/VYiAmYAAAEAAAAAAZEBkAADAAAxESERAZEBkP5wAAEAAAAA\

AAAAAAAAAAAxAAABAAAAAABXBAMAIAAAETU2NTQnJjU0NwYVFBcWFRQHFhUUBwYVFBcmNTQ3NjU0\
NSMSVz8VJU1NJRU/VxIjAgMCGEM2YDk0ZjoySyI5YU1hGBhmTGA5JUoyOmY0OWA2SAAB/7D/7AGa\
ABQAAwAAJyEVIVAB6v4WFCgAAAAAAf+w/+wCYgAUAAMAACchFSFQArL9ThQoAAAAAAH/sP/sASIA\
FAADAAAnIRUhUAFy/o4UKAAAAAABAAAAAAAjA+gAAwAAETMRIyMjA+j8GAABAAAB9AAjA+gAAwAA\
ETMRIyMjA+j+DAABAAAC+AAjBFYAAwAAETcRIyMjBEwK/qIAAAAAAgAAAUAAZAKeAAcADwAAEiIm\





NDYyFhQCIiY0NjIWFEcqHR0qHR0qHR0qHQI6HSodHSr+6R0qHR0qAAAABQAAAAAEJAGuAC8ANwA/\
AEcAUwAAITUzHgEzMjY1NCcuBDU0NjMyFhc3MxcjLgEjIgYVFB4DFx4BFRQGIyInByAiJjQ2MhYU\
BCImNDYyFhQBETMyNjQmIwM1MxEjNTMyFhUUIwJOHhVPMik7lBkaKhYRWT0kJxkeHgceD0owHzkQ\
IhkyCE5NW09FLiMBmyodHSod/eoqHR0qHf6JKDxGRjzcRkbccYn6oDxLICEtKAcIFBQjFUNNCw4Z\
mzpIKBsPFw8JCwIVNzM6TiAgHSodHSodHSodHSoBaf6YYaZh/noeAWgeZ2vSAAUAAAAABCQBrgAa\
ACIAKgAyAD4AACEiJjU0NjMyFhc3MxcHJiMiBhQWMzI2NxcOATIiJjQ2MhYUBCImNDYyFhQBETMy\
NjQmIwM1MxEjNTMyFhUUIwMCZ3WCWiUpGx4eCCEkXjg2Njg2TREjFFmxKh0dKh396iodHSod/oko\
PEZGPNxGRtxxifprZ2V3DBIepgSMbZhtST4KSlEdKh0dKh0dKh0dKgFp/phhpmH+eh4BaB5na9IA\
AAAD/9wAAgHeArMABwAPAE0AAAAiJjQ2MhYUBCImNDYyFhQXNDYzMhYVFAcWMzI2NTQvAQMnEy4B\
NTQ+ATc2MzIWFRQGIyImNTQ3JiMiBhUUHwETFwMeARUUDgEHBiMiJgGeIBgYIBj+fiAYGCAYWxsU\
Ex4sFykmNiZ7zyvRWkgcExQmMzA4GxQTHiwXKSY2JnrUK9VaSBwTFCYzMDgBUhggGBggVBggGBgg\

0xIcGhEdDhctJi0mZf7eIAElR3E4Fi0RERM4IRIcGhEdDhctJi0mZQEmH/7XR3I4Fi0RERM4AAUA\
ev80A14CVAAXABsAHwAjACcAAAEzFR4BFzMVIw4BBxUjNS4BJyM1Mz4BNxEjFhc3FTY3JzMmJwc1\
BgcB2ChVeAeKigd4VShVeAeKigd4VXAFayhrBXBwBWsoawUCVIkIi2AoYIsIiYkIi2AoYokI/uW6\
DsjIDrooug7IyQ67AAAAAAQAAP1vAqcEiAALAEgAUgBnAAABBhUUFz4BNTQnDgETFxQVFAYjIiY1\
NDYzMhYUBgcWMzI2NTQ1JwYjIi4CNTQ3PgY/ASY1NDY3FhUUBgcXNjMyFhUUJzQmIyIGIxM+AScO\

ARUUFhcuATU0NycOAQceATMyNwFsBwVIdTY5QksXT01SX0AyL0E/Ly0YLkEXFBVJhmc9Og0kIC4d\
MRIXFg1sSWJceRQQD3GITmJQAgcCIVtB4jZGJx09P6YRj2wBAqB6EBADUzYuF2YxlUhyDQde+2zs\
AQJMXFNBLUg7WDcBGT1FAgHpAjNbh0+FZhcwJS0aJw4REJZmjKEHOuWIqV/PAp1yzro9ZwH+nRJd\
8gtGMSBCEg5FR6kuvXKXZ42jAgAAAAACAAD9/ALSAgAAZABoAAABMjY1NCcmIyIHDgIHJicmJxEj\
ETMRNjc2Nx4DFxYzMjY1NCcmIyIHFhcUFhUUBisBJjU0NzY3NjMyFxYXFRQGBwYjIicHFzYzMhYX\
Fh0BBgcGIyImNTQ3MzIWFRQGFQYHFgEzESMB3j5KDRpJRjwCBgoEIhoeLhwcLh4aIgYUDBkPJyUx\
PRIkUi8xMg0CMyEFRAUaVScjXlU3CFpIHy00PyIiPzRCZh4oCDdWXUxyRAUhMwIPMDX+TXt7/iB4\
Si0tbEkFDhsKYicrH/4EBAD+ER8rJ2ILKhcdCRt7QjE2YhoQKAMNBB4rGTIUC0QZDU80UxJObxwN\
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\
T2IzKi0qKjU6HR03ZEUzGaskVENdKQsJOGRFNBgAAgAA//8BPAK8ABYAIwAAFREzMj4DNTQnFhUU\
BxYVFA4DBzUyPgM1NCcOAx4WQEk+KAUeExI0TlBCCRZAST4pARVWTUQBAVcZM0VkNx0dOjUqKi0q\
M2JPSlAkqxg0RWQ4CQopXUNTAAADAAD9KgE8AJEAGwArADsAABkBMx4GFRQHFhUUBxYVFAc2NTQu\
AiM1HgMXNDY1NCcmJyYjIiceAxc0NjU0JyYnJiMiHgYnMzs4LhwSEhITHgU9V1UcCURNVhUBckZA\
BwUBAQlETVYVAXJGQAcFAf6QAgEbOTM3Oj1IJSwrJy0sKykrNjkZIUR3SCmpJFRBXSkEDQOIXzsI\
AaskU0JdKQQNA4hfOwgBAAADAAD/VgE8Ar0AGQAmADMAABEzMj4DNTQnFhUUBxYUBxYVFA4DByM3\

Mj4DNTQnDgMnMj4DNTQnDgMeFkBJPigFHhMSEhI0TlBCCR4eFkBJPikBFVZNRAkWQEk+KQEVVk1E\
AVcZM0VkNx0dOjUqKixVKi0qM2JPSlAkqhk0RWQ4CgopXUNUhhk0RWQ4CgopXUNUAAAABAAA/UIB\
PAFUABwAKQA2AEMAABkBMx4EFRQHFhQHFhQHFhUUBzY1NC4DIzUeAxc2NTQuAyceAxc2NTQuAyce\

Axc2NTQuAx4JQlBONBISEhISEx4FKD5JQBYJRE1WFQEpPklAFglETVYVASk+SUAWCURNVhUBKT5J\
QP6oAqwkUEpPYjMqLSpVLCpVLCoqNTodHTdkRTMZqyRUQ10pCwk4ZEU0GKskVENdKQsJOGRFNBir\
JFRDXSkLCThkRTQYAAAABAAA/o4BPAKgABwAKQA2AEMAABkBMzI+AzU0JxYVFAcWFAcWFAcWFRQO\
Awc1Mj4DNTQnDgMnMj4DNTQnDgMnMj4DNTQnDgMeFkBJPigFHhMSEhISEjROUEIJFkBJPikBFVZN\
RAkWQEk+KQEVVk1ECRZAST4pARVWTUT+jgKsGTNFZDcdHTo1KiosVSosVSotKjNiT0pQJKoZNEVk\
OAoKKV1DVIYZNEVkOAoKKV1DVIYZNEVkOAoKKV1DVAAFAAD9VQE8AhIAIAAtADoARwBUAAAZATMe\

BRUUBxYUBxYUBxYUBxYVFAc2NTQuAyM1HgMXNjU0LgMnHgMXNjU0LgMnHgMXNjU0LgMnHgMXNjU0\
LgMeBzI/RjolEhISEhISEhMeBSg+SUAWCURNVhUBKT5JQBYJRE1WFQEpPklAFglETVYVASk+SUAW\
CURNVhUBKT5JQP67A1cfQzpGQVQrKi0qVSwqVSwqVSwqKjU6HR03ZEUzGaskVENdKQsJOGRFNBir\
JFRDXSkLCThkRTQYqyRUQ10pCwk4ZEU0GKskVENdKQoKOGRFNBgAAAUAAP28ATwCeQAfACwAOQBG\
AFMAABkBMzI+AzU0JxYVFAcWFAcWFAcWFAcWFRQOAwc1Mj4DNTQnDgMnMj4DNTQnDgMnMj4DNTQn\
DgMnMj4DNTQnDgMeFkBJPigFHhMSEhISEhISNE5QQgkWQEk+KQEVVk1ECRZAST4pARVWTUQJFkBJ\


PikBFVZNRAkWQEk+KQEVVk1E/bwDVxkzRWQ3HR06NSoqLFUqLFUqLFUqLSozYk9KUCSqGTRFZDgK\
CildQ1SGGTRFZDgKCildQ1SGGTRFZDgKCildQ1SGGTRFZDgKCildQ1QAAAACAAD/ZADhAbAACgAW\
AAA3IgYdATY3NjU0JjcyFhUUBwYjETMRNmcUKyImKx0HIzlLUkQoI3omE7kPODsvGyYmMiNJTFIC\
TP68NAAAAgAA/oYAxQF6AAMADAAAFzc1BxEVNxEjNQcRNxyQkKkZrAFlLZYtAUnoNP3A4jMCQwEA\
AAIAAP6YAP8BaAADAB8AADcVNzUDIzUHNTc1BzU3NTMVNzUzFTcVBxU3FQcVIzUHU1paHjU1NTUe\
Wh01NTU1HVpGpxun/jejD1wPpw9aD6ifHKujD1wPpw9aD6ifHAAAAAEAFP+EAQsAegAeAAAXNSYn\
BzAVIzUzNycwIzUzFRYXNzA1MxUjBgcXMDMVwygMM0g5MzM5SCESNEg5IRM0OXw7Jg0zO0oyMkg5\
IhEzOUciETRIAAQAAP9qAWwBsAAOABwAKwA6AAA3DgEdATI3Njc2NTQnJiM3MhYVFAcGBwYjETMR\
NhcOAR0BMjc2NzY1NCcmIzcyFhUUBwYHDgEjETMRNk4RHg4eHwwEChARGR0rCRgrNS8fGdERHRAe\
HQsGCxAPFh8qCxkoFjcWHht9AR4QxikrNA0ZHhQVJjkhEiA5NEACRv7BMiYBHRHGKS8wExMcFhUm\
NiQWHD4vGyUCRv7BMgAAAgAy/2QBDQGwAAoAFgAANyIGFRQXFhc1NCYnMhcRMxEiJyY1NDarFyAu\


LhoqMzojIzpSTzl6JhswRUIDyxIeJjQBRP20UlBFIzIAAAT/9f9qAWwBsAAOAB0ALAA7AAA3IyIH\


BhUUFxYXFjM1NCYnMhcRMxEiJicmJyY1NDYXDgEdATI3Njc2NTQnJiM3MhYVFAcGBw4BIxEzETZX\
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\
JxscKRdILgEWA/70LVUoISs3JxscKRdBCzwMKCggIB8ZHRsiywkEKSgfIB4ZHRtNAgL8bgwBJAwo\
KCAgHxkdGyIAAAAAAQAA/QwB2gHAAEUAABMGIyImNTQ2MzIWFRQHMj8BBiMiJjU0NjMyFhUUBzI/\
AQYjIiY1NDYzMhYVFAcyPwEiDgEjIiY1NDYyFhUUBzI3NjIXASerKCErNycbHCkXQQs6KCErNycb\
HCkXQQs6KB8rNycbHCkXPws6ASAcEys3JjgpF0guAhUD/qkt/jwMKCggIB8ZHRsiygwoKCAgHxkd\
GyLKDCgoICAfGR0bIssJBCkoHyAeGR0bTQIC+24MAAAAAQAA/QwCGQKuAFYAACUGIyImNTQ2MzIW\
FRQHMj8BBiMiJjU0NjMyFhUUBzI/ASIOASMiJjU0NjMyFhUUBzI3NjIXAScTBiMiJjU0NjMyFhUU\


BzI/AQYjIiY1NDYzMhYVFAcyNwE/KCErNycbHCkXQQs2KB8rNycbHCkXPws0ASAcEys3JxscKRdI\

LgEWA/5qLVUoISs3JxscKRdBCzooISs3JxscKRdBCzQMKCggIB8ZHRsixAwoKCAgHxkdGyLHCQQp\



KB8gHhkdG00CAvqADAEkDCgoICAfGR0bIsgMKCggIB8ZHRsiAAEAAP8aA1IA5gALAAA1MxUhNTMR\
IzUhFSMZAyAZGfzgGeZ9ff40fX0AAAAAA//w/wYCJgD6AAcADwATAAA2IiY0NjIWFAAiJjQ2MhYU\

BQEzAVAyIyMyIwGIMiMjMiP90gG4fv5HSyMyIyMy/s8jMiMjMloB9P4MAAT/4f8GAwcA+gAHAA8A\
EwAXAAA2IiY0NjIWFAAiJjQ2MhYUBQEzATMBMwFBMiMjMiMCdzIjIzIj/OMBuHv+R3kBuHv+R0sj\
MiMjMv7PIzIjIzJaAfT+DAH0/gwAAv+0/4gBfAEYABkASAAANxYzMjY3PgU1NCYnJiMiBgcGFRQe\

ARciJwczMhQrASI0OwETPgI1NC4CIyIOAwcGJjc2NzYzMhYXPgEzMhYVFAbFAwQSMg4CAwMDAQEH\
CAMDEzQLDwQHCBYZLTQLC+ELC0toAQMCAQIFAwgMDwsZCgUbBTEPGCQjJAcdJiMeLWsoATUkBQsM\

DAwMBhAXAwEzHCYlDBQNKiB6Hh4BHQIIDgYDBQUCBxQSLBAIDwlYEBkTGh4PNDBHbQAB/9v/9gG+\
ARgAVwAANwYHBisBIj8BNiYjIgYHBiY3PgMzMhc2MzIXPgEzMh4DFRQPAQYVFDMyNz4FNzYWBw4C\
IyImNTQ2PwI0IyIPAQYHJwYmPwE2NTQmIyIHUAcIBAQ1DQ1CBAYIDRYkBRUEFBAiHxI3CyQkLQkL\


KRMKEg8LBwUvBAgCAwULBwwDDQEGFQYSFy0fFRkCAzQBFRsIQQgPJg0IBEMBDAkbCBURAwEapw0P\
GjkIDAklGjASKCgoEBgGCw8TCg0NfQsJDgIDCQYPBRIBCQ0LHx8aFRQGDQeIBg4UqxYBAQEPCKsD\




AwYIFAAB/37/YAFeAbgASAAAByImNTQ2MzIWFRQHBhUUMzI+BzcjIiY1ND4DOwE+ATMyFhUUBiMi\
JjU0NzY0IyIOBwczMhYUBisBDgEyIDAXExIXEgoZCxAPCw0KDg0UCjUJCgEDBAYDQRRpNCAwFxMS\
FxIKGQcMCgcIBQYDBgE2CQoKCT8hdqAmIBoiFA8OCwcNDgYREyUkPDdVJwwJAwUFBAJLXyYgGiIU\
Dw4LBhwFCwoUDRoNHgYMEAzFwQAAAAEAAAAAANoBGAA0AAAzIiY1NDYzMhYVFAcWMzI2NTQuAicm\

NTQ2MzIWFRQGIyIuATU0NyYjIgYVFB4CFx4BFRRQHjISDA4XDAYYFiEJCxgGPDctIjYWEAgQCwUQ\
Dg8ZERIeBRsXKxsQFg0LGAwSFhILDwcNBCYqIy0kGBAYCQ8KCAgUEQ0JEwwRAxIfFVoAAAH/5P//\
AOcBEAA/AAAnBi4BPwEuASMqASMiDgEHBicmNz4BNxYzMj4BMzIXFhQPAQ4BFRQeARcWNicuATU0\
NjMyFRQHBgciJiMuASMiCgYLAQWwCBcRBQwHBREUBQ8EAwgMCwEwIhkgEQcFCAsHngEBECwXCAwD\
AyQUDCYkERIDBQIXNAkSAwQIDQbBBAMbIQQNCwkSHjACAwMEAQERB6cDAwIFAQkLBA8JCAgUCxM3\
KhUJAQEDDQAF/7T/iAVLARgAGwA1ANkA8AELAAAlFjMyPgE3PgY1NCYnJiMiBgcOAhUUBRYzMjY3\
PgU1NCYnJiMiBgcGFRQeARciJwczMhQrASI0OwETPgI1NC4CIyIOAwcGJjc2NzYzMhYXPgEzMhc+\
ATMyFhc+ATMyFz4BMzIWFz4BMzIXPgEzMhYXPgEzMhYVFAYjIicHMzIUKwEiNDsBEz4CNTQmIyIG\









BxUUBiMiJwczMhQrASI0OwETPgI1NCYjIgYHFRQGIyInBzMyFCsBIjQ7ARM0PgM0NTQmIyIGBxUU\

BiUWMzI2Nz4ENTQmJyYjIgYHBhUUBRYzMjY3PgY1NCYnJiMiDgEHBhUUFgIKAwQMHx4JAgMCAgIB\
AQcIAwMTNAsFBwP+ywMEEjIOAgMDAwEBBwgDAxM0Cw8EBwgWGS00CwvhCwtLaAEDAgECBQMIDA8L\

GQoFGwUxDxgkIyQHHSYjLxMSIhsjJAcdJiMvExIiGyMkBx0mIy8TEiIbIyQHHSYjHi1rSRYZLTQL\
C+ELC0toAQMCBQYOFBRrSRYZLTQLC+ELC0toAQMCBQYOFBRrSRYZLTQLC+ELC0toAQIBAgUGDhQU\
awODAwQSMg4CBAMDAQcIAwMTNAsP/ssDBBIyDgIDAgICAQEHCAMDDCEeBw8JKAEYKhcECgkLCgoL\
BRAXAwEzHA0bGAsoBwE1JAULDAwMDAYQFwMBMxwmJQwUDSogeh4eAR0CCA4GAwUFAgcUEiwQCA8J\
WBAZExoeDzIcFhMaHg8yHBYTGh4PMhwWExoeDzQwR20geh4eAR0CCA4GBwgYIwFHbSB6Hh4BHQII\
DgYHCBgjAUdtIHoeHgEdAQMFBQYHAwcIGCMBR20oATUkBg0PDg8HEBcDATMcJiUoBwE1JAQKCQsK\
CgsFEBcDARglEiYlEhoAAAT/tP+IBAYBGAB9AJgAtADOAAAhIicHMzIUKwEiNDsBEz4CNTQmIyIG\
BxUUBiMiJwczMhQrASI0OwETND4DNDU0JiMiBgcVFAYjIicHMzIUKwEiNDsBEz4CNTQuAiMiDgMH\

BiY3Njc2MzIWFz4BMzIXPgEzMhYXPgEzMhc+ATMyFhc+ATMyFhUUBicWMzI2Nz4GNTQmJyYjIg4B\
BwYVFBYFFjMyPgE3PgY1NCYnJiMiBgcOAhUUBRYzMjY3PgU1NCYnJiMiBgcGFRQeAQNSFhktNAsL\
4QsLS2gBAwIFBg4UFGtJFhktNAsL4QsLS2gBAgECBQYOFBRrSRYZLTQLC+ELC0toAQMCAQIFAwgM\
DwsZCgUbBTEPGCQjJAcdJiMvExIiGyMkBx0mIy8TEiIbIyQHHSYjHi1rTAMEEjIOAgMCAgIBAQcI\
AwMMIR4HDwn+wgMEDB8eCQIDAgICAQEHCAMDEzQLBQcD/ssDBBIyDgIDAwMBAQcIAwMTNAsPBAcg\
eh4eAR0CCA4GBwgYIwFHbSB6Hh4BHQEDBQUGBwMHCBgjAUdtIHoeHgEdAggOBgMFBQIHFBIsEAgP\
CVgQGRMaHg8yHBYTGh4PMhwWExoeDzQwR20oATUkBAoJCwoKCwUQFwMBGCUSJiUSGgMBGCoXBAoJ\
CwoKCwUQFwMBMxwNGxgLKAcBNSQFCwwMDAwGEBcDATMcJiUMFA0AA/+0/4gCwQEYAFcAcQCNAAAz\
IicHMzIUKwEiNDsBEz4CNTQuAiMiDgMHBiY3Njc2MzIWFz4BMzIXPgEzMhYXPgEzMhYVFAYjIicH\
MzIUKwEiNDsBEzQ+AzQ1NCYjIgYHFRQGJxYzMjY3PgU1NCYnJiMiBgcGFRQeAQUWMzI+ATc+BjU0\
JicmIyIGBw4CFRTIFhktNAsL4QsLS2gBAwIBAgUDCAwPCxkKBRsFMQ8YJCMkBx0mIy8TEiIbIyQH\



HSYjHi1rSRYZLTQLC+ELC0toAQIBAgUGDhQUa0wDBBIyDgIDAwMBAQcIAwMTNAsPBAcBSgMEDB8e\
CQIDAgICAQEHCAMDEzQLBQcDIHoeHgEdAggOBgMFBQIHFBIsEAgPCVgQGRMaHg8yHBYTGh4PNDBH\

bSB6Hh4BHQEDBQUGBwMHCBgjAUdtKAE1JAULDAwMDAYQFwMBMxwmJQwUDQIBGCoXBAoJCwoKCwUQ\

FwMBMxwNGxgLKAAAAv/b/4gDKwEYAHoAjwAAJTY3NjMyFhc+ATMyFhUUBiMiJwczMhQrASI0OwET\
PgM1NCYjIg4DBw4BIyImNTQ2PwI0IyIPAQYHJwYmPwE2NTQmIyIPAQYHBisBIj8BNiYjIgYHBiY3\
PgMzMhc2MzIXPgEzMh4DFRQPAQYVFDMyNz4BFxYzMjY3PgE1NCYnJiMiBgcGFRQWAa0xFBgiIyQH\
HSYjHi1rSRYZLTQLC+ELC0toAQEDAQUGBw8SDhgIGjwtFRkCAzQBFRsIQQgPJg0IBEMBDAkbCEQH\
CAQENQ0NQgQGCA0WJAUVBBQQIh8SNwskJC0JCykTChIPCwcFLwQIAgMPJdEDBBIyDgYHBwgDAxM0\
Cw8Jd2wYHRMaHg80MEdtIHoeHgEdAgUJCQUHCA0fGzUQNjgVFAYNB4gGDhSrFgEBAQ8IqwMDBggU\
rBEDARqnDQ8aOQgMCSUaMBIoKCgQGAYLDxMKDQ19CwkOAgg1OQE1JA8mERAXAwEzHCYlEhoAAv/b\


/2ADGQG4AFcAnwAANwYHBisBIj8BNiYjIgYHBiY3PgMzMhc2MzIXPgEzMh4DFRQPAQYVFDMyNz4F\
NzYWBw4CIyImNTQ2PwI0IyIPAQYHJwYmPwE2NTQmIyIHEyImNTQ2MzIWFRQHBhUUMzI+BzcjIiY1\
NDY7AT4BMzIWFRQGIyImNTQ3NjQjIg4HBzMyFhUUDgErAQ4BUAcIBAQ1DQ1CBAYIDRYkBRUEFBAi\
HxI3CyQkLQkLKRMKEg8LBwUvBAgCAwULBwwDDQEGFQYSFy0fFRkCAzQBFRsIQQgPJg0IBEMBDAkb\
CPUgMBcTEhcSChkLEA8LDQoODRQKNQkKCQhBFGk0IDAXExIXEgoZBwwKBwgFBgMGATYJCwUJBj8h\
dhURAwEapw0PGjkIDAklGjASKCgoEBgGCw8TCg0NfQsJDgIDCQYPBRIBCQ0LHx8aFRQGDQeIBg4U\
qxYBAQEPCKsDAwYIFP6fJiAaIhQPDgsHDQ4GERMlJDw3VScMCQgLS18mIBoiFA8OCwYcBQsKFA0a\
DR4GDAgFCQbFwQAAAAAB/37/YAJpAbgAewAAJSMOASMiJjU0NjMyFhUUBwYVFDMyPgc3IyImNTQ+\

AzsBPgEzMhYVFAYjIiY1NDc2NCMiBwYHFz4BMzIWFRQGIyImNTQ3NjQjIg4HBzMyFhQGKwEOASMi\

JjU0NjMyFhUUBwYVFDMyPgcBX5shdl8gMBcTEhcSChkLEA8LDQoODRQKNQkKAQMEBgNBFGk0IDAX\
ExIXEgoZJhcDAZsUaTQgMBcTEhcSChkHDAoHCAUGAwYBNgkLCwk/IXZfIDAXExIXEgoZCxAPCw0K\



Dw0U5sXBJiAaIhQPDgsHDQ4GERMlJDw3VScMCQMFBQQCS18mIBoiFA8OCwYccwwGAUtfJiAaIhQP\
DgsGHAULChQNGg0eBgwQDMXBJiAaIhQPDgsHDQ4GERMlJDw3VQAAAAAB/37/YAN0AbgAswAAEzM+\
ATMyFhUUBiMiJjU0NzY0IyIHBgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIWFRQOASsBDgEjIiY1\
NDYzMhYVFAcGFRQzMj4HNyMOASMiJjU0NjMyFhUUBwYVFDMyPgc3Iw4BIyImNTQ2MzIWFRQHBhUU\

MzI+BzcjIiY1ND4DOwE+ATMyFhUUBiMiJjU0NzY0IyIOAgcGzJwUaTQgMBcTEhcSChkmFwMBmxRp\
NCAwFxMSFxIKGQcMCgcIBQYDBgE2CQoECQY/IXZfIDAXExIXEgoZCxAPCw0KDw0UCpshdl8gMBcT\
EhcSChkLEA8LDQoODRUKmyF2XyAwFxMSFxIKGQsQDwsNCg4NFAo1CQoBAwQGA0EUaTQgMBcTEhcS\
ChkOFQ4IBgIBDktfJiAaIhQPDgsGHHMMBgFLXyYgGiIUDw4LBhwFCwoUDRoNHgYMCAUJBsXBJiAa\

IhQPDgsHDQ4GERMlJDw3VSfFwSYgGiIUDw4LBw0OBhETJSQ7OFUnxcEmIBoiFA8OCwcNDgYREyUk\
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\
DA0EDgINEh4PIy0kGBAYCQ8KCAgUEQ0JEwwRAxIfFVoDBAgNBr8CAgEBGB8EDQsIEx4uBAIBCAEB\
AREHowMFAgcDBwsDBA0GCAgUCxM3KxQJAQgGB6smIBoiFA8OCwIFBAYDCAYGERMlJDw3VScGCQYI\
C0tfJiAaIhQPDgsGHAULChQNGg0eBgwQDMXBAAAAAgAF//sB/AGaAAkALAAAAQ8BBhUUMzI2NwcO\


ASMiJjU0PwEjNTM/AQc3MhU2MzIWFRQGIiY1NDcGDwEjATZ/OQIUGEQSDCkyHyIiAzdpcxZZJpgZ\
IDUYHRggFgk3EEJIAQkHuAgDFRcPKBkUJBoLC7MgSi54Ci0pHBUSGxEOEhMPJ9gAAQAA//0BtQDU\
AC8AACUyNTQnBiImNT4BMzIWFRQHBiMiLwEmIyIVFBc2MzIWFRQGByInJjU0NzYzFh8BFgFwLRoQ\
HBQBFwkkLSkXHiYeohoRLhkQDw0VFA0dGBwoFiIqF6IcMTkgExAWDQ4WMzUzJBUVehI6IBIQFw4P\
EgIaIi0zJBMCEHoTAAEAAP/NAbUBAwA2AAAXIiY1NDc2MxYfATUzFRcWMzI2NTQnBiMiNT4BMzIW\


FRQHBiMiLwEVIzUnJiMiBhUUFzYzMhUUTSAtKBYgKBcuHlwiDxUcFhQSHAEPCSAtKRccJB4uHlwg\
DxYcFRQTHANDJjMkEwIQJGmARxcpHCcQDBsPFUImMyQVFSRsg0cWKB4nDwwdIAABAA0AAAJFAOAA\
CwAANyc3FzcXNxcHJwcnIhWOZXhqTRaSaXRpLhmZfHx8VBehfHx8AAAAAQAN/8sCRQERABMAACUH\
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\
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\
RTUyNAd1bmlFNTI1B3VuaUU1MjkHdW5pRTUyQQd1bmlFNTJCB3VuaUU1MkMHdW5pRTUyRAd1bmlF\
NTJGB3VuaUU1MzAHdW5pRTUzMQd1bmlFNTM5B3VuaUU1NjYHdW5pRTU2Nwd1bmlFNTY5B3VuaUU1\
NkMHdW5pRTU2RAd1bmlFNTgyB3VuaUU1RDAHdW5pRTVFMgd1bmlFNjEwB3VuaUU2MTIHdW5pRTYx\
NAd1bmlFNjE4B3VuaUU2MjQHdW5pRTYzMAd1bmlFNjUwB3VuaUU2NTUHdW5pRTkxMAd1bmlFOTEx\
B3VuaUU5MTIHdW5pRTkxNAd1bmlFOTE1B3VuaUU5MTgHdW5pRTkyMAd1bmlFOTI1B3VuaUU5NUQH\
dW5pRUEwMgd1bmlFQUE0AAAAAAH//wACAAEAAAAAAAAADAAUAAQAAAACAAAAAQAAAAEAAAAAAAEA\

AAAA1+feHQAAAADRlyIXAAAAANgjLVI=\
")'

|
|
|
|
|
|
|
|
|



|
|
>
|
|
|
|
|
|
<
|
>
>
|
|
|
|

|
>
|


|
<
|
|
|
|
|
|
>
|
<
|
|
|
>
>
>
>
>
|
|
<
|
|
|
<
<
|
<
|
>
|
|
|
<
|
>
|
|
|
|
|
|
|
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
|
|
|
<
<
<
<
|
<
>
|
<
<
>
|
|
|
<
|
<
>
|
|
<
|
|
|
>
>
|
|
|
|
|
|
|
|
<
<
|
>
>
|
>
>
|
<
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
|
|
<
|
<
<
|
|
>
>
|
>
|
>
>
>
|
<
>
|
<
|
>
|
<
>
|
<
<
>
>
|
<
>
>
>
>
|
|
<
|
>
|
|
|
|
|
<
|
|
>
>
>
>
>
>
>
>
>
|
>
|
<
>
|
<
|
<
|
|
|
<
>
|
|
<
|
|
<
<
|
|
|
<
>
>
>
|
<
>
|
>
|
<
<
<
|
<
<
>
>
|
|
|
|
|
<
|
<
>
|
>
|
|
>
>
>
|
<
|
|
>
|
|
|
<
>
|
<
<
<
<
<
<
<
<
|
|
|
|
|
|
|
|
|
<
<
|
>
>
|
|
<
<
|
>
>
|
|
|
<
|
>
|
|
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
>
|
>
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
>
|
|
|
|
|
|





|
>
|
|
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/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//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\

5KTkqOSs5MDkzuTq5O7lAeUl5S3lMeU55WflaeVt5YLl0OXi5hDmEuYU5hjmJOYw5lDmVekS6RXp\
GOkg6SXpXeoC6qTso+yl7Kfsqey3//8AAAAAACDgAOAi4DDgOOBD4EXgUOBc4GLgaeB64IDglOCg\
4Kngs+EB4bnhu+Hn4fLh9OH34fnh/OH+4kDiYOKA5KDkouSk5KjkrOTA5M7k4eTu5QDlIOUp5S/l\
OeVm5WnlbOWC5dDl4uYQ5hLmFOYY5iTmMOZQ5lXpEOkU6RjpIOkl6V3qAuqk7KLspeyn7Knst///\
AAP/5CAFH+Qf2R/SH8kfyB/BH7YfsR+rH5sfmR+SH4gfhB97Hy4edx52HkseQR5AHj4ePR47Hjod\
+x3lHcobrhutG6wbqRumG5Mbhht0G3EbYBtCGz8bPhs3GwsbChsIGvQapxqWGmkaaBpnGmQaWRpO\
Gi8aKxdxF3AXbhdnF2MXLBaIFecT6hPpE+gT5xPaAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAQYAAAMAAAAAAAAAAQIAAAACAAAAAAAAAAAAAAAAAAAAAQAABAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAogAAAAqACoAKgA2\

AD4AbgB8AIoAmACkALAAvgDcAVABrAIcAl4C6gN8A9wD8ASMBR4FdgW0BdYF6gY8BowGrAbyBzIH\
bAe4B/gIPgiYCKwI0gj4CSoJRgloCZQJqgnECfoKCAoUCiAKMgpOCnAKfgqSCqQKtAsACxALKgtE\
C3wLtAwCDE4MtA0WDZQODA4yDkwOfA6mDv4PJA9+D54P3BAEEBgQJBAyEEIQahCQEJwQqBC0EMAQ\
4hEIET4RjBHsEmASdhKcEsoTHhOSE+YUJhRqFMAWGhcmF+AYoBlwGgwa6hwGHQgdSh2OHdgd8h4W\
HioePh5uHn4ekB6sHsIe5h8WH/ggtCDcIPohLCFqIZwh3iHwIf4iGiI0IloifCKqIsQi7iMqIzwA\
AAACACIAAAEyAqoAAwAHAC6xAQAvPLIHBADtMrEGBdw8sgMCAO0yALEDAC88sgUEAO0ysgcGAfw8\
sgECAO0yMxEhESczESMiARDuzMwCqv1WIgJmAAABAAAAAAGRAZAAAwAAMREhEQGRAZD+cAABAAAA\
AAAAAAAAAAAAMQAAAQAAAAAAVwQDACAAABE1NjU0JyY1NDcGFRQXFhUUBxYVFAcGFRQXJjU0NzY1\

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\

MjY0JiMDNTMRIzUzMhYVFCMDAmd1glolKRseHgghJF44NjY4Nk0RIxRZsSodHSod/eoqHR0qHf6J\
KDxGRjzcRkbccYn6a2dldwwSHqYEjG2YbUk+CkpRHSodHSodHSodHSoBaf6YYaZh/noeAWgeZ2vS\
AAAAA//cAAIB3gKzAAcADwBNAAAAIiY0NjIWFAQiJjQ2MhYUFzQ2MzIWFRQHFjMyNjU0LwEDJxMu\


ATU0PgE3NjMyFhUUBiMiJjU0NyYjIgYVFB8BExcDHgEVFA4BBwYjIiYBniAYGCAY/n4gGBggGFsb\

FBMeLBcpJjYme88r0VpIHBMUJjMwOBsUEx4sFykmNiZ61CvVWkgcExQmMzA4AVIYIBgYIFQYIBgY\
INMSHBoRHQ4XLSYtJmX+3iABJUdxOBYtERETOCESHBoRHQ4XLSYtJmUBJh/+10dyOBYtERETOAAF\
AHr/NANeAlQAFwAbAB8AIwAnAAABMxUeARczFSMOAQcVIzUuAScjNTM+ATcRIxYXNxU2NyczJicH\
NQYHAdgoVXgHiooHeFUoVXgHiooHeFVwBWsoawVwcAVrKGsFAlSJCItgKGCLCImJCItgKGKJCP7l\
ug7IyA66KLoOyMkOuwAAAAAEAAD9bwKnBIgACwBEAE4AYwAAAQYVFBc+ATU0Jw4BExcVFAYjIiY1\

NDYzMhYUBgcWMzI2PQEnBiMiJjU0Nz4GPwEmNTQ2NxYVFAYHFzYzMhYVFCc0JiMiBiMTPgEnDgEV\
FBYXLgE1NDcnDgEHHgEzMjcBbAcFSHU2OUJLF09NUl9AMi9BPy8tGC9AFxwNmNs6DSQgLh0xEhcW\
DWxJYlx5FBQLcYhOYlACBwIhW0HiNkYnHT0/phGPbAECoXkKFgNTNy0jWjGVSHINB177bOwDTFxT\
QS1IO1g3ARk8RgPpAsedhWYXMCUtGicOERCWZoyhBzrliKlfzwKcc866PmYB/p0SXfILRjEgQhIO\
RUepLr1yl2eNowIAAgAA/fwC0gIAAGQAaAAAATI2NTQnJiMiBw4CByYnJicRIxEzETY3NjceAxcW\
MzI2NTQnJiMiBxYXFBYVFAYrASY1NDc2NzYzMhcWFxUUBgcGIyInBxc2MzIWFxYdAQYHBiMiJjU0\
NzMyFhUUBhUGBxYBMxEjAd4+Sg0aSUY8AgYKBCIaHi4cHC4eGiIGFAwZDyclMT0SJFIvMTINAjMh\
BUQFGlUnI15VNwhaSB8tND8iIj80QmYeKAg3Vl1MckQFITMCDzA1/k17e/4geEotLWxJBQ4bCmIn\
Kx/+BAQA/hEfKydiCyoXHQkbe0IxNmIaECgDDQQeKxkyFAtEGQ1PNFMSTm8cDRdLShc+LDlDElM0\
UE48MhkrHgQOBCYQHAPe/AAAAAP/uv2lAsMA/wAqADYAQgAANzQ2MzIWFxYVFAYHDgEHNjc+ATc2\

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\




BiczOzguHBITHgUpPkhAFglETVYVASk+SEH+qQFXGzkzNzo9SCUsKykrNjkZITdkRTQYqyRUQ10p\

BAwEN2VFMxkAAAACAAD//wE8ArwAGAAmAAAVETMyPgM1NCcWFRQHFhUUDgUHNTI+AzU0JjUOAx4W\
QEg+KQUeExIcLjg7MycGFkFIPikBFVZNRAEBVxg0RWQ3IRk5NispKywlSD06NzM5G6sZM0VlNwQM\


AyldQ1MAAAAAAwAA/SoBPACRABsAKQA3AAAZATMeBhUUBxYVFAcWFRQHNjU0LgIjNR4DFzQ2NTQu\
AiMnHgMXNDY1NC4CIx4GJzM7OC4cEhISEx4FPVdVHAlETVYVATxWVR0CCURNVhUBPFZVHf6QAgEb\
OTM3Oj1IJSwrJy0sKykrNjkZIUR3SCmpJFRBXSkEDQNEdkgpqyRTQl0pBA0DRHZIKQAAAwAA/1YB\
PAK9ABwAKQA2AAARMzI+AzU0JxYVFAcWFRQHFhUUDgUHIzcyPgI1NCY1DgMnMj4CNTQmNQ4DHhZA\

SD4pBR4TEhISHC44OzMnBh4eHVVXPQEVVk1ECR1VVz0BFVZNRAFXGDRFZDchGTk2KykrLC0nKywl\

SD06NzM5G6opSXhEBAwEKV1DVIYpSXhEAw0EKV1DVAAEAAD9QgE8AVQAIAAuADwASgAAGQEzHgYV\
FAcWFRQHFhUUBxYVFAc2NTQuAyM1HgMXNDY1NC4DJx4DFzQ2NTQuAyceAxc0NjU0LgMeBiczOzgu\
HBISEhISEx4FKT5IQBYJRE1WFQEpPkhBFglETVYVASk+SEEWCURNVhUBKT5IQf6oAqwbOTM3Oj1I\

JSwrJy0sKyctLCspKzY5GSE3ZEU0GKskVENdKQQMBDdlRTMZqyRUQ10pAw4DN2VFMxmrJFRDXSkE\
DAQ3ZUUzGQAEAAD+jgE8AqAAHwAsADkARgAAGQEzMj4CNTQnFhUUBxYVFAcWFRQHFhUUDgUHNTI+\
AjU0JjUOAycyPgI1NCY1DgMnMj4CNTQmNQ4DHhxVVz0FHhMSEhISEhwuODszJwYdVVc9ARVWTUQJ\
HVVXPQEVVk1ECR1VVz0BFVZNRP6OAqwpSHdEIRk5NispKywtJyssLScrLCVIPTo3MzkbqilJeEQD\
DQQpXUNUhilJeEQDDQQpXUNUhilJeEQDDQQpXUNUAAAABQAA/VUBPAISACQAMgBAAE4AXAAAGQEz\
HgYVFAcWFRQHFhUUBxYVFAcWFRQHNjU0LgMjNR4DFzQ2NTQuAyceAxc0NjU0LgMnHgMXNDY1NC4D\
Jx4DFzQ2NTQuAx4GJzM7OC4cEhISEhISEhMeBSk+SEAWCURNVhUBKT5IQRYJRE1WFQEpPkhBFglE\
TVYVASk+SEEWCURNVhUBKT5IQf67A1cbOTM3Oj1IJSwrJy0sKyctLCsnLSwrKSs2ORkhN2RFNBir\
JFRDXSkDDQQ3ZUUzGaskVENdKQQMBDdlRTMZqyRUQ10pAw4DN2VFMxmrJFRDXSkEDAQ3ZUUzGQAA\
BQAA/bwBPAJ5ACMAMAA9AEoAVwAAGQEzMj4CNTQnFhUUBxYVFAcWFRQHFhUUBxYVFA4FBzUyPgI1\
NCY1DgMnMj4CNTQmNQ4DJzI+AjU0JjUOAycyPgI1NCY1DgMeHFVXPQUeExISEhISEhIcLjg7MycG\
HVVXPQEVVk1ECR1VVz0BFVZNRAkdVVc9ARVWTUQJHVVXPQEVVk1E/bwDVylId0QhGTk2KykrLC0n\
KywtJyssLScrLCVIPTo3MzkbqilJeEQDDQQpXUNUhilJeEQDDQQpXUNUhilJeEQDDQQpXUNUhilJ\


eEQDDQQpXUNUAAAAAgAA/2QA4QGwAAoAFgAANyIGHQE2NzY1NCY3MhYVFAcGIxEzETZnFCsiJisd\
ByM5S1JEKCN6JhO5Dzg7LxsmJjIjSUxSAkz+vDQAAAIAAP6GAMUBegADAAwAABc3NQcRFTcRIzUH\
ETcckJCpGawBZS2WLQFJ6DT9wOIzAkMBAAACAAD+mAD/AWgAAwAfAAA3FTc1AyM1BzU3NQc1NzUz\
FTc1MxU3FQcVNxUHFSM1B1NaWh41NTU1HlodNTU1NR1aRqcbp/43ow9cD6cPWg+onxyrow9cD6cP\
Wg+onxwAAAABABT/hAELAHoAHgAAFzUmJwcwFSM1MzcnMCM1MxUWFzcwNTMVIwYHFzAzFcMoDDNI\
OTMzOUghEjRIOSETNDl8OyYNMztKMjJIOSIRMzlHIhE0SAAEAAD/agFsAbAADgAcACsAOgAANw4B\
HQEyNzY3NjU0JyYjNzIWFRQHBgcGIxEzETYXDgEdATI3Njc2NTQnJiM3MhYVFAcGBw4BIxEzETZO\


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\

AAEAAP4MAUgAwAAkAAAXBiMiJjU0NjMyFhUUBzI/AQYjIiY1NDYzMhYVFAcyNzYyFwMnqyghKzcn\


GxwpF0ELPDYYKzcnGxwpF0guAhUDxS3EDCgoICAfGR0bIsoMKSgfIB4ZHRtNAgL9bgwAAAEAAP4M\
AY8BwAA2AAA3BiMiJjU0NjMyFhUUBzI/ASIOASMiJjU0NjMyFhUUBzI3NjIXAScTBiMiJjU0NjMy\
FhUUBzI39igfKzcnGxwpFz8LOgEgHBMrNycbHCkXSC4BFgP+9C1VKCErNycbHCkXQQs8DCgoICAf\
GR0bIssJBCkoHyAeGR0bTQIC/G4MASQMKCggIB8ZHRsiAAAAAAEAAP0MAdoBwABFAAATBiMiJjU0\
NjMyFhUUBzI/AQYjIiY1NDYzMhYVFAcyPwEGIyImNTQ2MzIWFRQHMj8BIg4BIyImNTQ2MhYVFAcy\
NzYyFwEnqyghKzcnGxwpF0ELOighKzcnGxwpF0ELOigfKzcnGxwpFz8LOgEgHBMrNyY4KRdILgIV\
A/6pLf48DCgoICAfGR0bIsoMKCggIB8ZHRsiygwoKCAgHxkdGyLLCQQpKB8gHhkdG00CAvtuDAAA\
AAEAAP0MAhkCrgBWAAAlBiMiJjU0NjMyFhUUBzI/AQYjIiY1NDYzMhYVFAcyPwEiDgEjIiY1NDYz\
MhYVFAcyNzYyFwEnEwYjIiY1NDYzMhYVFAcyPwEGIyImNTQ2MzIWFRQHMjcBPyghKzcnGxwpF0EL\
NigfKzcnGxwpFz8LNAEgHBMrNycbHCkXSC4BFgP+ai1VKCErNycbHCkXQQs6KCErNycbHCkXQQs0\
DCgoICAfGR0bIsQMKCggIB8ZHRsixwkEKSgfIB4ZHRtNAgL6gAwBJAwoKCAgHxkdGyLIDCgoICAf\

GR0bIgABAAD/GgNSAOYACwAANTMVITUzESM1IRUjGQMgGRn84BnmfX3+NH19AAAAAAP/8P8GAiYA\
+gAHAA8AEwAANiImNDYyFhQAIiY0NjIWFAUBMwFQMiMjMiMBiDIjIzIj/dIBuH7+R0sjMiMjMv7P\

IzIjIzJaAfT+DAAE/+H/BgMHAPoABwAPABMAFwAANiImNDYyFhQAIiY0NjIWFAUBMwEzATMBQTIj\
IzIjAncyIyMyI/zjAbh7/kd5Abh7/kdLIzIjIzL+zyMyIyMyWgH0/gwB9P4MAAL/tP+IAXwBGAAR\
ADsAADcWMzI2NzY1NCcmIyIGBwYVFBciJwczMhQrASI0OwETNjU0IyIOAwcGJjc2NzYzMhYXPgEz\

MhYVFAbFAgUSMg4NDwIEEjULDycqGS00CwvhCwtLaAYLCAwPCxkKBRsFMQ8YJCMkBx0mIx4tWSgB\
NSQgJiUFATMcJyQpLiB6Hh4BHRIMDwcUEiwQCA8JWBAZExoeDzQwSWsAAAH/2//2Ab4BGABTAAA3\


BgcGKwEiPwE2JiMiBgcGJjc+AzMyFzYzMhc+ATMyFhUUDwEGFRQzMjc+BTc2FgcOAiMiJjU0PwE2\
NTQjIg8BBgcnBiY/ATY1NCMiB1AHCAQENQ0NQgQGCA0WJAUVBBQQIh8SNwskJC0JCykTGSQFLwQI\
AQQFCwcMAw0BBhUGEhctHxUZBTQBFRsIQQgPJg0IBEMBFRsIFREDARqnDQ8aOQgMCSUaMBIoKCgQ\

GCMaCw99CwkOAgMJBg8FEgEJDQsfHxoWEw0NiAIEDhSrFgEBAQ8IqwIEDhQAAAAB/37/YAFeAbgA\
QQAAByImNTQ2MzIWFRQHBhUUMzI+BzcjIjU0OwE+ATMyFhUUBiMiJjU0NzY0IyIOBwczMhQrAQ4B\
MiAwFxMSFxIKGQsQDwsNCg4NFAo1ExFBFGk0IDAXExIXEgoZBwwKBwgFBgMGATYTEz8hdqAmIBoi\
FA8OCwcNDgYREyUkPDdVJxUTS18mIBoiFA8OCwYcBQsKFA0aDR4GKMXBAAAB/9sAAAETARMAKgAA\
Nz4BJiMiBg8BBgcGKwEiPwE2JiMiBgcGJjc+ATMyFhc2MzIWFRQjIiY1NNAFAwMFFCQJPwcIBAQ1\
DQ1CBAYIDhciBRUEITgiGx0EHyQaICsPG+cDBwQsGJwRAwEaow0PGjUIDAk8QBoNJx4ZNxYNFAAA\

AAEAAAAAANwBGAAxAAAzIiY1NDYyFhUUBxYzMjY1NC4CJyY1NDYzMhYVFAYjIiY1NDcmIyIGFRQe\
AhcWFRRQHjIVGhcQDBMWIQkLGAY6Ny0iNhYQDRYHDxEPGRESHgUwKxsQFhAMEhAQFhILDwcNBCUw\
IygkGBAYFA4DDhMRDQkTDBEDICtVAAAB/+T//ADnAQ8APAAAJwYuAT8BIgYjIiYOAQcGJyY3PgE3\
HgEzMjYzMhcWFA8BBhUUMzI2MzIXFjc2JyI1NDYzMhUUBisBLgEjIgoGCwEFsAUfDAMUDB8FDwQD\
CAwLAQ4vFSIrBAgFCweeAgIBDQYaIw0IBQckEg0mKB4dGCUIDgMECA0GwQYGAjIFDQsJEh4wAgEG\
CwEBEQenBgIDAxoKDg0DHQ4VNx4vAxgABf+0/4gFSwEYABsANQDZAPABCwAAJRYzMj4BNz4GNTQm\
JyYjIgYHDgIVFAUWMzI2Nz4FNTQmJyYjIgYHBhUUHgEXIicHMzIUKwEiNDsBEz4CNTQuAiMiDgMH\

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\

IgYHBhUUHgEDUhYZLTQLC+ELC0toAQMCBQYOFBRrSRYZLTQLC+ELC0toAQIBAgUGDhQUa0kWGS00\
CwvhCwtLaAEDAgECBQMIDA8LGQoFGwUxDxgkIyQHHSYjLxMSIhsjJAcdJiMvExIiGyMkBx0mIx4t\

a0wDBBIyDgIDAgICAQEHCAMDDCEeBw8J/sIDBAwfHgkCAwICAgEBBwgDAxM0CwUHA/7LAwQSMg4C\

AwMDAQEHCAMDEzQLDwQHIHoeHgEdAggOBgcIGCMBR20geh4eAR0BAwUFBgcDBwgYIwFHbSB6Hh4B\
HQIIDgYDBQUCBxQSLBAIDwlYEBkTGh4PMhwWExoeDzIcFhMaHg80MEdtKAE1JAQKCQsKCgsFEBcD\
ARglEiYlEhoDARgqFwQKCQsKCgsFEBcDATMcDRsYCygHATUkBQsMDAwMBhAXAwEzHCYlDBQNAAP/\

tP+IAsEBGABXAHEAjQAAMyInBzMyFCsBIjQ7ARM+AjU0LgIjIg4DBwYmNzY3NjMyFhc+ATMyFz4B\
MzIWFz4BMzIWFRQGIyInBzMyFCsBIjQ7ARM0PgM0NTQmIyIGBxUUBicWMzI2Nz4FNTQmJyYjIgYH\
BhUUHgEFFjMyPgE3PgY1NCYnJiMiBgcOAhUUyBYZLTQLC+ELC0toAQMCAQIFAwgMDwsZCgUbBTEP\

GCQjJAcdJiMvExIiGyMkBx0mIx4ta0kWGS00CwvhCwtLaAECAQIFBg4UFGtMAwQSMg4CAwMDAQEH\
CAMDEzQLDwQHAUoDBAwfHgkCAwICAgEBBwgDAxM0CwUHAyB6Hh4BHQIIDgYDBQUCBxQSLBAIDwlY\


EBkTGh4PMhwWExoeDzQwR20geh4eAR0BAwUFBgcDBwgYIwFHbSgBNSQFCwwMDAwGEBcDATMcJiUM\
FA0CARgqFwQKCQsKCgsFEBcDATMcDRsYCygAAAL/2/+IAysBGAB6AI8AACU2NzYzMhYXPgEzMhYV\
FAYjIicHMzIUKwEiNDsBEz4DNTQmIyIOAwcOASMiJjU0Nj8CNCMiDwEGBycGJj8BNjU0JiMiDwEG\

BwYrASI/ATYmIyIGBwYmNz4DMzIXNjMyFz4BMzIeAxUUDwEGFRQzMjc+ARcWMzI2Nz4BNTQmJyYj\
IgYHBhUUFgGtMRQYIiMkBx0mIx4ta0kWGS00CwvhCwtLaAEBAwEFBgcPEg4YCBo8LRUZAgM0ARUb\
CEEIDyYNCARDAQwJGwhEBwgEBDUNDUIEBggNFiQFFQQUECIfEjcLJCQtCQspEwoSDwsHBS8ECAID\
DyXRAwQSMg4GBwcIAwMTNAsPCXdsGB0TGh4PNDBHbSB6Hh4BHQIFCQkFBwgNHxs1EDY4FRQGDQeI\

Bg4UqxYBAQEPCKsDAwYIFKwRAwEapw0PGjkIDAklGjASKCgoEBgGCw8TCg0NfQsJDgIINTkBNSQP\
JhEQFwMBMxwmJRIaAAL/2/9gAxkBuABXAJ8AADcGBwYrASI/ATYmIyIGBwYmNz4DMzIXNjMyFz4B\
MzIeAxUUDwEGFRQzMjc+BTc2FgcOAiMiJjU0Nj8CNCMiDwEGBycGJj8BNjU0JiMiBxMiJjU0NjMy\
FhUUBwYVFDMyPgc3IyImNTQ2OwE+ATMyFhUUBiMiJjU0NzY0IyIOBwczMhYVFA4BKwEOAVAHCAQE\



NQ0NQgQGCA0WJAUVBBQQIh8SNwskJC0JCykTChIPCwcFLwQIAgMFCwcMAw0BBhUGEhctHxUZAgM0\


ARUbCEEIDyYNCARDAQwJGwj1IDAXExIXEgoZCxAPCw0KDg0UCjUJCgkIQRRpNCAwFxMSFxIKGQcM\
CgcIBQYDBgE2CQsFCQY/IXYVEQMBGqcNDxo5CAwJJRowEigoKBAYBgsPEwoNDX0LCQ4CAwkGDwUS\
AQkNCx8fGhUUBg0HiAYOFKsWAQEBDwirAwMGCBT+nyYgGiIUDw4LBw0OBhETJSQ8N1UnDAkIC0tf\
JiAaIhQPDgsGHAULChQNGg0eBgwIBQkGxcEAAAAAAf9+/2ACaQG4AHsAACUjDgEjIiY1NDYzMhYV\
FAcGFRQzMj4HNyMiJjU0PgM7AT4BMzIWFRQGIyImNTQ3NjQjIgcGBxc+ATMyFhUUBiMiJjU0NzY0\
IyIOBwczMhYUBisBDgEjIiY1NDYzMhYVFAcGFRQzMj4HAV+bIXZfIDAXExIXEgoZCxAPCw0KDg0U\
CjUJCgEDBAYDQRRpNCAwFxMSFxIKGSYXAwGbFGk0IDAXExIXEgoZBwwKBwgFBgMGATYJCwsJPyF2\

XyAwFxMSFxIKGQsQDwsNCg8NFObFwSYgGiIUDw4LBw0OBhETJSQ8N1UnDAkDBQUEAktfJiAaIhQP\

DgsGHHMMBgFLXyYgGiIUDw4LBhwFCwoUDRoNHgYMEAzFwSYgGiIUDw4LBw0OBhETJSQ8N1UAAAAA\
Af9+/2ADdAG4ALMAABMzPgEzMhYVFAYjIiY1NDc2NCMiBwYHFz4BMzIWFRQGIyImNTQ3NjQjIg4H\
BzMyFhUUDgErAQ4BIyImNTQ2MzIWFRQHBhUUMzI+BzcjDgEjIiY1NDYzMhYVFAcGFRQzMj4HNyMO\
ASMiJjU0NjMyFhUUBwYVFDMyPgc3IyImNTQ+AzsBPgEzMhYVFAYjIiY1NDc2NCMiDgIHBsycFGk0\
IDAXExIXEgoZJhcDAZsUaTQgMBcTEhcSChkHDAoHCAUGAwYBNgkKBAkGPyF2XyAwFxMSFxIKGQsQ\
DwsNCg8NFAqbIXZfIDAXExIXEgoZCxAPCw0KDg0VCpshdl8gMBcTEhcSChkLEA8LDQoODRQKNQkK\
AQMEBgNBFGk0IDAXExIXEgoZDhUOCAYCAQ5LXyYgGiIUDw4LBhxzDAYBS18mIBoiFA8OCwYcBQsK\
FA0aDR4GDAgFCQbFwSYgGiIUDw4LBw0OBhETJSQ8N1UnxcEmIBoiFA8OCwcNDgYREyUkOzhVJ8XB\
JiAaIhQPDgsHDQ4GERMlJDw3VScMCQMFBQQCS18mIBoiFA8OCwYcEyciGwoAAf9+/2AEgAG4AOcA\

AAEXPgEzMhYVFAYjIiY1NDc2NCMiBwYHFz4BMzIWFRQGIyImNTQ3NjQjIg4HBzMyFhQGKwEOASMi\
JjU0NjMyFhUUBwYVFDMyPgc3Iw4BIyImNTQ2MzIWFRQHBhUUMzI+BzcjDgEjIiY1NDYzMhYVFAcG\
FRQzMj4HNyMOASMiJjU0NjMyFhUUBwYVFDMyPgc3IyImNTQ+AzsBPgEzMhYVFAYjIiY1NDc2NCMi\
BwYHFz4BMzIWFRQGIyImNTQ3NjQjIg4HAdicFGk0IDAXExIXEgoZJhcDAZsUaTQgMBcTEhcSChkH\
DAoHCAUGAwYBNgkKCgk/IXZfIDAXExIXEgoZCxAPCw0KDw0UCpshdl8gMBcTEhcSChkLEA8LDQoO\
DRQKmyF2XyAwFxMSFxIKGQsQDwsNCg8NFAqbIXZfIDAXExIXEgoZCxAPCw0KDg0UCjUJCgEDBAYD\

QRRpNCAwFxMSFxIKGSYXAwGbFGk0IDAXExIXEgoZBwwKBwgFBgMGAQ8BS18mIBoiFA8OCwYccwwG\
AUtfJiAaIhQPDgsGHAULChQNGg0eBgwQDMXBJiAaIhQPDgsHDQ4GERMlJDw3VSfFwSYgGiIUDw4L\








Bw0OBhETJSM8N1YnxcEmIBoiFA8OCwcNDgYREyUkPDdVJ8XBJiAaIhQPDgsHDQ4GERMlJDw3VScM\
CQMFBQQCS18mIBoiFA8OCwYccwwGAUtfJiAaIhQPDgsGHAUKCxMOGQ4dAAMAAP9gAt8BuAA3AHsA\
xQAAMyImNTQ2MzIWFRQHFjMyNjU0LgMnLgI1NDYzMhYVFAYjIi4BNTQ3JiMiBhUUHgIXHgEVFCUG\
LgE/AS4CIyIGIyIOAQcGJyY3PgE3HgEzMjYzMhYXFhQPAQ4BFRQeARcWPgEnLgE1NDYzMhUUBwYj\
IiMuAiMiBgUiJjU0NjMyFhUUBw4BHgEVFBYyPgc3IyIuATU0NjsBPgEzMhYVFAYjIiY1NDc2NCMi\
DgcHMzIWFAYrAQ4BUB4yEgwOFwwGGBYhAwwFFwMUGBQ3LSI2FhAIEAsFEA4PGRESHgUbFwEmBgsB\
Ba4ECQoHBhwGBRASBRAFAwcLDAEYJQ4gKAcECwcLB5oCAhoqDQUJBQIDJBQMJiYSEwMDEiMYCAoS\
/r0gMBcTEhcSBAEBAgYWEA8LDQoODRQKNQYJBAkIQRRpNCAwFxMSFxIKGQcMCgcIBQYDBgE2CQoK\
CT8hdisbEBYNCxgMEhYSCQwNBA4CDRIeDyMtJBgQGAkPCggIFBENCRMMEQMSHxVaAwQIDQa/AgIB\


ARgfBA0LCBMeLgQCAQgBAQERB6MDBQIHAwcLAwQNBggIFAsTNysUCQEIBgerJiAaIhQPDgsCBQQG\
AwgGBhETJSQ8N1UnBgkGCAtLXyYgGiIUDw4LBhwFCwoUDRoNHgYMEAzFwQAAAAIABf/7AfwBmgAJ\
ACwAAAEPAQYVFDMyNjcHDgEjIiY1ND8BIzUzPwEHNzIVNjMyFhUUBiImNTQ3Bg8BIwE2fzkCFBhE\
EgwpMh8iIgM3aXMWWSaYGSA1GB0YIBYJNxBCSAEJB7gIAxUXDygZFCQaCwuzIEoueAotKRwVEhsR\
DhITDyfYAAEAAP/9AbUA1AAvAAAlMjU0JwYiJjU+ATMyFhUUBwYjIi8BJiMiFRQXNjMyFhUUBgci\


JyY1NDc2MxYfARYBcC0aEBwUARcJJC0pFx4mHqIaES4ZEA8NFRQNHRgcKBYiKheiHDE5IBMQFg0O\
FjM1MyQVFXoSOiASEBcODxICGiItMyQTAhB6EwABAAD/zQG1AQMANgAAFyImNTQ3NjMWHwE1MxUX\
FjMyNjU0JwYjIjU+ATMyFhUUBwYjIi8BFSM1JyYjIgYVFBc2MzIVFE0gLSgWICgXLh5cIg8VHBYU\
EhwBDwkgLSkXHCQeLh5cIA8WHBUUExwDQyYzJBMCECRpgEcXKRwnEAwbDxVCJjMkFRUkbINHFige\
Jw8MHSAAAQANAAACRQDgAAsAADcnNxc3FzcXBycHJyIVjmV4ak0Wkml0aS4ZmXx8fFQXoXx8fAAA\
AAEADf/LAkUBEQATAAAlBycHJzcXNzUzFzcXNxcHJwcVIwEWQGlLFY5lFhsBRmpPFJJpGRtGRnxO\

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\

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\
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
SplineFontDB: 3.0
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\\).
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
PfmFamily: 17
TTFWeight: 400
TTFWidth: 5
LineGap: 92
VLineGap: 92
Panose: 2 0 5 3 0 0 0 0 0 0
OS2TypoAscent: 0
|




|















|







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.2
FontName: abc2svg
FullName: abc2svg
FamilyName: abc2svg
Weight: Regular
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: 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
Encoding: Custom
Compacted: 1
UnicodeInterp: none
NameList: Adobe Glyph List
DisplaySize: -96
AntiAlias: 1
FitToEm: 1
WinInfo: 48 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

StartChar: .nodef
Encoding: 0 0 0
Width: 400
Flags: M
LayerCount: 2
Fore







|



|







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: 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: 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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE043
Encoding: 9 57411 9
Width: 100
Flags: W
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







|
<
<







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: MW


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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE048
Encoding: 13 57416 13
Width: 862
Flags: W
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







|
<
<
<







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: MW



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
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
 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
 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
 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
 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
 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
 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
 375 138 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE05C
Encoding: 15 57436 15
Width: 722
Flags: W
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







|
|
|
|



|
|





|
|

|
|







|
|


|
|









|
|








|
<
<







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
 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 -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
 377 -620 435 -592 435 -490 c 0
 435 -489 435 -488 435 -487 c 0
 412 -254 l 17
 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 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 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
 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: MW


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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE062
Encoding: 16 57442 16
Width: 703
Flags: W
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 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
 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
 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
 632 -170 610 -150 610 -119 c 0
 610 -90 629 -69 656 -69 c 4
 683 -69 707 -90 707 -119 c 0
 707 -148 687 -170 661 -170 c 0x9e
655 72 m 0x3e
 625 72 605 94 605 127 c 0
 605 156 626 174 654 174 c 4
 681 174 702 153 702 127 c 0
 702 94 682 72 655 72 c 0x3e
EndSplineSet
Validated: 1
EndChar

StartChar: uniE069
Encoding: 17 57449 17
Width: 470







|
<
<



|


|
|
|
|
|
|

|
|
|



|
|

|

|
|

|

|







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: HMW


LayerCount: 2
Fore
SplineSet
19 29 m 0
 19 161 125 255 261 255 c 0
 378 255 440 220 496 146 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 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 0
661 -170 m 0
 632 -170 610 -150 610 -119 c 0
 610 -90 629 -69 656 -69 c 0
 683 -69 707 -90 707 -119 c 0
 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 0
 681 174 702 153 702 127 c 0
 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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07A
Encoding: 18 57466 18
Width: 779
Flags: W
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 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
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
 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
 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
 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
 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
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







|
<
<



|



|





|











|















|





|











|
<
<







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: MW


LayerCount: 2
Fore
SplineSet
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 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 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 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 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 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: MW


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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07C
Encoding: 20 57468 20
Width: 807
Flags: W
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 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
 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
 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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE07D
Encoding: 21 57469 21
Width: 200







|
<
<



|















|
|



|
|



|







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: MW


LayerCount: 2
Fore
SplineSet
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 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 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 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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE08A
Encoding: 32 57482 32
Width: 409
Flags: W
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
 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







|
<
<






|
|







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: HMW


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 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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE08B
Encoding: 33 57483 33
Width: 408
Flags: W
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







|
<
<







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: MW


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
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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE0A3
Encoding: 40 57507 40
Width: 329
Flags: W
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
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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE0A9
Encoding: 42 57513 42
Width: 330







|
|
|
|
|







|
<
<



|
|
|
|
|
|
|
|
|
|
|
|
|
|











|
|
|
|
|







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 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: MW


LayerCount: 2
Fore
SplineSet
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 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
 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









































































































































































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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE241
Encoding: 49 57921 49
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
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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE243
Encoding: 51 57923 51
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
 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
 226 365 56 278 30 171 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE244
Encoding: 52 57924 52
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
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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE245
Encoding: 53 57925 53
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
 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
 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
 226 367 56 280 30 171 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE246
Encoding: 54 57926 54
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
 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
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
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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE247
Encoding: 55 57927 55
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
 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 -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
 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
 226 338 56 251 30 142 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE248
Encoding: 56 57928 56
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
 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
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
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
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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE249
Encoding: 57 57929 57
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
 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 -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
 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
 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
 226 299 56 213 30 103 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE260
Encoding: 58 57952 58
Width: 269
Flags: HMW
LayerCount: 2
Fore
SplineSet
103 122 m 0
 77 122 40 90 40 65 c 2








|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





|
|
|
|
|
|
|
|





|


















|





|
|
|
|
|
|
|
|
|
|
|
|
|
|





|








|
|
|
|
|
|


|
|






|




















|
|



|
|





|







|
|
|
|
|
|
|
|



|
|


|
|






|








|
|
|
|
|
|
|
|
|
|



|
|


|
|


|
|





|








|
|
|
|
|
|
|
|
|
|

|
|
|
|

|
|


|
|






|








|
|
|
|
|
|
|
|
|
|
|
|



|
|


|
|


|
|


|
|





|








|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|

|
|


|
|


|
|






|







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: 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 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: 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: 58 57922 58
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
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: 59 57923 59
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 -1 m 9
 0 342 l 1
 30 342 l 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
 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: 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 -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 -135 105 -28 32 -28 c 0
 31 -28 31 -28 30 -28 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE245
Encoding: 61 57925 61
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 343 m 17
 30 343 l 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
 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
 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: 62 57926 62
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 -344 m 9
 0 340 l 1
 30 340 l 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 -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 -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 -145 292 -139 292 -132 c 0
 292 62 101 169 30 169 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE247
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 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 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
 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
 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: 64 57928 64
Width: 315
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 -325 m 9
 0 530 l 1
 30 530 l 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 -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 -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 -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 45 292 51 292 58 c 0
 292 252 101 359 30 359 c 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE249
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 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 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
 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
 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
 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: 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
 40 108 l 1
 63 142 93 160 133 160 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE261
Encoding: 59 57953 59
Width: 200
Flags: W
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







|

|
<







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: 67 57953 67
Width: 200
Flags: MW

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
 1 377 l 1
 28 378 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE262
Encoding: 60 57954 60
Width: 255
Flags: HMW
LayerCount: 2
Fore
SplineSet
83 70 m 21
 83 -97 l 5







|







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: 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
 83 -189 l 5
 83 -360 l 5
EndSplineSet
Validated: 1
EndChar

StartChar: uniE263
Encoding: 61 57955 61
Width: 267
Flags: W
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







|

|
<
<







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: 69 57955 69
Width: 267
Flags: MW


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
 267 -124 l 1
 195 -124 l 9
EndSplineSet
Validated: 1
EndChar

StartChar: uniE264
Encoding: 62 57956 62
Width: 366
Flags: HMW
LayerCount: 2
Fore
SplineSet
78 125 m 1
 55 124 31 100 31 78 c 2







|







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: 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
 219 113 l 1
 235 143 256 163 291 163 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE280
Encoding: 63 57984 63
Width: 269
Flags: HMW
LayerCount: 2
Fore
SplineSet
171 122 m 0
 140 122 116 92 116 57 c 0







|







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: 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
 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
Width: 366
Flags: HMW
LayerCount: 2
Fore
SplineSet
87 125 m 1
 86 125 l 2







|







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: 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
 219 113 l 1
 235 143 256 163 291 163 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE282
Encoding: 65 57986 65
Width: 169
Flags: HMW
LayerCount: 2
Fore
SplineSet
68 320 m 8
 98 320 l 1







|







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: 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
 68 152 l 1
 68 320 l 8
EndSplineSet
Validated: 1
EndChar

StartChar: uniE283
Encoding: 66 57987 66
Width: 314
Flags: HMW
LayerCount: 2
Fore
SplineSet
233 192 m 17
 233 360 l 1







|







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: 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
 233 101 l 1
 172 83 l 9
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4A0
Encoding: 67 58528 67
Width: 320
Flags: W
HStem: 0 244<5 15>
VStem: -3 322
LayerCount: 2
Fore
SplineSet







|







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: 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
 319 109 314 101 305 98 c 1
 18 1 l 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4A2
Encoding: 68 58530 68
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
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
Flags: HMW
LayerCount: 2
Fore
SplineSet
40 0 m 29
 0 280 l 25
 100 280 l 25
 60 0 l 25
 40 0 l 29
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4AC
Encoding: 71 58540 71
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
Width: 600
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 0 m 1
 0 214 150 330 300 330 c 3







|















|















|
|




|



|





|



















|







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: 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: 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: 78 58536 78
Width: 100
Flags: HMW
LayerCount: 2
Fore
SplineSet
40 0 m 25
 0 280 l 25
 100 280 l 25
 60 0 l 25
 40 0 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4AC
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: 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
 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
Width: 182
Flags: HMW
LayerCount: 2
Fore
SplineSet
86 301 m 0
 123 301 136 294 156 272 c 0







|







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: 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
 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
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
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
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
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
Width: 238
Flags: MW
LayerCount: 2
Fore
SplineSet
41 391 m 1
 230 162 l 1







|















|















|















|















|







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: 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: 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: 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: 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: 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
 122 193 l 1
 41 391 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E6
Encoding: 79 58598 79
Width: 255
Flags: W
HStem: 48 33<112 170.038>
VStem: 0 135<81.9955 177.625>
LayerCount: 2
Fore
SplineSet







|







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: 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
 58 -227 l 1
 171 60 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E7
Encoding: 80 58599 80
Width: 329
Flags: W
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
 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
 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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E8
Encoding: 81 58600 81
Width: 399
Flags: W
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
 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
 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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E9
Encoding: 82 58601 82
Width: 472
Flags: W
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
 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
 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
 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
 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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4EA
Encoding: 83 58602 83
Width: 472
Flags: W
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
 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
 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
 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
 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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4EE
Encoding: 84 58606 84
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







|

|
<
<



|



|












|





|

|
<
<



|

|
|
|



















|





|

|
<
<



|



|






|




|
|
|












|





|

|
<
<



|



|






|
















|









|





|







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: 88 58599 88
Width: 329
Flags: MW


LayerCount: 2
Fore
SplineSet
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 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 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E8
Encoding: 89 58600 89
Width: 399
Flags: MW


LayerCount: 2
Fore
SplineSet
246 60 m 1
 226 54 217 48 175 48 c 0
 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 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E9
Encoding: 90 58601 90
Width: 472
Flags: MW


LayerCount: 2
Fore
SplineSet
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 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 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 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 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4EA
Encoding: 91 58602 91
Width: 472
Flags: MW


LayerCount: 2
Fore
SplineSet
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 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 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 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 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4EE
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
 0 -230 l 25
 0 230 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE500
Encoding: 85 58624 85
Width: 563
Flags: W
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







|

|
<
<







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: 93 58624 93
Width: 563
Flags: MW


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
 109 -250 l 1
 -16 -250 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE501
Encoding: 86 58625 86
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







|







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: 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
 334 -250 l 5
 212 -250 l 5
EndSplineSet
Validated: 1
EndChar

StartChar: uniE520
Encoding: 87 58656 87
Width: 563
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
 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
 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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE521
Encoding: 88 58657 88
Width: 440
Flags: W
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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE522
Encoding: 89 58658 89
Width: 351
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
 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
 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>




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
 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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE525
Encoding: 91 58661 91
Width: 563
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
 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
 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
 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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE529
Encoding: 92 58665 92
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







|
|





|
|
|
|
|
|
|
|
|
|







|
|






|





|

|
<
<



|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|





|
|













|
|










|
|






|
|
|
|
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>



|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|





|
|







|
|
|


|
|
|


|
<
|
|
|
>
|
|
|
|
>
>
|





|







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: 95 58656 95
Width: 379
Flags: HMW
LayerCount: 2
Fore
SplineSet
197 40 m 0
 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 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 319 0 220 0 c 4
EndSplineSet
Validated: 1
EndChar

StartChar: uniE521
Encoding: 96 58657 96
Width: 440
Flags: MW


LayerCount: 2
Fore
SplineSet
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: 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 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 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: 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 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 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: 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
 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 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
 65 75 64 72 64 70 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: 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
 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
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







|







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: 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
 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
Width: 703
Flags: HMW
LayerCount: 2
Fore
SplineSet
200 0 m 0
 183 0 173 6 153 32 c 1







|







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: 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
 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
Width: 811
Flags: HMW
LayerCount: 2
Fore
SplineSet
429 119 m 0
 480 232 500 280 556 280 c 0







|







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: 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
 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
Width: 351
Flags: HMW
LayerCount: 2
Fore
SplineSet
80 21 m 0
 76 11 74 1 57 0 c 0







|







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: 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
 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
Width: 617
Flags: HMW
LayerCount: 2
Fore
SplineSet
351 230 m 9
 196 230 l 1







|







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: 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
 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
Width: 351
Flags: HMW
LayerCount: 2
Fore
SplineSet
204 270 m 9
 360 270 l 1







|







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: 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
 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
Width: 617
Flags: HMW
LayerCount: 2
Fore
SplineSet
472 271 m 9
 628 270 l 1







|







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: 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
 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
Width: 351
Flags: HMW
LayerCount: 2
Fore
SplineSet
80 0 m 0xd8
 41 0 0 33 0 70 c 0xe8







|







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: 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
 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
Width: 509
Flags: HMW
LayerCount: 2
Fore
SplineSet
310 265 m 1
 183 258 l 1







|







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: 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
 230 0 l 1
 242 40 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE567
Encoding: 102 58727 102
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







|







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: 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
 323 68 l 1
 342 55 353 49 368 49 c 0x5c
EndSplineSet
Validated: 1
EndChar

StartChar: uniE569
Encoding: 103 58729 103
Width: 437
Flags: HMW
LayerCount: 2
Fore
SplineSet
77 -3 m 0
 33 -3 0 54 0 102 c 0







|







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: 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
 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
Width: 580
Flags: W
HStem: 0 224
LayerCount: 2
Fore
SplineSet
34 46 m 13







|







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: 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
 109 124 l 5
 34 46 l 13
EndSplineSet
Validated: 1
EndChar

StartChar: uniE56D
Encoding: 105 58733 105
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







|







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: 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
 278 -53 l 25
 278 70 l 1xa0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE582
Encoding: 106 58754 106
Width: 280
Flags: HMW
LayerCount: 2
Fore
SplineSet
123 0 m 25
 123 123 l 25







|







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: 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
 157 0 l 25
 123 0 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE5D0
Encoding: 107 58832 107
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
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







|















|







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: 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: 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
 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
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
Width: 250
Flags: HMW
LayerCount: 2
Fore
SplineSet
105 0 m 25
 0 450 l 29
 40 450 l 29
 125 90 l 25
 210 450 l 29
 250 450 l 29
 145 0 l 25
 105 0 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE614
Encoding: 111 58900 111
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
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
Width: 250
Flags: HMW
LayerCount: 2
Fore
SplineSet
90 120 m 0
 110 120 132 165 132 200 c 28







|



















|





|
|
|

|
|

|





|




















|

















|







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: 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: 119 58898 119
Width: 250
Flags: HMW
LayerCount: 2
Fore
SplineSet
105 0 m 29
 0 450 l 25
 40 450 l 25
 125 90 l 25
 210 450 l 25
 250 450 l 25
 145 0 l 25
 105 0 l 29
EndSplineSet
Validated: 1
EndChar

StartChar: uniE614
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: 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: 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
 108 0 l 25
 72 0 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniE630
Encoding: 114 58928 114
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
 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
 116 0 l 25
 84 0 l 25
 84 101.288034818 l 1
 84 102 l 1
116 126 m 5
 149.481193966 134.213607404 175 164.479867787 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
 84 200 l 25
 116 200 l 25
 116 126.757636954 l 1
 116 126 l 5
EndSplineSet
Validated: 1
EndChar

StartChar: uniE650
Encoding: 115 58960 115
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







|






|


|
|


|

|
|


|
|


|
|





|







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: 123 58928 123
Width: 200
Flags: HMW
LayerCount: 2
Fore
SplineSet
84 102 m 1
 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 163 109 116 102 c 1
 116 101 l 1
 116 0 l 25
 84 0 l 25
 84 101 l 1
 84 102 l 1
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 51 134 84 126 c 1
 84 127 l 1
 84 200 l 25
 116 200 l 25
 116 127 l 1
 116 126 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE650
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
 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
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







|







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: 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
 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
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
45 0 m 28
 45 -113 137 -205 250 -205 c 28







|







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: 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
 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
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
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
 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
EndSplineSet
Validated: 1
EndChar

StartChar: uniE914
Encoding: 120 59668 120
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
463 130 m 0
 464 127 465 123 465 120 c 0







|




















|





|
|
|
|
|


|
|
|
|
|
|
|
|
|
|
|
|





|







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: 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: 128 59666 128
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
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 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: 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
 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
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
463 130 m 0
 464 127 465 123 465 120 c 0







|







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: 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
 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
Width: 500
Flags: HMW
LayerCount: 2
Fore
SplineSet
273 249 m 1
 356 243 424 203 463 130 c 0







|







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: 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
 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
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
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
Width: 540
Flags: HMW
LayerCount: 2
Fore
SplineSet
30 70 m 25
 30 -70 l 25







|















|















|







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: 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: 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: 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
 0 -190 l 25
 0 190 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniEA02
Encoding: 126 59906 126
Width: 250
Flags: HMW
LayerCount: 2
Fore
SplineSet
0 65 m 25
 137 250 l 25







|







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: 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
 32 41 l 25
 0 65 l 25
EndSplineSet
Validated: 1
EndChar

StartChar: uniEAA4
Encoding: 127 60068 127
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
 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

























































































































EndSplineSet
Validated: 1
EndChar















EndChars
EndSplineFont







|










|
|




|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


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: 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 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 -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
// 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









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) {
    var	pit = Number(p)
	if (isNaN(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()

    // 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,
	maps = this.get_maps(),
		a = parm.split(/\s+/)

	switch (a[1]) {
	case "channel":
		if (a[2] != "10")
			break
		this.set_v_param("midictl", "0 1") // channel 10 is bank 128 program 0


		break
	case "drummap":
		if (this.cfmt().sound != "play")
			break
//fixme: should have a 'MIDIdrum' per voice?
		n = norm(a[2]);
		v = tonote(a[3]);
		if (!n || !v) {
			this.syntax(1, this.errs.bad_val, "%%MIDI drummap")
			break
		}

		if (!maps.MIDIdrum)
			maps.MIDIdrum = {}

		maps.MIDIdrum[n] = [null, 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
		}





		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) {
			s = this.new_block("midictl");

			s.ctrl = n;
			s.val = v


		}
		break
	}
    }, // do_midi()

    // set the MIDI parameters in the current voice
    set_midi: function(a) {












>
>
>
>
>
>
>
>




|
>
>
>
>
>
|
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|



|

<
<
<
<
<
<
|
|

<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
<
|


|

|
<
>
>


<
<

|
|




>


>
|
>












>
>
>
>
>
|
>












<
<
|

>


>
>







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) {

    // 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		// octave
	pit = pit % 12;			// in octave






	return p * 40 + abc2svg.isb40[pit] + 2
    } // mid_b40()















    // do_midi()
    var	n, v, s, maps,

	a = parm.split(/\s+/)

	switch (a[1]) {
	case "channel":				// channel 10 is bank 128
		if (a[2] != "10")
			break			// other channel values are ignored

		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":


//fixme: should have a 'MIDIdrum' per voice?
		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] = []
		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)
		}
		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.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
		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;

		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) {
	if (cmd == "ambitus") {
		this.cfmt().ambitus = param
		return
	}
	of(cmd, param, lock)
    },

    set_width: function(of, s) {
	if (s.clef_type != undefined && s.nhd > 0) {
		s.wl = 40;
		s.wr = 12
	} else {







>











|




|







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) {
	if (cmd == "ambitus") {
		this.cfmt().ambitus = param
		return
	}
	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
		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,
			[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)







|
<







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, -abc2svg.ifb40[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
    },

    output_music: function(of) {
	this.capo_first = false;
	of()
    },

    set_fmt: function(of, cmd, param, lock) {
	if (cmd == "capo") {
		this.cfmt().capo = param
		return
	}
	of(cmd, param, lock)
    },

    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







|




|













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) {
	if (cmd == "capo") {
		this.cfmt().capo = param
		return
	}
	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
// 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


    // 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))













|
>







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,
	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
	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
	 || 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;


	s.notes = s.notes.concat(s2.notes);

	s.nhd = nhd = s.notes.length - 1;
	this.sort_pitch(s)		// 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)
} // combine_notes()

// combine 2 voices
function do_combine(s) {
	var s2, nhd, nhd2, type

	while (1) {







|
<
<















>
|
>

|














|
|
|
|
|
|







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)


		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
		s2.notes[m].s = s
	Array.prototype.push.apply(s.notes, s2.notes);
	s.nhd = nhd = s.notes.length - 1;
	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].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
			}
		} else if (s.type == C.REST) {
			if (s.invis
			 && !s2.invis)
				delete s.invis
		} else {
			combine_notes.call(this, s, s2)


		}









		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


				s.a_dd = s.a_dd.concat(s2.a_dd)


		}
		this.unlksym(s2)			// remove the next symbol

		// there may be more voices
		if (s.in_tuplet || !may_combine.call(this, s))
			break
	}
} // do_combine()















	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







>
>


>
>
>
>
>
>
>
>






>
>
|
>
>
|








>
>
>
>
>
>
>
>
>
>
>
>
>
>







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)
		}

		// 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
			if (s2.beam_end)
				break
			do {
				s2 = s2.next
			} while (s2.type != C.NOTE && s2.type != C.REST)
		}
	}






    }, // 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)







>
>
>
>
>
>







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

    output_music: function(of) {
	if (this.cfmt().diag)
		abc2svg.diag.do_diag.call(this)
	of()
    },

    set_fmt: function(of, cmd, param, lock) {
	if (cmd == "diagram") {
		this.cfmt().diag = param
		return
	}
	of(cmd, param, lock)
    },

    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







|




|







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) {
	if (cmd == "diagram") {
		this.cfmt().diag = param
		return
	}
	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
    // 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) {
	if (cmd == "equalbars")
		this.cfmt().equalbars = this.get_bool(param)
	else
		of(cmd, param, lock)
    },

    // 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
	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:







|



|













|

<
<







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) {
	if (cmd == "equalbars")
		this.cfmt().equalbars = this.get_bool(param)
	else
		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/space
	for (s2 = tsfirst; s2; s2 = s2.ts_next) {


		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
	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
		}
	}



	bars.push([s, s.time - t]);



	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 (!d)
		d = this.equalbars_d = x;	// offset first note/rest


	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)








>
>
>
|
>
>
>










|
<
|

>







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])
	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;
	if (this.equalbars_d < x)

		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
// 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]
//		<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

//	%%gridfont font_name size (default: 'serif 16')

abc2svg.grid = {

// function called before tune generation
    do_grid: function() {
    var	C = abc2svg.C,







|





>







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] [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
	 && 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 = '',
	pcell = '',
	cells = [],
	nc = grid.n



	// set some chord(s) in each cell
	function set_chords() {
	    var	i, ch,
		pch = '-'

		for (i = 0; i < chords.length; i++) {







|
|
<

|
>
>







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, k, l, nr, line, bar, w, hr, x0, x, y, yl,
	lc = '',

	cells = [],
	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
				continue
			}
			if (!ch[2])
				ch[2] = ch[1] || ch[0];
			pch = ch[3]
		}
	} // set_chords()





























































	// set some chords in each cell
	set_chords()

	// build the content of the cells
	if (!grid.ls) {
		cells = chords







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
					break
				cells.push(chords[k]);
				bars.push(bar[k])
			}
		}
	}

	// get the number of columns and rows
	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
	}






















	// 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++) {
		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
	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="' +
				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) {



			y += hr;			// new row
			x = x0

		}

		if (bar[bar.length - 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
	}







|






<








<


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




















<

|











|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<












|
>
>
>


>

>
|







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
	if (nc < 0)
		nc = -nc
	if (nc < 3)				// auto
		nc = cells.length % 6 == 0 ? 6 : 8
	if (nc > cells.length)
		nc = cells.length;


	hr = font.size * 2
	if (wmx < hr * 1.4)
		wmx = hr * 1.4;				// cell width

	w = wmx * nc
	if (w > img.width) {
		nc /= 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';

	y = 1
	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 cells
































































	line += lc






	// 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 == 0
		 || (grid.repbrk
		  && (bars[i].slice(-1) == ':' || bars[i][0] == ':'))
		 || k >= nc) {
			y += hr;			// new row
			x = x0
			k = 0
		}
		k++
		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
	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) {
		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:







|







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 = 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
					break
				chords.push(chord);
				bars.push(bt)
			}
			chord = [];
			cur_beat = s.time;	// synchronize in case of error
			beat_i = 0
			if (bt.indexOf(':'))
				rep = true	// some repeat


			break
		case C.METER:
			beat = get_beat(s)
			wm = s.wmeasure
			break
		}
	}







|

>
>







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(':') >= 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
	s = {
		type: C.BLOCK,
		subtype: 'ml',
		dur: 0,
		time: 0,
		p_v: p_voice,
		v: p_voice.v,

		text: build_grid.call(this, chords, bars, font, wmx)
	}

	if (grid.nomusic) {		// if no music
		this.set_tsfirst(s)
		return
	}







>







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

    output_music: function(of) {
	if (this.cfmt().grid)
		abc2svg.grid.do_grid.call(this);
	of()
    },

    set_fmt: function(of, cmd, parm, lock) {
	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.slice(0, 8) == "include=")
				grid.ls = item.slice(8).split(',')
		}
		this.cfmt().grid = grid
		return
	}
	of(cmd, parm, lock)
    },

    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







|


















>
>






|












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) {
	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)
    },

    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
//	%%grid2 y
// This command must appear in a voice.

abc2svg.grid2 = {

// function called before tune generation
    do_grid: function() {
    var s, v, p_v,
	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) {







			if (s.dur) {		// set all notes
				s.invis = true;	//  as invisible
				delete s.sl1;	//  with no slur
				s.ti1 = 0	//  and no tie
				if (s.tf)	// don't show the tuplets
					s.tf[0] = 1




			}







		}
	}
    }, // do_grid()

    // draw the chord symbol in the middle of the staff
    draw_chosym: function(s) {
    var	ix, gch;







|










>
>
>
>
>
>
>
|


|


>
>
>
>
|
>
>
>
>
>
>
>







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, 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
			}

			// set all notes
				s.invis = true;	//  as invisible
				delete s.sl1;	//  with no slur
				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
    },

    output_music: function(of) {
	abc2svg.grid2.do_grid.call(this);
	of()
    },

    set_fmt: function(of, cmd, param, lock) {
	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)
    },

    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







|








|













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) {
	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)
    },

    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
			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
    abc_end: function(of) {
    var page = this.page
	if (page.h) {
		abc2svg.page.img_out(page,
			page.head + page.style + page.hf + page.out + '</svg>');
		abc2svg.page.img_out(page, '</div>');
		page.h = 0;
		page.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()

    // start a new page
    svg_out: function(abc, page) {
    var	h,
	cfmt = abc.cfmt()






	function header_footer(str) {
	    var	c, i, t,

		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 '\t':
				if (j < 2)
					j++		// next column
				continue
			case '\\':			// hope '\n'




				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




				break
			case 'D':
				r[j] += new Date().toUTCString()
				break
			case 'F':
				r[j] += abc.get_fname()
				break
			case 'I':
				c = str[++i]
			case 'T':
				t = abc.info()[c]
				if (t)
					r[j] += t.split('\n', 1)[0]







|


|
|
<
|
<
|




<
<
<
<
<
<
<
<
<
<
|
<
|
|
|
>
>
>
>

>
|
|
>












>
>
>
>
>
>
>
>
>
>
>
>
>
>





>
>
>
>














|
>
>
>
>


|


|







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
    abc_end: function(of) {
    var page = this.page
	if (page && page.in_page) {
		abc2svg.page.close_page(page)

		if (user.img_out == page.img_out_sav)

			user.img_out = page.user_out
	}
	of()
    }, // abc_end()











    // generate a header or a footer in page.hf and return its height

    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(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':
				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] += strftime(cfmt.dateformat)
				break
			case 'F':
				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
					r[j] += t
					break
				}
				break
			case 'V':
				r[j] += "abc2svg-" + abc2svg.version
				break








//fixme: handle the font changes ($<number>) ?










			}
		}



		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
	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)
		}

		a = header_footer(str)
		if (up)
			y0 = page.hbase + fh
		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;
			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
			}
					
		}
		return fh * a[4]
	} // gen_hf()









	// svg_out()



	// start a new SVG container for the page







	abc2svg.page.img_out(abc.page, '<div>');




	page.hbase = page.topmargin;
	page.hmax = cfmt.pageheight - page.topmargin - page.botmargin;
	page.pn++;
	page.pna++;
	page.gutter = -page.gutter;

	// define the header/footer
	page.hf = ''
	if (page.header) {

		h = gen_hf(true, abc.get_font("header"), page.header);









		page.hmax -= h;
		page.hbase += h







	}
	if (page.footer)

		page.hmax -= gen_hf(false, abc.get_font("footer"), page.footer);



	page.h = page.hb = 0;



















	page.out = ''
    }, // svg_out()

    // handle the output flow of the abc2svg generator
    img_in: function(p) {
    var h, i,
	page = this.page

	// copy a block
	function blkcpy(page) {
	    var b, h, i

		if (!page.blk || !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
			    .replace(/<svg(.|\n)*?>/,
				'<g transform="translate(' +
				page.gutter.toFixed(1) + ', ' +
				(h + page.hbase).toFixed(1) +
				')">')
			    .replace('</svg>', '</g>\n');
			h += b.h
		}
		page.blk = null
		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.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
		break
	case "<svg":				// SVG image
		h = Number(p.match(/height="(\d+)px"/)[1])
		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 (!this.cfmt().fullsvg)
				page.style = ''
		}






		p = get_style(p, page)

		// if no overflow yet, keep the block
		if (page.blk) {
			page.blk.push({p: p, h: h})
		} else {
			page.out += 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

		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) {
    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,
				h: 0,		// current page height
				pn: 0,		// page number
				pna: 0,		// absolute page number
				out: '',	// page content
				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







>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>


>
>
>










|
<
<
<
<
<
<

|
|
|
|
|

|
<
|
<
<
|
|
|
|
|
|
|
|
|
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
|

>
>
>
>
>
>
>
>
|
>
>

|
>
>
>
>
>
>
>
|
>
>
>

<
|
<
<
<




>
|
>
>
>
>
>
>
>
>
>

|
>
>
>
>
>
>
>

|
>
|
>
|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|



|




|

|



|
|
|
|
<
<
<
<
<
<
|

|
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<






<
|
|
<
<
<
<
<
<
<
<
|
|



|
<
<
<
<
<
<
<
<
|
|
<
<
|
>
>
>
>
>
|
<


|

|
|
<
<
<
<
<
<



>
|
<



<





|


















|



|
<







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

				// 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
	}

	// gen_hf







	if (str[0] == '-') {		// not on 1st page
		if (page.pn == 1)
			return 0
		str = str.slice(1)
	}

	a = header_footer(font, str)

	y0 = font.size * .8


	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

		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()

    // 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 = ""

	page.pn++
	page.pna++

	// 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.hmax = cfmt.pageheight - page.topmargin - page.botmargin - ht




	// define the header/footer
	page.hf = ''
	if (page.header) {
		h = abc2svg.page.gen_hf(page, true,
					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.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.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, ht,
	page = this.page

	// copy a block
	function blkcpy(page) {
	    var b

		if (!page.blk.length) {
			page.blk = null
			return
		}
		page.h = page.hb
		while (page.blk.length) {
			b = page.blk.shift()
			abc2svg.page.img_out(page, b.p)






			page.h += b.h
		}
		page.blk = null			// direct output

	} // blkcpy()



















	// 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)			// empty page
			abc2svg.page.open_page(page, 0)








		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 (page.blk)		// if inside a block
				blkcpy(page)	// output the beginning of the tune



			ht = page.blk ? 0 :
				this.cfmt().topspace // tune continuation
//			if (page.in_page)
				abc2svg.page.close_page(page)
			abc2svg.page.open_page(page, ht)
		}


		// if no overflow yet, keep the block
		if (page.blk)
			page.blk.push({p: p, h: h})
		else
			abc2svg.page.img_out(page, p)






		page.h += h
		break
	case "</di":				// end of block
		if (page.blk)
			blkcpy(page)

		break
//	default:
////fixme: %%beginml cannot be treated (no information about its height)

//		break
	}
    }, // img_in()

    // handle the page related parameters
    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,
				h: 0,		// current page height
				pn: 0,		// page number
				pna: 0,		// absolute page number
				first: true,	// no skip to next page

				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
				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
			}
			if (cfmt.oneperpage)
				page.oneperpage = this.get_bool(cfmt.oneperpage)



			// 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)
		}







|



>
>







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 = 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
		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)
	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







<
<






|
<
<
<
<
<
<
<
<











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


			return
		case "oneperpage":
			page[cmd] = this.get_bool(parm)
			return
		}
	}
	of(cmd, parm)








    }, // 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
// 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.



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])

// 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












>
>





|







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([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
	"s-g":   73,
	"s-s":   37,
	"s-w":   71,
	"t":     54,
	"v":     58
}



















































    // convert a drum instrument to a note
    function tonote(p) {
    var	i, j, s,
	pit = Number(p)

	if (isNaN(pit)) {
		s = p.match(/^([_^=]*)([A-Ga-g])([,']*)$/)	// '
		if (s) {				// note name
			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
		}

		// 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







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|



|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
<
|







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 b40
    function tob40(p) {
    var	i, j, s,
	pit = Number(p)

	if (isNaN(pit)) {		// not a MIDI pitch
		s = abc_b40(p)		// try a ABC note
		if (s)
















			return s


		// 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
				case 'i':
				case 'o':
					pit = prn[s + p[1]]
					break
				}
				break
			}
		}
	}
	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()

    // 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,
	maps = this.get_maps(),
	a = parm.split(/\s+/);


	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
		}
		delete v.acc





		maps.MIDIdrum[n] = [[a[3]], v]

	} else {					// play
		v = tonote(a[2])
		if (!v) {
			this.syntax(1, this.errs.bad_val, "%%percmap")
			return
		}

		if (!maps.MIDIdrum)
			maps.MIDIdrum = {}
		maps.MIDIdrum[n] = [null, v]
	}

	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[0] = 1		// bank 128
			break
		}
	}
    }, // set_perc()

    do_pscom: function(of, text) {







<
<
|
|
|
<
<
<
<
<

<
<
|
|

<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|

|
>

<




|
|
|
|
>
|
|
|
|
|
|
<
>
>
>
>
>
|
>
|
<
|
<
<
<
>
|
|
|

>















|







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
		}





	}


	return mid_b40(pit)
    } // tob40()















    // do_perc()
    var	vpr, vpl,
	maps = this.get_maps(),
	a = parm.split(/\s+/),
	n = abc_b40(a[1])			// note as b40


	if (!n) {
		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
	}

	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 {

		if (a)



			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[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
	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()
}

// ------ 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);







|







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(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
// 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) {
	if (cmd == "soloffs") {
	    var	i, v,
		C = abc2svg.C,
		soloffs = this.cfmt().soloffs = {}

		parm = parm.split(/\s+/)
		while (parm.length) {











|







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) {
	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
//			default:
//				// error
//				break
			}
		}
		return
	}
	of(cmd, parm, lock)
    },

    set_sym_glue: function(of, width) {
    var	s,
	C = abc2svg.C,
	tsfirst = this.get_tsfirst(),
	soloffs = this.cfmt().soloffs;







|







37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//			default:
//				// error
//				break
			}
		}
		return
	}
	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
	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) {
	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)
    },

    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_stems = abc2svg.sth.set_stems.bind(abc, abc.set_stems)
    }
} // sth

abc2svg.modules.hooks.push(abc2svg.sth.set_hooks);

// the module is loaded







|








|










|







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_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)
    },

    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_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
		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) {
	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)
    },

    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







|

















|













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) {
	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)
    },

    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
{
  "name": "abc2svg",
  "version": "1.19.1",
  "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",

    "abcemb2-1.js",


    "ambitus-1.js",
    "break-1.js",
    "capo-1.js",

    "clip-1.js",
    "cmdline.js",
    "combine-1.js",
    "diag-1.js",
    "edit-1.css",
    "edit-1.js",
    "edit-1.xhtml",


|















>

>
>



>







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.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

    "grid2-1.js",
    "json-1.js",
    "page-1.js",
    "perc-1.js",
    "play-1.js",
    "psvg-1.js",
    "setmidi-1.js",
    "sth-1.js",
    "soloffs-1.js",

    "temper-1.js",
    "toabc.js",
    "toaudio-1.js",
    "toabw.js",

    "toodt.js",

    "toxhtml.js"
  ],
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "bin": {
    "abc2odt": "./abc2odt",
    "abc2svg": "./abc2svg"

  },
  "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"


}








|

>




>

>







|
>















|
>
>
|
>
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",
    "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",
    "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",
  "dependencies": {
    "npm": "^6.10.1"
  }
}
Changes to toabc.js.
︙︙
43
44
45
46
47
48
49

50
51
52
53
54
55
56
		uppermordent: 'P',
		segno: 'S',
		trill: 'T',
		upbow: 'u',
		downbow: 'v',
		roll: '~'
	},

	mode_tb = [			// key modes
		[0, ""],
		[2, "dor"],
		[4, "phr"],
		[-1, "lyd"],
		[1, "mix"],
		[3, "m"],







>







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
			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)
					ln += ' nm="' + 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);




			abc2svg.print(vo[0].join(''));
			vo[0] = []
			return
		}
		for (var v = 0; v < nv; v++) {
			if (vo[v].length == 0)
				continue
			vi_out(v);




			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":
		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":

			if (s.text.indexOf('\n') <= 0
			 && (!s.opt || s.opt == 'c')) {
				if (s.opt == 'c')
					ln = "%%center " + s.text
				else
					ln += s.text
				break







|

>
>










<







>
>
>
>








>
>
>
>











|
<
<
<
<
<











>







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) {
					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()


		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
		default:





			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
			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) {







<
<
<







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



		}
		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
break
}
			d++
		}
		if (d > 0)
			line += "//////".slice(0, d)
	} // dur_dump()














































	function gch_dump(a_gch) {
	    var i, j, gch
		for (i = 0; i < a_gch.length; i++) {
			line += '"';
			gch = a_gch[i]


			switch (gch.type) {
			case 'g':
				for (j = 0; j < gch.text.length; j++) {
					switch (gch.text.charCodeAt(j)) {
					case 0x266d:
						line += 'b'
						break








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



<

>
>







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++) {

			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
		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


		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) {
				case C.BAR:
					if (w.length == 0)
						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++) {







|
|
>













|
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







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

		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) {
				if (s.type != C.NOTE)


					continue



















				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
					case '_\n':
						w[i] += '_'
						break
					default:
						t = t.replace(/ /g, '~')
						t = t.replace(/-/g, '\\-')
						t = t.replace(/\n/g, '-');


						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+:'))
			}
		}
	} // 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, '(')







		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) {







>
>
|








|




















|
|
>
>
|
>
>
>
>
>
>
>







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
						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(/\*+$/,""))
			}
		}
	} // 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, 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
				if (p < 16) {
					line += ","
					if (p < 9)
						line += ","
				}
			}
		}
		if (!tie_ch && note.ti1)
			slti_dump(note.ti1, '-')
		if (note.sl2)
			line += ')'


	} // note_dump()

	function slti_dump(fl, ty) {
		if (fl & C.SL_DOTTED)
			line += ".";
		line += ty
		switch (fl & 0x07) {







|
|
|
|
>
>







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.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
		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)
				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) {







|







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)
				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
		}
		if (s.tempo_str2)
			ln += '"' + s.tempo_str2 + '"'
		info_out(ln)
	} // tempo_dump()

	function tuplet_dump(s) {
	    var	s2, r
		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) {
			line += '(' + s.tp1;
			r = 1
			for (s2 = s.next; s2; s2 = s2.next) {
				if (!s2.dur)
					continue
				r++
				if (s2.te1)
					break
			}

			if (r == s.tp1
			 && ((s.tp1 == 2 && s.tq1 == 3)
			  || (s.tp1 == 3 && s.tq1 == 2)
			  || (s.tp1 == 4 && s.tq1 == 3)))
				;
			else
				line += ':' + s.tq1 + ':' + r
		}
	} // tuplet_dump()

	function sym_dump(s) {
	    var	tie_ch

		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)
			tuplet_dump(s)


		tmp = s.slur_start
		while (tmp) {
			slti_dump(tmp, '(');




			tmp >>= 4







		}
		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!"







|
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
|
<
|
<
|
<
<
<
|
<
>
|
|
|
|


|




|









|

>
>
|
<
|
>
>
>
>
|
>
>
>
>
>
>
>







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	tp


















		while (1) {

			tp = s.tp.shift()

			if (!tp)



				break

			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 += ':' + tp.q + ':' + tp.ro
		}
	} // tuplet_dump()

	function sym_dump(s) {
	    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.tp)
			tuplet_dump(s)
		if (s.sls) {
			for (i = 0; i < s.sls.length; i++) {
				sl = s.sls[i];

				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
			break
		case C.MREST:
			line += s.invis ? 'X' : 'Z'
			if (s.nmes != 1)
				line += s.nmes
			break
		case C.NOTE:













			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) {
						tie_ch = false
						break
					}
				}
				line += '['
				for (i = 0; i < s.notes.length; i++)
					note_dump(s, s.notes[i], tie_ch);







>
>
>
>
>
>
>
>
>
>
>
>
>




















|







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].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
				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
			while (tmp) {
				line += ')';
				tmp--
			}
			break
		case C.PART:

			info_out('P:' + s.text)
			break
		case C.REST:
			line += s.invis ? 'x' : 'z';
			dur_dump(s.dur)
			break
		case C.SPACE:







|
|
|






>







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.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
			break
		default:
			voice_out();
			abc2svg.print('% ??sym: ' + music_types[s.type])
			break
		}
	} // sym_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;







>
>







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
			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

			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.beam_end)
			line += ' '
		if (s.eoln && s.next) {
			line += '$';
			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.print('%abc-2.2\n\
% generated by abc2svg toabc\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()
}







>
|
|
>





>
|
|
|
<
|











|

|
>













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]);
			}
			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 && !s.beam_st && !s.next.beam_end)
				line += ' '
			if (s.eoln)

				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(args) {
	abc2svg.print('%abc-2.2\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
			r[j] += c
			continue
		case '$':
			break
		}
		c = str[++i]
		switch (c) {


		case 'd':	// cannot know the modification date of the file

			break
		case 'D':
			r[j] += (new Date()).toUTCString()
			break
		case 'F':
			r[j] += abc.get_fname()
			break
		case 'I':
			c = str[++i]
		case 'T':
			t = abc.info()[c]
			if (t)
				r[j] += t.split('\n', 1)[0]







>
>
|
>





|







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)
				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.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
	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)
	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]







|







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(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

// replace <>& by XML character references
function clean_txt(txt) {
	return txt.replace(/<|>|&.*?;|&/g, function(c) {
		switch (c) {
		case '<': return "&lt;"
		case '>': return "&gt;"

		}
		if (c == '&')
			return "&amp;"
		return c
	})
}

abc2svg.abort = function(e) {
	abc.blk_flush();
	if (typeof abc2svg.printErr == 'function')







>

<
<







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;"
		}


		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
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'
}













// 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)

	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':







>
>
>
>
>
>
>
>
>
>
>
>










>







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
			continue
		case '$':
			break
		}
		c = str[++i]
		switch (c) {
		case 'd':
			t = abc.get_fname();

			res += fs.statSync(t).ctime.toLocaleString()
			break
		case 'D':
			res += (new Date()).toLocaleString()
			break
		case 'F':
			res += abc.get_fname()
			break
		case 'I':
			c = str[++i]
		case 'T':
			t = abc.info()[c]
			if (t) {
				t_info = '\







|
>
|





|







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':
			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.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
			r[j] += c
			continue
		case '$':
			break
		}
		c = str[++i]
		switch (c) {


		case 'd':	// cannot know the modification date of the file

			break
		case 'D':
			r[j] += get_date()
			break
		case 'F':
			r[j] += abc.get_fname()
			break
		case 'I':
			c = str[++i]
		case 'T':
			t = abc.info()[c]
			if (t)
				r[j] += t.split('\n', 1)[0]







>
>
|
>





|







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)
				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.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
    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)
		for (i = 0; i < 3; i++) {
			str = a[i]
			if (!str)
				continue
			if (str.indexOf('\x0c') >= 0) {
				str = str.replace('\x0c', '');
				page = " page"







|







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(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
<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\
<title>abc2svg document</title>\n\
</head>\n\
<body>')
		if (header)
			gen_hf("header", header)
		if (footer)
			gen_hf("footer", footer);







|
>
>
>
>
>
>
>
|
|







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))
// 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
// 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
}



var	errtxt = '',
	new_page = '',
	abc,

	jsdir = document.currentScript ?
		document.currentScript.src.match(/.*\//) :
		(function() {
			var scrs = document.getElementsByTagName('script');


			return scrs[scrs.length - 1].src.match(/.*\//) || ''
		})()




// -- 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

	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"

	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







>
>













>
>
|

<
>

|
|
|
>
>
|
<
|
>
>
>
|
<







|













<
<
<
<
<
<
<
<
<
<
















<
<
<
<
<
<
<
<
<
|
>


|
<

>







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 = '',
	new_page = '',

	page,				// document source
	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('abcdoc-') >= 0)
					return s_a[k].src.match(/.*\//) || ''

			}
			return ""	// ??

	})(),
    user = {	// -- abc2svg init argument

	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 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)
	}










    // 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"

	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


	try {
		document.body.innerHTML = new_page + page.slice(i)
	} catch (e) {
		alert("abc2svg bad generated SVG: " + e.message +
			"\nStack:\n" + e.stack)
	}























}

// wait for the page to be loaded
document.addEventListener("DOMContentLoaded", dom_loaded, false)








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




>
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
	else if (typeof msg == 'object')
		alert("window error: " + msg.type + ' ' + msg.target.src)
	else
		alert("window error: " + msg)
	return false
}





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(/.*\//) || ''


		})()

// -- 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 endplay() {
	playing = false
}

// function called on click on the music
function playseq(seq) {
    var	outputs

	if (!abcplay) {
		if (typeof AbcPlay == "undefined") { // as play-1.js not loaded,
			playseq = function(){}	     // don't come here anymore
			return
		}
		abcplay = AbcPlay(playconf);
	}
	if (playing) {
		abcplay.stop();
		return







>
>
>
>


<


<
<
<







|
|
|
>
>
|
>
>
|


|







>
|
>
>
>
>
>
>


















|




|







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 = '',

	playing,
	abcplay,



	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 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
    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
abc2svg.playseq = function(seq) {
    var	outputs

	if (!abcplay) {
		if (typeof AbcPlay == "undefined") { // as play-1.js not loaded,
			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
			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,
		seq = 0,
		re = /\n%abc|\nX:/g,
		re_stop = /\nX:|\n<|\n%.begin/g,
		select = window.location.hash.slice(1);






	abc = new abc2svg.Abc(user)















	// check if a selection
	if (select) {
		select = decodeURIComponent(select);
		select = page.search(select)
		if (select < 0)
			select = 0
	}







<
<
<
<
<
<
<
<
<
<
















<
<
<
<
<
<
<
<
<
<

<



|





>
>
>
>
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>







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 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)
	}











function render() {


	// search the ABC tunes,
	// replace them by SVG images with play on click
    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
			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(' +
					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);
			errtxt = ""
		}

		if (page[j] == 'X')
			new_page += '</div>\n'
	    } // selection

		i = k
		if (i >= page.length)
			break







|












<

<
<
<
|
|


>







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="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)
		}

		if (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
	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)
		}






















} // dom_loaded()

// wait for the page to be loaded
document.addEventListener("DOMContentLoaded", dom_loaded, false)







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|
|
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 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
		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(/.*\//) || ''


		})()

// -- 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;"







<















|
>
>
|
>
>
|


|







|







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 = '',

	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 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
    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
	}
	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')







<
<
<
<
<
<
<







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() {








	// 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
		tunes += toabc(elt.innerHTML) + '\n'
	}
	indx[i] = tunes.length;
	ready()
}

function ready() {
    var	i, j

	// 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);






	abc = new abc2svg.Abc(user)





	// generate and replace
	for (var 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)







|












>
>
>
>
>


>
>
>
>

|







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, 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 (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
		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";
			errtxt = ""
		}
		try {
			elts[i].innerHTML = new_page
		} catch (e) {
			alert("abc2svg bad generated SVG: " + e.message +
				"\nStack:\n" + e.stack)







|
|







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 += '<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
			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)







|
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
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
// 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/>.



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
}

// 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)
}

setTimeout(follow, 1000)







>
>



















|
|
|
|
|
<
<
<
<
<
<
<
<
<
<




|
<
|
<
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')
}

	playconf.onnote = function(i, on) {
		var elts = document.getElementsByClassName('_' + i + '_')
		if (elts && elts[0])
			elts[0].style.fillOpacity = on ? 0.4 : 0
	}











	// create the style of the rectangles
	var sty = document.createElement("style");
	sty.innerHTML = ".abcr {fill: #d00000; fill-opacity: 0; z-index: 15}";
	document.head.appendChild(sty)

} // follow()

Changes to util/json.js.
︙︙
36
37
38
39
40
41
42



43
44
45
46
47
48
49
		links = {
			next: true,
			prev: true,
			ts_next: true,
			ts_prev: true,
			extra: true,
			p_v: true,



			dd_st: true
		}
	// generate an attribute
	function attr_gen(ind, attr, val) {
		var	i, e,
			indn = ind + inb	// next indentation








>
>
>







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
//#javascript
// Set the MIDI pitches in the notes
//
// Copyright (C) 2015-2017 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:



//	// 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
//		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


	// add MIDI pitches
	AbcMIDI.prototype.add = function(s,		// starting symbol
					voice_tb) {	// voice table

		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

		// 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()

		// 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)

				continue








			s = voice_tb[v].clef


			if (!s.clef_octave
			 || s.clef_oct_transp)

				transp = 0
			else
				transp = s.clef_octave

			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,
			s = voice_tb[v].sym,
			vtime = s.time,		// next time
			rep_tie_map = []

		tie_map = []
		tie_time = []

		while (s) {
			if (s.time > vtime) {	// if time skip
				bar_map()	// force a measure bar


				vtime = s.time
			}
			if (s.dur)
				vtime = s.time + s.dur
			switch (s.type) {
			case C.BAR:
//fixme: pb when lack of measure bar (voice overlay, new voice)
				// x times repeat
				if (s.text) {
					if (s.text[0] == '1') {	// 1st time
						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 = []
						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
				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];
						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];
					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()

} // end AbcMidi



|

















>
>
>







<
<
<
|










>
|
|
|
|
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<

|
|
>

>
>
>
>
>
>
>
>
|
>
>
|
|
>
|
<
|
|
<
|
<
<
|
|
|
<
<
|

|
|
>
|
<
<
>
>
|
|
|
|

|
<
<
|
|
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<

|
|
|
|
<
<
>



<
<
|
|
<
<
|
<

<
<
<

|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<





|
>

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-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) {
//



//		// 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
	  add: function(s,				// starting symbol
			voice_tb) {			// voice table
	    var p_v, s,








		temper = voice_tb[0].temper,		// (set by the module temper.js)






		v = voice_tb.length































		// initialize the clefs and keys
		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
				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,

				s.clef_octave && !s.clef_oct_transp ?
					(s.clef_octave / 7 * 40) : 0)

		}



	    function vloop(s, sndtran, ctrans) {
		var	i, g, note,


			transp = sndtran + ctrans

		function midi_set(note) {
		    var m = abc2svg.b40m(note.b40 + transp)
			if (temper		// if not equal temperament
			 && (!note.acc


			  || note.acc | 0 == note.acc)) // and not micro-tone
				m += temper[m % 12]
			note.midi = m
		}

		while (s) {
			switch (s.type) {
			case C.CLEF:


				ctrans = (s.clef_octave && !s.clef_oct_transp) ?
						(s.clef_octave / 7 * 40) : 0





				transp = ctrans + sndtran









				break
			case C.KEY:
				if (s.k_sndtran != undefined) {
					sndtran = s.k_sndtran
					transp = ctrans + sndtran


				}
				break
			case C.GRACE:
				for (g = s.extra; g; g = g.next) {


					for (i = 0; i <= g.nhd; i++)
						midi_set(g.notes[i])


				}

				break



			case C.NOTE:
				for (i = 0; i <= s.nhd; i++)
					midi_set(s.notes[i])














				break
			}
			s = s.next
		}
	    } // vloop()
	  } // 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
	function play2(out) {
	    var o

		if (!out)
			out = []
		o = audio5.get_outputs()	// get the HTML5 audio port
		if (o)
			out = out.concat(o)
		if (out.length == 0) {
			if (conf.onend)		// no output port
				conf.onend()

		}
		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++)







|



>







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)
			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




	if (typeof Midi5 == "function")
		midi5 = Midi5(conf)
	if (typeof Audio5 == "function")
		audio5 = Audio5(conf);

	return abcplay
} // AbcPlay











>
>
>
>
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
// 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


// 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







<
<




|
>







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,



	a_e,				// event array

	p_time,				// last playing time
	abc_time,			// last ABC time
	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
	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
	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,
		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
	function set_voices() {
	    var v, p_v, s, mi




		temper = voice_tb[0].temper;	// (set by the module temper.js)





		transp = new Int8Array(voice_tb.length)
		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) {
					switch(i) {
					case 0:		// bank MSB
						mi += val * 128 * 128
						break

					case 32:	// bank LSB
						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]))
					}
				})
			}
			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,
			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) {
			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
				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)
				break
		}
		n = s.notes.length
		for (i = 0; i < n; i++) {
			note = s.notes[i]
			if (note.pit == pit) {

				d += s.dur / play_factor;
				note.ti2 = true
				return note.ti1 ? do_tie(s, note, d) : d
			}
		}
		return d
	} // do_tie()

	// generate the grace notes
	function gen_grace(s) {







|
<
<
<
<
|
<



<
<

<



|



>
>
>
|
>
>
>
>
>
|





|
<
|
<

>
|
<
<
<
>
|
|
|
|
|
|
|
|
<



<
<
<
<
<
<
<
<
<



<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

|
|
>
|
<
<
<

<
<
<

|





<
|








|


|
|

|
>

<
|







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




	var	i, n, dt, d, v,

		rep_st_s,		// start of sequence to be repeated
		rep_en_s,		// end ("|1")
		rep_nx_s,		// restart at end of repeat


		rep_st_fac,		// and play factor

		instr = [],		// instrument per voice
		s = start

	// 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,
				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) {
				for (s = p_v.sym; s; s = s.next)

					if (s.dur)	// search a note/rest

						break
				if (!s)
					continue	// no note in this voice



				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









		}
	} // set_voices()




























































	// handle the ties
	function do_tie(s, b40, d) {
	    var	i, note,
		v = s.v,
		end_time = s.time + s.dur







		// search the end of the tie
		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) {

				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 && s.v == v)
				break
		}
		i = s.notes.length
		while (--i >= 0) {
			note = s.notes[i]
			if (note.b40 == b40) {
				note.ti2 = true	// don't generate sound anymore
				d += s.dur / play_factor;

				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
		}
	} // 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)
				continue
			a_e.push(new Float32Array([
				s.istart,
				t,
				instr[s.v],
				pit2mid(s, i),
				note.ti1 ? do_tie(s, note, d) : d,
				1,
				s.v]))
		}
	} // gen_note()

	// add() main


	set_voices();			// initialize the voice parameters

	if (!a_e) {			// if first call
		a_e = []
		abc_time = rep_st_t = 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
	}



	// 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)
				break






			// right repeat
			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
					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







>
|





|
|







>
|



|


|

>
>



















<
<
<
<
<
<


<
|


>
>
>
>
>

|
<
<





<
<
<
<

|







<
<
<
<
<




<
<
<
<
<
<
<
<
<
<
<

<






<
<
<
<












<
<
<







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)		// tied note
				continue
			a_e.push(new Float32Array([
				s.istart,
				t,
				instr[s.v],
				note.midi,
				note.tie_ty ? do_tie(s, note.b40, d) : d,
				1,
				s.v]))
		}
	} // gen_note()

	// add() main

	// set the MIDI pitches
	abcmidi.add(start, voice_tb)

	if (!a_e) {			// if first call
		a_e = []
		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 = 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
		}







		switch (s.type) {
		case C.BAR:

			if (!s.seqst)
				break

			// end of repeat
			if (s == rep_en_s) {
				s = rep_nx_s
				abc_time = s.time

			// right repeat
			} else if (s.bar_type[0] == ':') {


				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




					play_factor = rep_st_fac;
				} else {	// back to the beginning of the tune
					s = start;
					set_voices();
				}
				abc_time = s.time
				break
			}






			// left repeat
			if (s.bar_type[s.bar_type.length - 1] == ':') {
				rep_st_s = s;
				rep_en_s = null











				rep_st_fac = play_factor


			// 1st time repeat
			} else if (s.text && s.text[0] == '1') {
				rep_en_s = s
			}
			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
︙︙
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
					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
				s.istart,
				p_time,
				-1,			// MIDI control
				s.ctrl,
				s.val,
				1,
				s.v]))





			break
		}
		s = s.ts_next
	}
    } // add()
  } // return
} // ToAudio

// nodejs
if (typeof module == 'object' && typeof exports == 'object')
	exports.ToAudio = ToAudio







<
<
<

|
|
|







>
>
>
>
>











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.BLOCK:
			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
// 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
//		[5]: volume (0..1 - optional)

//
// 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,
		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







|
|
|

>










|
|
|







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
//		[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 = 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
				if (--w_instr == 0)
					play_start(a_e)
			})
	} // load_instr()

	// start loading the instruments
	function load_res(a_e) {
		var i, e, instr


		for (i = evt_idx; ; i++) {
			e = a_e[i]
			if (!e || evt_idx >= iend)
				break
			instr = e[2]





















			if (instr >= 0 && !params[instr]) {
				params[instr] = [];
				load_instr(instr, a_e)

			}
		}
	}

	// create a note
	// @e[2] = instrument index
	// @e[3] = MIDI key + detune







|
>

|

|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
>







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, v,
		bk = []				// bank number

		for (i = evt_idx; i < iend; i++) {
			e = a_e[i]
			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 (!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

	// play the events
	play: function(istart, i_iend, a_e) {
		if (!a_e || istart >= a_e.length) {
			onend()			// nothing to play
			return
		}








		// initialize the audio subsystem if not done yet

		// (needed for iPhone/iPad/...)









		if (!gain) {
			ac = conf.ac
			if (!ac)
				conf.ac = ac = new (window.AudioContext ||
							window.webkitAudioContext);



			gain = ac.createGain();
			gain.gain.value = conf.gain
		}

		iend = i_iend;
		evt_idx = istart;
		load_res(a_e);








>
>
>
>
>
>
>
|
>
|
>
>
>
>
>
>
>
>
>


|


>
>
>







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

		// 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
		}

		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
//		[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() {},
	rf,			// get_outputs result function

// MIDI variables
	op,			// output port
	v_i = [],		// voice (channel) to instrument


// -- 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

		// 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([
				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







|
|





>



















|
|
|







|
<
<
<
<







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 = 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 (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([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
	}
	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

	// play the next events
	if (a_e)			// if not stop
		e = a_e[evt_idx]
	if (!op || evt_idx >= iend || !e) {
		onend()
		return







|







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, 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
			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

			op.send(new Uint8Array([0xb0 + (e[6] & 0x0f),
						e[3], e[4]]), t + stime)













		}

		e = a_e[++evt_idx]
		if (!e || evt_idx >= iend) {
			setTimeout(onend,
				t + stime - window.performance.now() + d)
			return







>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>







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 + 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

	// play the events
	play: function(istart, i_iend, a_e) {
		if (!a_e || istart >= a_e.length) {
			onend()			// nothing to play
			return
		}







		iend = i_iend;
		evt_idx = istart;
if (0) {
// temperament
	op.send(new Uint8Array([
			0xf0, 0x7f,	// realtime SysEx
			0x7f,		// all devices







>
>
>
>
>
>
>







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
︙︙