abcjs-rails 1.8 → 1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (25) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/assets/javascripts/abcjs/api/abc_animation.js +224 -0
  4. data/app/assets/javascripts/abcjs/api/abc_tunebook.js +158 -154
  5. data/app/assets/javascripts/abcjs/data/abc_tune.js +35 -1
  6. data/app/assets/javascripts/abcjs/edit/abc_editor.js +18 -17
  7. data/app/assets/javascripts/abcjs/parse/abc_parse.js +23 -6
  8. data/app/assets/javascripts/abcjs/parse/abc_parse_header.js +5 -1
  9. data/app/assets/javascripts/abcjs/{write/raphael.js → raphael.js} +2562 -266
  10. data/app/assets/javascripts/abcjs/write/abc_absolute_element.js +163 -0
  11. data/app/assets/javascripts/abcjs/write/abc_beam_element.js +162 -0
  12. data/app/assets/javascripts/abcjs/write/abc_cresendo_element.js +46 -0
  13. data/app/assets/javascripts/abcjs/write/abc_dynamic_decoration.js +36 -0
  14. data/app/assets/javascripts/abcjs/write/abc_ending_element.js +53 -0
  15. data/app/assets/javascripts/abcjs/write/abc_glyphs.js +6 -3
  16. data/app/assets/javascripts/abcjs/write/abc_layout.js +84 -29
  17. data/app/assets/javascripts/abcjs/write/abc_relative_element.js +72 -0
  18. data/app/assets/javascripts/abcjs/write/abc_staff_group_element.js +225 -0
  19. data/app/assets/javascripts/abcjs/write/abc_tie_element.js +83 -0
  20. data/app/assets/javascripts/abcjs/write/abc_triplet_element.js +85 -0
  21. data/app/assets/javascripts/abcjs/write/abc_voice_element.js +177 -0
  22. data/app/assets/javascripts/abcjs/write/abc_write.js +65 -28
  23. data/lib/abcjs-rails/version.rb +1 -1
  24. metadata +24 -14
  25. data/app/assets/javascripts/abcjs/write/abc_graphelements.js +0 -790
@@ -0,0 +1,163 @@
1
+ // abc_absolute_element.js: Definition of the AbsoluteElement class.
2
+ // Copyright (C) 2010,2014 Gregory Dyke (gregdyke at gmail dot com) and Paul Rosen
3
+ //
4
+ // This program is free software: you can redistribute it and/or modify
5
+ // it under the terms of the GNU General Public License as published by
6
+ // the Free Software Foundation, either version 3 of the License, or
7
+ // (at your option) any later version.
8
+ //
9
+ // This program is distributed in the hope that it will be useful,
10
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ // GNU General Public License for more details.
13
+ //
14
+ // You should have received a copy of the GNU General Public License
15
+ // along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ /*globals ABCJS */
18
+
19
+ if (!window.ABCJS)
20
+ window.ABCJS = {};
21
+
22
+ if (!window.ABCJS.write)
23
+ window.ABCJS.write = {};
24
+
25
+ // duration - actual musical duration - different from notehead duration in triplets. refer to abcelem to get the notehead duration
26
+ // minspacing - spacing which must be taken on top of the width defined by the duration
27
+ // type is a meta-type for the element. It is not necessary for drawing, but it is useful to make semantic sense of the element. For instance, it can be used in the element's class name.
28
+ ABCJS.write.AbsoluteElement = function(abcelem, duration, minspacing, type) {
29
+ this.abcelem = abcelem;
30
+ this.duration = duration;
31
+ this.minspacing = minspacing || 0;
32
+ this.x = 0;
33
+ this.children = [];
34
+ this.heads = [];
35
+ this.extra = [];
36
+ this.extraw = 0;
37
+ //this.decs = [];
38
+ this.w = 0;
39
+ this.right = [];
40
+ this.invisible = false;
41
+ this.bottom = 7;
42
+ this.top = 7;
43
+ this.type = type;
44
+ };
45
+
46
+ ABCJS.write.AbsoluteElement.prototype.getMinWidth = function () { // absolute space taken to the right of the note
47
+ return this.w;
48
+ };
49
+
50
+ ABCJS.write.AbsoluteElement.prototype.getExtraWidth = function () { // space needed to the left of the note
51
+ return -this.extraw;
52
+ };
53
+
54
+ ABCJS.write.AbsoluteElement.prototype.addExtra = function (extra) {
55
+ if (extra.dx<this.extraw) this.extraw = extra.dx;
56
+ this.extra[this.extra.length] = extra;
57
+ this.addChild(extra);
58
+ };
59
+
60
+ ABCJS.write.AbsoluteElement.prototype.addHead = function (head) {
61
+ if (head.dx<this.extraw) this.extraw = head.dx;
62
+ this.heads[this.heads.length] = head;
63
+ this.addRight(head);
64
+ };
65
+
66
+ ABCJS.write.AbsoluteElement.prototype.addRight = function (right) {
67
+ if (right.dx+right.w>this.w) this.w = right.dx+right.w;
68
+ this.right[this.right.length] = right;
69
+ this.addChild(right);
70
+ };
71
+
72
+ ABCJS.write.AbsoluteElement.prototype.addChild = function (child) {
73
+ child.parent = this;
74
+ this.children[this.children.length] = child;
75
+ this.pushTop(child.top);
76
+ this.pushBottom(child.bottom);
77
+ };
78
+
79
+ ABCJS.write.AbsoluteElement.prototype.pushTop = function (top) {
80
+ this.top = Math.max(top, this.top);
81
+ };
82
+
83
+ ABCJS.write.AbsoluteElement.prototype.pushBottom = function (bottom) {
84
+ this.bottom = Math.min(bottom, this.bottom);
85
+ };
86
+
87
+ ABCJS.write.AbsoluteElement.prototype.draw = function (renderer, bartop) {
88
+ this.elemset = renderer.paper.set();
89
+ if (this.invisible) return;
90
+ renderer.beginGroup();
91
+ for (var i=0; i<this.children.length; i++) {
92
+ this.elemset.push(this.children[i].draw(renderer,this.x, bartop));
93
+ }
94
+ this.elemset.push(renderer.endGroup(this.type));
95
+ if (this.klass)
96
+ this.setClass("mark", "", "#00ff00");
97
+ var self = this;
98
+ this.elemset.mouseup(function () {
99
+ renderer.notifySelect(self);
100
+ });
101
+ this.abcelem.abselem = this;
102
+
103
+ var spacing = ABCJS.write.spacing.STEP*renderer.scale;
104
+
105
+ var start = function () {
106
+ // storing original relative coordinates
107
+ this.dy = 0;
108
+ },
109
+ move = function (dx, dy) {
110
+ // move will be called with dx and dy
111
+ dy = Math.round(dy/spacing)*spacing;
112
+ this.translate(0, -this.dy);
113
+ this.dy = dy;
114
+ this.translate(0,this.dy);
115
+ },
116
+ up = function () {
117
+ var delta = -Math.round(this.dy/spacing);
118
+ self.abcelem.pitches[0].pitch += delta;
119
+ self.abcelem.pitches[0].verticalPos += delta;
120
+ renderer.notifyChange();
121
+ };
122
+ if (this.abcelem.el_type==="note" && renderer.editable)
123
+ this.elemset.drag(move, start, up);
124
+ };
125
+
126
+ ABCJS.write.AbsoluteElement.prototype.isIE=/*@cc_on!@*/false;//IE detector
127
+
128
+ ABCJS.write.AbsoluteElement.prototype.setClass = function (addClass, removeClass, color) {
129
+ if (color !== null)
130
+ this.elemset.attr({fill:color});
131
+ if (!this.isIE) {
132
+ for (var i = 0; i < this.elemset.length; i++) {
133
+ if (this.elemset[i][0].setAttribute) {
134
+ var kls = this.elemset[i][0].getAttribute("class");
135
+ if (!kls) kls = "";
136
+ kls = kls.replace(removeClass, "");
137
+ kls = kls.replace(addClass, "");
138
+ if (addClass.length > 0) {
139
+ if (kls.length > 0 && kls.charAt(kls.length-1) !== ' ') kls += " ";
140
+ kls += addClass;
141
+ }
142
+ this.elemset[i][0].setAttribute("class", kls);
143
+ }
144
+ }
145
+ }
146
+ };
147
+
148
+ ABCJS.write.AbsoluteElement.prototype.highlight = function (klass, color) {
149
+ if (klass === undefined)
150
+ klass = "note_selected";
151
+ if (color === undefined)
152
+ color = "#ff0000";
153
+ this.setClass(klass, "", color);
154
+ };
155
+
156
+ ABCJS.write.AbsoluteElement.prototype.unhighlight = function (klass, color) {
157
+ if (klass === undefined)
158
+ klass = "note_selected";
159
+ if (color === undefined)
160
+ color = "#000000";
161
+ this.setClass("", klass, color);
162
+ };
163
+
@@ -0,0 +1,162 @@
1
+ // abc_beam_element.js: Definition of the BeamElem class.
2
+ // Copyright (C) 2010,2014 Gregory Dyke (gregdyke at gmail dot com) and Paul Rosen
3
+ //
4
+ // This program is free software: you can redistribute it and/or modify
5
+ // it under the terms of the GNU General Public License as published by
6
+ // the Free Software Foundation, either version 3 of the License, or
7
+ // (at your option) any later version.
8
+ //
9
+ // This program is distributed in the hope that it will be useful,
10
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ // GNU General Public License for more details.
13
+ //
14
+ // You should have received a copy of the GNU General Public License
15
+ // along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ /*globals ABCJS */
18
+
19
+ if (!window.ABCJS)
20
+ window.ABCJS = {};
21
+
22
+ if (!window.ABCJS.write)
23
+ window.ABCJS.write = {};
24
+
25
+ ABCJS.write.BeamElem = function(type, flat) {
26
+ this.isflat = (flat);
27
+ this.isgrace = (type && type==="grace");
28
+ this.forceup = (type && type==="up");
29
+ this.forcedown = (type && type==="down");
30
+ this.elems = []; // all the ABCJS.write.AbsoluteElements
31
+ this.total = 0;
32
+ this.dy = (this.asc)?ABCJS.write.spacing.STEP*1.2:-ABCJS.write.spacing.STEP*1.2;
33
+ if (this.isgrace) this.dy = this.dy*0.4;
34
+ this.allrests = true;
35
+ };
36
+
37
+ ABCJS.write.BeamElem.prototype.add = function(abselem) {
38
+ var pitch = abselem.abcelem.averagepitch;
39
+ if (pitch===undefined) return; // don't include elements like spacers in beams
40
+ this.allrests = this.allrests && abselem.abcelem.rest;
41
+ abselem.beam = this;
42
+ this.elems.push(abselem);
43
+ //var pitch = abselem.abcelem.averagepitch;
44
+ this.total += pitch; // TODO CHORD (get pitches from abselem.heads)
45
+ if (!this.min || abselem.abcelem.minpitch<this.min) {
46
+ this.min = abselem.abcelem.minpitch;
47
+ }
48
+ if (!this.max || abselem.abcelem.maxpitch>this.max) {
49
+ this.max = abselem.abcelem.maxpitch;
50
+ }
51
+ };
52
+
53
+ ABCJS.write.BeamElem.prototype.average = function() {
54
+ try {
55
+ return this.total/this.elems.length;
56
+ } catch (e) {
57
+ return 0;
58
+ }
59
+ };
60
+
61
+ ABCJS.write.BeamElem.prototype.draw = function(renderer) {
62
+ if (this.elems.length === 0 || this.allrests) return;
63
+ this.drawBeam(renderer);
64
+ this.drawStems(renderer);
65
+ };
66
+
67
+ ABCJS.write.BeamElem.prototype.calcDir = function() {
68
+ var average = this.average();
69
+ // var barpos = (this.isgrace)? 5:7;
70
+ this.asc = (this.forceup || this.isgrace || average<6) && (!this.forcedown); // hardcoded 6 is B
71
+ return this.asc;
72
+ };
73
+
74
+ ABCJS.write.BeamElem.prototype.drawBeam = function(renderer) {
75
+ var average = this.average();
76
+ var barpos = (this.isgrace)? 5:7;
77
+ this.calcDir();
78
+
79
+ var barminpos = this.asc ? 5 : 8; //PER: I just bumped up the minimum height for notes with descending stems to clear a rest in the middle of them.
80
+ this.pos = Math.round(this.asc ? Math.max(average+barpos,this.max+barminpos) : Math.min(average-barpos,this.min-barminpos));
81
+ var slant = this.elems[0].abcelem.averagepitch-this.elems[this.elems.length-1].abcelem.averagepitch;
82
+ if (this.isflat) slant=0;
83
+ var maxslant = this.elems.length/2;
84
+
85
+ if (slant>maxslant) slant = maxslant;
86
+ if (slant<-maxslant) slant = -maxslant;
87
+ this.starty = renderer.calcY(this.pos+Math.floor(slant/2));
88
+ this.endy = renderer.calcY(this.pos+Math.floor(-slant/2));
89
+
90
+ var starthead = this.elems[0].heads[(this.asc)? 0: this.elems[0].heads.length-1];
91
+ var endhead = this.elems[this.elems.length-1].heads[(this.asc)? 0: this.elems[this.elems.length-1].heads.length-1];
92
+ this.startx = starthead.x;
93
+ if(this.asc) this.startx+=starthead.w-0.6;
94
+ this.endx = endhead.x;
95
+ if(this.asc) this.endx+=endhead.w;
96
+
97
+ // PER: if the notes are too high or too low, make the beam go down to the middle
98
+ if (this.asc && this.pos < 6) {
99
+ this.starty = renderer.calcY(6);
100
+ this.endy = renderer.calcY(6);
101
+ } else if (!this.asc && this.pos > 6) {
102
+ this.starty = renderer.calcY(6);
103
+ this.endy = renderer.calcY(6);
104
+ }
105
+
106
+ var pathString = "M"+this.startx+" "+this.starty+" L"+this.endx+" "+this.endy+
107
+ "L"+this.endx+" "+(this.endy+this.dy) +" L"+this.startx+" "+(this.starty+this.dy)+"z";
108
+ renderer.printPath({path:pathString, stroke:"none", fill:"#000000", 'class': renderer.addClasses('beam-elem')});
109
+ };
110
+
111
+ ABCJS.write.BeamElem.prototype.drawStems = function(renderer) {
112
+ var auxbeams = []; // auxbeam will be {x, y, durlog, single} auxbeam[0] should match with durlog=-4 (16th) (j=-4-durlog)
113
+ renderer.beginGroup();
114
+ for (var i=0,ii=this.elems.length; i<ii; i++) {
115
+ if (this.elems[i].abcelem.rest)
116
+ continue;
117
+ var furthesthead = this.elems[i].heads[(this.asc)? 0: this.elems[i].heads.length-1];
118
+ var ovaldelta = (this.isgrace)?1/3:1/5;
119
+ var pitch = furthesthead.pitch + ((this.asc) ? ovaldelta : -ovaldelta);
120
+ var y = renderer.calcY(pitch);
121
+ var x = furthesthead.x + ((this.asc) ? furthesthead.w: 0);
122
+ var bary=this.getBarYAt(x);
123
+ var dx = (this.asc) ? -0.6 : 0.6;
124
+ renderer.printStem(x,dx,y,bary);
125
+
126
+ var sy = (this.asc) ? 1.5*ABCJS.write.spacing.STEP: -1.5*ABCJS.write.spacing.STEP;
127
+ if (this.isgrace) sy = sy*2/3;
128
+ for (var durlog=ABCJS.write.getDurlog(this.elems[i].abcelem.duration); durlog<-3; durlog++) { // get the duration via abcelem because of triplets
129
+ if (auxbeams[-4-durlog]) {
130
+ auxbeams[-4-durlog].single = false;
131
+ } else {
132
+ auxbeams[-4-durlog] = {x:x+((this.asc)?-0.6:0), y:bary+sy*(-4-durlog+1),
133
+ durlog:durlog, single:true};
134
+ }
135
+ }
136
+
137
+ for (var j=auxbeams.length-1;j>=0;j--) {
138
+ if (i===ii-1 || ABCJS.write.getDurlog(this.elems[i+1].abcelem.duration)>(-j-4)) {
139
+
140
+ var auxbeamendx = x;
141
+ var auxbeamendy = bary + sy*(j+1);
142
+
143
+
144
+ if (auxbeams[j].single) {
145
+ auxbeamendx = (i===0) ? x+5 : x-5;
146
+ auxbeamendy = this.getBarYAt(auxbeamendx) + sy*(j+1);
147
+ }
148
+ // TODO I think they are drawn from front to back, hence the small x difference with the main beam
149
+
150
+ var pathString ="M"+auxbeams[j].x+" "+auxbeams[j].y+" L"+auxbeamendx+" "+auxbeamendy+
151
+ "L"+auxbeamendx+" "+(auxbeamendy+this.dy) +" L"+auxbeams[j].x+" "+(auxbeams[j].y+this.dy)+"z";
152
+ renderer.printPath({path:pathString, stroke:"none", fill:"#000000", 'class': renderer.addClasses('beam-elem')});
153
+ auxbeams = auxbeams.slice(0,j);
154
+ }
155
+ }
156
+ }
157
+ renderer.endGroup('beam-elem');
158
+ };
159
+
160
+ ABCJS.write.BeamElem.prototype.getBarYAt = function(x) {
161
+ return this.starty + (this.endy-this.starty)/(this.endx-this.startx)*(x-this.startx);
162
+ };
@@ -0,0 +1,46 @@
1
+ // abc_crescendo_element.js: Definition of the CrescendoElem class.
2
+ // Copyright (C) 2010,2014 Gregory Dyke (gregdyke at gmail dot com) and Paul Rosen
3
+ //
4
+ // This program is free software: you can redistribute it and/or modify
5
+ // it under the terms of the GNU General Public License as published by
6
+ // the Free Software Foundation, either version 3 of the License, or
7
+ // (at your option) any later version.
8
+ //
9
+ // This program is distributed in the hope that it will be useful,
10
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ // GNU General Public License for more details.
13
+ //
14
+ // You should have received a copy of the GNU General Public License
15
+ // along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ /*globals ABCJS */
18
+
19
+ if (!window.ABCJS)
20
+ window.ABCJS = {};
21
+
22
+ if (!window.ABCJS.write)
23
+ window.ABCJS.write = {};
24
+
25
+ ABCJS.write.CrescendoElem = function(anchor1, anchor2, dir) {
26
+ this.anchor1 = anchor1; // must have a .x and a .parent property or be null (means starts at the "beginning" of the line - after keysig)
27
+ this.anchor2 = anchor2; // must have a .x property or be null (means ends at the end of the line)
28
+ this.dir = dir; // either "<" or ">"
29
+ };
30
+
31
+ ABCJS.write.CrescendoElem.prototype.draw = function (renderer) {
32
+ if (this.dir === "<") {
33
+ this.drawLine(renderer, 0, -4);
34
+ this.drawLine(renderer, 0, 4);
35
+ } else {
36
+ this.drawLine(renderer, -4, 0);
37
+ this.drawLine(renderer, 4, 0);
38
+ }
39
+ };
40
+
41
+ ABCJS.write.CrescendoElem.prototype.drawLine = function (renderer, y1, y2) {
42
+ var ypos = renderer.layouter.minY - 7;
43
+ var pathString = ABCJS.write.sprintf("M %f %f L %f %f",
44
+ this.anchor1.x, renderer.calcY(ypos)+y1-4, this.anchor2.x, renderer.calcY(ypos)+y2-4);
45
+ renderer.printPath({path:pathString, stroke:"#000000", 'class': renderer.addClasses('decoration')});
46
+ };
@@ -0,0 +1,36 @@
1
+ // abc_dynamic_decoration.js: Definition of the DynamicDecoration class.
2
+ // Copyright (C) 2010,2014 Gregory Dyke (gregdyke at gmail dot com) and Paul Rosen
3
+ //
4
+ // This program is free software: you can redistribute it and/or modify
5
+ // it under the terms of the GNU General Public License as published by
6
+ // the Free Software Foundation, either version 3 of the License, or
7
+ // (at your option) any later version.
8
+ //
9
+ // This program is distributed in the hope that it will be useful,
10
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ // GNU General Public License for more details.
13
+ //
14
+ // You should have received a copy of the GNU General Public License
15
+ // along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ /*globals ABCJS */
18
+
19
+ if (!window.ABCJS)
20
+ window.ABCJS = {};
21
+
22
+ if (!window.ABCJS.write)
23
+ window.ABCJS.write = {};
24
+
25
+ ABCJS.write.DynamicDecoration = function(anchor, dec) {
26
+ this.anchor = anchor;
27
+ this.dec = dec;
28
+ };
29
+
30
+ ABCJS.write.DynamicDecoration.prototype.draw = function(renderer, linestartx, lineendx) {
31
+ var ypos = renderer.layouter.minY - 7;
32
+ var scalex = 1; // TODO-PER: do the scaling
33
+ var scaley = 1;
34
+ renderer.printSymbol(this.anchor.x, ypos, this.dec, scalex, scaley, renderer.addClasses('decoration'));
35
+ };
36
+
@@ -0,0 +1,53 @@
1
+ // abc_ending_element.js: Definition of the EndingElement class.
2
+ // Copyright (C) 2010,2014 Gregory Dyke (gregdyke at gmail dot com) and Paul Rosen
3
+ //
4
+ // This program is free software: you can redistribute it and/or modify
5
+ // it under the terms of the GNU General Public License as published by
6
+ // the Free Software Foundation, either version 3 of the License, or
7
+ // (at your option) any later version.
8
+ //
9
+ // This program is distributed in the hope that it will be useful,
10
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ // GNU General Public License for more details.
13
+ //
14
+ // You should have received a copy of the GNU General Public License
15
+ // along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ /*globals ABCJS */
18
+
19
+ if (!window.ABCJS)
20
+ window.ABCJS = {};
21
+
22
+ if (!window.ABCJS.write)
23
+ window.ABCJS.write = {};
24
+
25
+ ABCJS.write.EndingElem = function(text, anchor1, anchor2) {
26
+ this.text = text; // text to be displayed top left
27
+ this.anchor1 = anchor1; // must have a .x property or be null (means starts at the "beginning" of the line - after keysig)
28
+ this.anchor2 = anchor2; // must have a .x property or be null (means ends at the end of the line)
29
+ };
30
+
31
+ ABCJS.write.EndingElem.prototype.draw = function (renderer, linestartx, lineendx) {
32
+ var pathString;
33
+ if (this.anchor1) {
34
+ linestartx = this.anchor1.x+this.anchor1.w;
35
+ pathString = ABCJS.write.sprintf("M %f %f L %f %f",
36
+ linestartx, renderer.y, linestartx, renderer.y+10);
37
+ renderer.printPath({path:pathString, stroke:"#000000", fill:"#000000", 'class': renderer.addClasses('ending')}); //TODO scale
38
+ renderer.printText(linestartx+5*renderer.scale, 18.5, this.text, "start", 'ending').attr({"font-size":""+10*renderer.scale+"px"});
39
+ }
40
+
41
+ if (this.anchor2) {
42
+ lineendx = this.anchor2.x;
43
+ pathString = ABCJS.write.sprintf("M %f %f L %f %f",
44
+ lineendx, renderer.y, lineendx, renderer.y+10);
45
+ renderer.printPath({path:pathString, stroke:"#000000", fill:"#000000", 'class': renderer.addClasses('ending')}); // TODO scale
46
+ }
47
+
48
+
49
+ pathString = ABCJS.write.sprintf("M %f %f L %f %f",
50
+ linestartx, renderer.y, lineendx, renderer.y);
51
+ renderer.printPath({path:pathString, stroke:"#000000", fill:"#000000", 'class': renderer.addClasses('ending')}); // TODO scale
52
+ };
53
+