abcjs-rails 1.8 → 1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/app/assets/javascripts/abcjs/api/abc_animation.js +224 -0
- data/app/assets/javascripts/abcjs/api/abc_tunebook.js +158 -154
- data/app/assets/javascripts/abcjs/data/abc_tune.js +35 -1
- data/app/assets/javascripts/abcjs/edit/abc_editor.js +18 -17
- data/app/assets/javascripts/abcjs/parse/abc_parse.js +23 -6
- data/app/assets/javascripts/abcjs/parse/abc_parse_header.js +5 -1
- data/app/assets/javascripts/abcjs/{write/raphael.js → raphael.js} +2562 -266
- data/app/assets/javascripts/abcjs/write/abc_absolute_element.js +163 -0
- data/app/assets/javascripts/abcjs/write/abc_beam_element.js +162 -0
- data/app/assets/javascripts/abcjs/write/abc_cresendo_element.js +46 -0
- data/app/assets/javascripts/abcjs/write/abc_dynamic_decoration.js +36 -0
- data/app/assets/javascripts/abcjs/write/abc_ending_element.js +53 -0
- data/app/assets/javascripts/abcjs/write/abc_glyphs.js +6 -3
- data/app/assets/javascripts/abcjs/write/abc_layout.js +84 -29
- data/app/assets/javascripts/abcjs/write/abc_relative_element.js +72 -0
- data/app/assets/javascripts/abcjs/write/abc_staff_group_element.js +225 -0
- data/app/assets/javascripts/abcjs/write/abc_tie_element.js +83 -0
- data/app/assets/javascripts/abcjs/write/abc_triplet_element.js +85 -0
- data/app/assets/javascripts/abcjs/write/abc_voice_element.js +177 -0
- data/app/assets/javascripts/abcjs/write/abc_write.js +65 -28
- data/lib/abcjs-rails/version.rb +1 -1
- metadata +24 -14
- data/app/assets/javascripts/abcjs/write/abc_graphelements.js +0 -790
@@ -0,0 +1,83 @@
|
|
1
|
+
// abc_tie_element.js: Definition of the TieElement 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.TieElem = function(anchor1, anchor2, above, forceandshift) {
|
26
|
+
this.anchor1 = anchor1; // must have a .x and a .pitch, 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 and a .pitch property or be null (means ends at the end of the line)
|
28
|
+
this.above = above; // true if the arc curves above
|
29
|
+
this.force = forceandshift; // force the arc curve, regardless of beaming if true
|
30
|
+
// move by +7 "up" by -7 if "down"
|
31
|
+
};
|
32
|
+
|
33
|
+
ABCJS.write.TieElem.prototype.draw = function (renderer, linestartx, lineendx) {
|
34
|
+
var startpitch;
|
35
|
+
var endpitch;
|
36
|
+
|
37
|
+
if (this.startlimitelem) {
|
38
|
+
linestartx = this.startlimitelem.x+this.startlimitelem.w;
|
39
|
+
}
|
40
|
+
|
41
|
+
if (this.endlimitelem) {
|
42
|
+
lineendx = this.endlimitelem.x;
|
43
|
+
}
|
44
|
+
// PER: We might have to override the natural slur direction if the first and last notes are not in the
|
45
|
+
// save direction. We always put the slur up in this case. The one case that works out wrong is that we always
|
46
|
+
// want the slur to be up when the last note is stem down. We can tell the stem direction if the top is
|
47
|
+
// equal to the pitch: if so, there is no stem above it.
|
48
|
+
if (!this.force && this.anchor2 && this.anchor2.pitch === this.anchor2.top)
|
49
|
+
this.above = true;
|
50
|
+
|
51
|
+
if (this.anchor1) {
|
52
|
+
linestartx = this.anchor1.x;
|
53
|
+
startpitch = this.above ? this.anchor1.highestVert : this.anchor1.pitch;
|
54
|
+
if (!this.anchor2) {
|
55
|
+
endpitch = this.above ? this.anchor1.highestVert : this.anchor1.pitch;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
if (this.anchor2) {
|
60
|
+
lineendx = this.anchor2.x;
|
61
|
+
endpitch = this.above ? this.anchor2.highestVert : this.anchor2.pitch;
|
62
|
+
if (!this.anchor1) {
|
63
|
+
startpitch = this.above ? this.anchor2.highestVert : this.anchor2.pitch;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
// if (this.anchor1 && this.anchor2) {
|
68
|
+
// if ((!this.force && this.anchor1.parent.beam && this.anchor2.parent.beam &&
|
69
|
+
// this.anchor1.parent.beam.asc===this.anchor2.parent.beam.asc) ||
|
70
|
+
// ((this.force==="up") || this.force==="down") && this.anchor1.parent.beam && this.anchor2.parent.beam && this.anchor1.parent.beam===this.anchor2.parent.beam) {
|
71
|
+
// this.above = !this.anchor1.parent.beam.asc;
|
72
|
+
// preservebeamdir = true;
|
73
|
+
// }
|
74
|
+
// }
|
75
|
+
|
76
|
+
// var pitchshift = 0;
|
77
|
+
// if (this.force==="up" && !preservebeamdir) pitchshift = 7;
|
78
|
+
// if (this.force==="down" && !preservebeamdir) pitchshift = -7;
|
79
|
+
|
80
|
+
// renderer.debugMsgLow(linestartx, debugMsg);
|
81
|
+
renderer.drawArc(linestartx, lineendx, startpitch, endpitch, this.above);
|
82
|
+
|
83
|
+
};
|
@@ -0,0 +1,85 @@
|
|
1
|
+
// abc_triplet_element.js: Definition of the TripletElem 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.TripletElem = function(number, anchor1, anchor2, above) {
|
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.above = above;
|
29
|
+
this.number = number;
|
30
|
+
};
|
31
|
+
|
32
|
+
ABCJS.write.TripletElem.prototype.draw = function (renderer, linestartx, lineendx) {
|
33
|
+
// TODO end and beginning of line
|
34
|
+
if (this.anchor1 && this.anchor2) {
|
35
|
+
var ypos = this.above?16:-1; // PER: Just bumped this up from 14 to make (3z2B2B2 (3B2B2z2 succeed. There's probably a better way.
|
36
|
+
|
37
|
+
if (this.anchor1.parent.beam &&
|
38
|
+
this.anchor1.parent.beam===this.anchor2.parent.beam) {
|
39
|
+
var beam = this.anchor1.parent.beam;
|
40
|
+
this.above = beam.asc;
|
41
|
+
ypos = beam.pos;
|
42
|
+
} else {
|
43
|
+
this.drawLine(renderer,renderer.calcY(ypos));
|
44
|
+
}
|
45
|
+
var xsum = this.anchor1.x+this.anchor2.x;
|
46
|
+
var ydelta = 0;
|
47
|
+
if (beam) {
|
48
|
+
if (this.above) {
|
49
|
+
xsum += (this.anchor2.w + this.anchor1.w);
|
50
|
+
ydelta = 4;
|
51
|
+
} else {
|
52
|
+
ydelta = -4;
|
53
|
+
}
|
54
|
+
} else {
|
55
|
+
xsum += this.anchor2.w;
|
56
|
+
}
|
57
|
+
|
58
|
+
|
59
|
+
renderer.printText(xsum/2, ypos+ydelta, this.number, "middle", 'triplet').attr({"font-size":"10px", 'font-style': 'italic' });
|
60
|
+
|
61
|
+
}
|
62
|
+
};
|
63
|
+
|
64
|
+
ABCJS.write.TripletElem.prototype.drawLine = function (renderer, y) {
|
65
|
+
var pathString;
|
66
|
+
var linestartx = this.anchor1.x;
|
67
|
+
pathString = ABCJS.write.sprintf("M %f %f L %f %f",
|
68
|
+
linestartx, y, linestartx, y+5);
|
69
|
+
renderer.printPath({path:pathString, stroke:"#000000", 'class': renderer.addClasses('triplet')});
|
70
|
+
|
71
|
+
var lineendx = this.anchor2.x+this.anchor2.w;
|
72
|
+
pathString = ABCJS.write.sprintf("M %f %f L %f %f",
|
73
|
+
lineendx, y, lineendx, y+5);
|
74
|
+
renderer.printPath({path:pathString, stroke:"#000000", 'class': renderer.addClasses('triplet')});
|
75
|
+
|
76
|
+
pathString = ABCJS.write.sprintf("M %f %f L %f %f",
|
77
|
+
linestartx, y, (linestartx+lineendx)/2-5, y);
|
78
|
+
renderer.printPath({path:pathString, stroke:"#000000", 'class': renderer.addClasses('triplet')});
|
79
|
+
|
80
|
+
|
81
|
+
pathString = ABCJS.write.sprintf("M %f %f L %f %f",
|
82
|
+
(linestartx+lineendx)/2+5, y, lineendx, y);
|
83
|
+
renderer.printPath({path:pathString, stroke:"#000000", 'class': renderer.addClasses('triplet')});
|
84
|
+
|
85
|
+
};
|
@@ -0,0 +1,177 @@
|
|
1
|
+
// abc_voice_element.js: Definition of the VoiceElement 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.VoiceElement = function(voicenumber, voicetotal) {
|
26
|
+
this.children = [];
|
27
|
+
this.beams = [];
|
28
|
+
this.otherchildren = []; // ties, slurs, triplets
|
29
|
+
this.w = 0;
|
30
|
+
this.duplicate = false;
|
31
|
+
this.voicenumber = voicenumber; //number of the voice on a given stave (not staffgroup)
|
32
|
+
this.voicetotal = voicetotal;
|
33
|
+
};
|
34
|
+
|
35
|
+
ABCJS.write.VoiceElement.prototype.addChild = function (child) {
|
36
|
+
if (child.type === 'bar') {
|
37
|
+
var firstItem = true;
|
38
|
+
for (var i = 0; firstItem && i < this.children.length; i++) {
|
39
|
+
if (this.children[i].type !== "staff-extra")
|
40
|
+
firstItem = false;
|
41
|
+
}
|
42
|
+
if (!firstItem) {
|
43
|
+
this.beams.push("bar");
|
44
|
+
this.otherchildren.push("bar");
|
45
|
+
}
|
46
|
+
}
|
47
|
+
this.children[this.children.length] = child;
|
48
|
+
};
|
49
|
+
|
50
|
+
ABCJS.write.VoiceElement.prototype.addOther = function (child) {
|
51
|
+
if (child instanceof ABCJS.write.BeamElem) {
|
52
|
+
this.beams.push(child);
|
53
|
+
} else {
|
54
|
+
this.otherchildren.push(child);
|
55
|
+
}
|
56
|
+
};
|
57
|
+
|
58
|
+
ABCJS.write.VoiceElement.prototype.updateIndices = function () {
|
59
|
+
if (!this.layoutEnded()) {
|
60
|
+
this.durationindex += this.children[this.i].duration;
|
61
|
+
if (this.children[this.i].duration===0) this.durationindex = Math.round(this.durationindex*64)/64; // everytime we meet a barline, do rounding to nearest 64th
|
62
|
+
this.i++;
|
63
|
+
}
|
64
|
+
};
|
65
|
+
|
66
|
+
ABCJS.write.VoiceElement.prototype.layoutEnded = function () {
|
67
|
+
return (this.i>=this.children.length);
|
68
|
+
};
|
69
|
+
|
70
|
+
ABCJS.write.VoiceElement.prototype.getDurationIndex = function () {
|
71
|
+
return this.durationindex - (this.children[this.i] && (this.children[this.i].duration>0)?0:0.0000005); // if the ith element doesn't have a duration (is not a note), its duration index is fractionally before. This enables CLEF KEYSIG TIMESIG PART, etc. to be laid out before we get to the first note of other voices
|
72
|
+
};
|
73
|
+
|
74
|
+
// number of spacing units expected for next positioning
|
75
|
+
ABCJS.write.VoiceElement.prototype.getSpacingUnits = function () {
|
76
|
+
return (this.minx<this.nextx) ? Math.sqrt(this.spacingduration*8) : 0; // we haven't used any spacing units if we end up using minx
|
77
|
+
};
|
78
|
+
|
79
|
+
//
|
80
|
+
ABCJS.write.VoiceElement.prototype.getNextX = function () {
|
81
|
+
return Math.max(this.minx, this.nextx);
|
82
|
+
};
|
83
|
+
|
84
|
+
ABCJS.write.VoiceElement.prototype.beginLayout = function (startx) {
|
85
|
+
this.i=0;
|
86
|
+
this.durationindex=0;
|
87
|
+
this.ii=this.children.length;
|
88
|
+
this.startx=startx;
|
89
|
+
this.minx=startx; // furthest left to where negatively positioned elements are allowed to go
|
90
|
+
this.nextx=startx; // x position where the next element of this voice should be placed assuming no other voices and no fixed width constraints
|
91
|
+
this.spacingduration=0; // duration left to be laid out in current iteration (omitting additional spacing due to other aspects, such as bars, dots, sharps and flats)
|
92
|
+
};
|
93
|
+
|
94
|
+
// Try to layout the element at index this.i
|
95
|
+
// x - position to try to layout the element at
|
96
|
+
// spacing - base spacing
|
97
|
+
// can't call this function more than once per iteration
|
98
|
+
ABCJS.write.VoiceElement.prototype.layoutOneItem = function (x, spacing) {
|
99
|
+
var child = this.children[this.i];
|
100
|
+
if (!child) return 0;
|
101
|
+
var er = x - this.minx; // available extrawidth to the left
|
102
|
+
if (er<child.getExtraWidth()) { // shift right by needed amount
|
103
|
+
x+=child.getExtraWidth()-er;
|
104
|
+
}
|
105
|
+
child.x=x; // place child at x
|
106
|
+
|
107
|
+
this.spacingduration = child.duration;
|
108
|
+
//update minx
|
109
|
+
this.minx = x+child.getMinWidth(); // add necessary layout space
|
110
|
+
if (this.i!==this.ii-1) this.minx+=child.minspacing; // add minimumspacing except on last elem
|
111
|
+
|
112
|
+
this.updateNextX(x, spacing);
|
113
|
+
|
114
|
+
// contribute to staff y position
|
115
|
+
this.staff.highest = Math.max(child.top,this.staff.highest);
|
116
|
+
this.staff.lowest = Math.min(child.bottom,this.staff.lowest);
|
117
|
+
|
118
|
+
return x; // where we end up having placed the child
|
119
|
+
};
|
120
|
+
|
121
|
+
// call when spacingduration has been updated
|
122
|
+
ABCJS.write.VoiceElement.prototype.updateNextX = function (x, spacing) {
|
123
|
+
this.nextx= x + (spacing*Math.sqrt(this.spacingduration*8));
|
124
|
+
};
|
125
|
+
|
126
|
+
ABCJS.write.VoiceElement.prototype.shiftRight = function (dx) {
|
127
|
+
var child = this.children[this.i];
|
128
|
+
if (!child) return;
|
129
|
+
child.x+=dx;
|
130
|
+
this.minx+=dx;
|
131
|
+
this.nextx+=dx;
|
132
|
+
};
|
133
|
+
|
134
|
+
ABCJS.write.VoiceElement.prototype.draw = function (renderer, bartop) {
|
135
|
+
var width = this.w-1;
|
136
|
+
renderer.y = this.staff.y;
|
137
|
+
renderer.staffbottom = this.staff.bottom;
|
138
|
+
this.barbottom = renderer.calcY(2);
|
139
|
+
|
140
|
+
renderer.measureNumber = null;
|
141
|
+
if (this.header) { // print voice name
|
142
|
+
var textpitch = 12 - (this.voicenumber+1)*(12/(this.voicetotal+1));
|
143
|
+
var headerX = (this.startx-renderer.paddingleft)/2+renderer.paddingleft;
|
144
|
+
headerX = headerX*renderer.scale;
|
145
|
+
renderer.paper.text(headerX, renderer.calcY(textpitch)*renderer.scale, this.header).attr({"font-size":12*renderer.scale, "font-family":"serif", 'font-weight':'bold', 'class': renderer.addClasses('staff-extra voice-name')}); // code duplicated above
|
146
|
+
}
|
147
|
+
|
148
|
+
for (var i=0, ii=this.children.length; i<ii; i++) {
|
149
|
+
var child = this.children[i];
|
150
|
+
var justInitializedMeasureNumber = false;
|
151
|
+
if (child.type !== 'staff-extra' && renderer.measureNumber === null) {
|
152
|
+
renderer.measureNumber = 0;
|
153
|
+
justInitializedMeasureNumber = true;
|
154
|
+
}
|
155
|
+
child.draw(renderer, (this.barto || i===ii-1)?bartop:0);
|
156
|
+
if (child.type === 'bar' && !justInitializedMeasureNumber)
|
157
|
+
renderer.measureNumber++;
|
158
|
+
}
|
159
|
+
|
160
|
+
renderer.measureNumber = 0;
|
161
|
+
window.ABCJS.parse.each(this.beams, function(beam) {
|
162
|
+
if (beam === 'bar')
|
163
|
+
renderer.measureNumber++;
|
164
|
+
else
|
165
|
+
beam.draw(renderer); // beams must be drawn first for proper printing of triplets, slurs and ties.
|
166
|
+
});
|
167
|
+
|
168
|
+
renderer.measureNumber = 0;
|
169
|
+
var self = this;
|
170
|
+
window.ABCJS.parse.each(this.otherchildren, function(child) {
|
171
|
+
if (child === 'bar')
|
172
|
+
renderer.measureNumber++;
|
173
|
+
else
|
174
|
+
child.draw(renderer,self.startx+10,width);
|
175
|
+
});
|
176
|
+
|
177
|
+
};
|
@@ -15,7 +15,7 @@
|
|
15
15
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
16
|
|
17
17
|
|
18
|
-
/*global window, ABCJS, Math */
|
18
|
+
/*global window, ABCJS, Math, Raphael */
|
19
19
|
|
20
20
|
if (!window.ABCJS)
|
21
21
|
window.ABCJS = {};
|
@@ -50,6 +50,18 @@ ABCJS.write.Printer = function(paper, params) {
|
|
50
50
|
this.paddingright = params.paddingright || 50;
|
51
51
|
this.paddingleft = params.paddingleft || 15;
|
52
52
|
this.editable = params.editable || false;
|
53
|
+
// HACK-PER: Raphael doesn't support setting the class of an element, so this adds that support. This doesn't work on IE8 or less, though.
|
54
|
+
this.usingSvg = (window.SVGAngle || document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? true : false); // Same test Raphael uses
|
55
|
+
if (this.usingSvg && params.add_classes)
|
56
|
+
Raphael._availableAttrs['class'] = "";
|
57
|
+
};
|
58
|
+
|
59
|
+
ABCJS.write.Printer.prototype.addClasses = function (c) {
|
60
|
+
var ret = [];
|
61
|
+
if (c.length > 0) ret.push(c);
|
62
|
+
if (this.lineNumber !== null) ret.push("l"+this.lineNumber);
|
63
|
+
if (this.measureNumber !== null) ret.push("m"+this.measureNumber);
|
64
|
+
return ret.join(' ');
|
53
65
|
};
|
54
66
|
|
55
67
|
// notify all listeners that a graphical element has been selected
|
@@ -125,10 +137,10 @@ ABCJS.write.Printer.prototype.addPath = function (path) {
|
|
125
137
|
}
|
126
138
|
};
|
127
139
|
|
128
|
-
ABCJS.write.Printer.prototype.endGroup = function () {
|
140
|
+
ABCJS.write.Printer.prototype.endGroup = function (klass) {
|
129
141
|
this.ingroup = false;
|
130
142
|
if (this.path.length===0) return null;
|
131
|
-
var ret = this.paper.path().attr({path:this.path, stroke:"none", fill:"#000000"});
|
143
|
+
var ret = this.paper.path().attr({path:this.path, stroke:"none", fill:"#000000", 'class': this.addClasses(klass)});
|
132
144
|
if (this.scale!==1) {
|
133
145
|
ret.scale(this.scale, this.scale, 0, 0);
|
134
146
|
}
|
@@ -146,7 +158,7 @@ ABCJS.write.Printer.prototype.printStaveLine = function (x1,x2, pitch) {
|
|
146
158
|
var y = this.calcY(pitch);
|
147
159
|
var pathString = ABCJS.write.sprintf("M %f %f L %f %f L %f %f L %f %f z", x1, y-dy, x2, y-dy,
|
148
160
|
x2, y+dy, x1, y+dy);
|
149
|
-
var ret = this.paper.path().attr({path:pathString, stroke:"none", fill:fill}).toBack();
|
161
|
+
var ret = this.paper.path().attr({path:pathString, stroke:"none", fill:fill, 'class': this.addClasses('staff')}).toBack();
|
150
162
|
if (this.scale!==1) {
|
151
163
|
ret.scale(this.scale, this.scale, 0, 0);
|
152
164
|
}
|
@@ -170,7 +182,7 @@ ABCJS.write.Printer.prototype.printStem = function (x, dx, y1, y2) {
|
|
170
182
|
if (!isIE && this.ingroup) {
|
171
183
|
this.addPath(pathArray);
|
172
184
|
} else {
|
173
|
-
var ret = this.paper.path().attr({path:pathArray, stroke:"none", fill:fill}).toBack();
|
185
|
+
var ret = this.paper.path().attr({path:pathArray, stroke:"none", fill:fill, 'class': this.addClasses('stem')}).toBack();
|
174
186
|
if (this.scale!==1) {
|
175
187
|
ret.scale(this.scale, this.scale, 0, 0);
|
176
188
|
}
|
@@ -178,9 +190,9 @@ ABCJS.write.Printer.prototype.printStem = function (x, dx, y1, y2) {
|
|
178
190
|
}
|
179
191
|
};
|
180
192
|
|
181
|
-
ABCJS.write.Printer.prototype.printText = function (x, offset, text, anchor) {
|
193
|
+
ABCJS.write.Printer.prototype.printText = function (x, offset, text, anchor, extraClass) {
|
182
194
|
anchor = anchor || "start";
|
183
|
-
var ret = this.paper.text(x*this.scale, this.calcY(offset)*this.scale, text).attr({"text-anchor":anchor, "font-size":12*this.scale});
|
195
|
+
var ret = this.paper.text(x*this.scale, this.calcY(offset)*this.scale, text).attr({"text-anchor":anchor, "font-size":12*this.scale, 'class': this.addClasses(extraClass)});
|
184
196
|
// if (this.scale!==1) {
|
185
197
|
// ret.scale(this.scale, this.scale, 0, 0);
|
186
198
|
// }
|
@@ -190,7 +202,7 @@ ABCJS.write.Printer.prototype.printText = function (x, offset, text, anchor) {
|
|
190
202
|
// assumes this.y is set appropriately
|
191
203
|
// if symbol is a multichar string without a . (as in scripts.staccato) 1 symbol per char is assumed
|
192
204
|
// not scaled if not in printgroup
|
193
|
-
ABCJS.write.Printer.prototype.printSymbol = function(x, offset, symbol, scalex, scaley) {
|
205
|
+
ABCJS.write.Printer.prototype.printSymbol = function(x, offset, symbol, scalex, scaley, klass) {
|
194
206
|
var el;
|
195
207
|
if (!symbol) return null;
|
196
208
|
if (symbol.length>0 && symbol.indexOf(".")<0) {
|
@@ -198,7 +210,7 @@ ABCJS.write.Printer.prototype.printSymbol = function(x, offset, symbol, scalex,
|
|
198
210
|
var dx =0;
|
199
211
|
for (var i=0; i<symbol.length; i++) {
|
200
212
|
var ycorr = this.glyphs.getYCorr(symbol.charAt(i));
|
201
|
-
el = this.glyphs.printSymbol(x+dx, this.calcY(offset+ycorr), symbol.charAt(i), this.paper);
|
213
|
+
el = this.glyphs.printSymbol(x+dx, this.calcY(offset+ycorr), symbol.charAt(i), this.paper, klass);
|
202
214
|
if (el) {
|
203
215
|
elemset.push(el);
|
204
216
|
dx+=this.glyphs.getSymbolWidth(symbol.charAt(i));
|
@@ -215,7 +227,7 @@ ABCJS.write.Printer.prototype.printSymbol = function(x, offset, symbol, scalex,
|
|
215
227
|
if (this.ingroup) {
|
216
228
|
this.addPath(this.glyphs.getPathForSymbol(x, this.calcY(offset+ycorr), symbol, scalex, scaley));
|
217
229
|
} else {
|
218
|
-
el = this.glyphs.printSymbol(x, this.calcY(offset+ycorr), symbol, this.paper);
|
230
|
+
el = this.glyphs.printSymbol(x, this.calcY(offset+ycorr), symbol, this.paper, klass);
|
219
231
|
if (el) {
|
220
232
|
if (this.scale!==1) {
|
221
233
|
el.scale(this.scale, this.scale, 0, 0);
|
@@ -262,7 +274,7 @@ ABCJS.write.Printer.prototype.drawArc = function(x1, x2, pitch1, pitch2, above)
|
|
262
274
|
var pathString = ABCJS.write.sprintf("M %f %f C %f %f %f %f %f %f C %f %f %f %f %f %f z", x1, y1,
|
263
275
|
controlx1, controly1, controlx2, controly2, x2, y2,
|
264
276
|
controlx2-thickness*uy, controly2+thickness*ux, controlx1-thickness*uy, controly1+thickness*ux, x1, y1);
|
265
|
-
var ret = this.paper.path().attr({path:pathString, stroke:"none", fill:"#000000"});
|
277
|
+
var ret = this.paper.path().attr({path:pathString, stroke:"none", fill:"#000000", 'class': this.addClasses('slur')});
|
266
278
|
if (this.scale!==1) {
|
267
279
|
ret.scale(this.scale, this.scale, 0, 0);
|
268
280
|
}
|
@@ -270,15 +282,15 @@ ABCJS.write.Printer.prototype.drawArc = function(x1, x2, pitch1, pitch2, above)
|
|
270
282
|
};
|
271
283
|
|
272
284
|
ABCJS.write.Printer.prototype.debugMsg = function(x, msg) {
|
273
|
-
return this.paper.text(x, this.y, msg).scale(this.scale, this.scale, 0, 0);
|
285
|
+
return this.paper.text(x, this.y, msg).scale(this.scale, this.scale, 0, 0).attr({'class': this.addClasses('debug-msg')});
|
274
286
|
};
|
275
287
|
|
276
288
|
ABCJS.write.Printer.prototype.debugMsgLow = function(x, msg) {
|
277
|
-
return this.paper.text(x, this.calcY(this.layouter.minY-7), msg).attr({"font-family":"serif", "font-size":12, "text-anchor":"begin"}).scale(this.scale, this.scale, 0, 0);
|
289
|
+
return this.paper.text(x, this.calcY(this.layouter.minY-7), msg).attr({"font-family":"serif", "font-size":12, "text-anchor":"begin", 'class': this.addClasses('debug-msg')}).scale(this.scale, this.scale, 0, 0);
|
278
290
|
};
|
279
291
|
|
280
292
|
ABCJS.write.Printer.prototype.printLyrics = function(x, msg) {
|
281
|
-
var el = this.paper.text(x, this.calcY(this.layouter.minY-7), msg).attr({"font-family":"Times New Roman", "font-weight":'bold', "font-size":14, "text-anchor":"begin"}).scale(this.scale, this.scale, 0, 0);
|
293
|
+
var el = this.paper.text(x, this.calcY(this.layouter.minY-7), msg).attr({"font-family":"Times New Roman", "font-weight":'bold', "font-size":14, "text-anchor":"begin", 'class': this.addClasses('lyrics')}).scale(this.scale, this.scale, 0, 0);
|
282
294
|
el[0].setAttribute("class", "abc-lyric");
|
283
295
|
return el;
|
284
296
|
};
|
@@ -316,7 +328,7 @@ ABCJS.write.Printer.prototype.printABC = function(abctunes) {
|
|
316
328
|
};
|
317
329
|
|
318
330
|
ABCJS.write.Printer.prototype.printTempo = function (tempo, paper, layouter, y, printer, x) {
|
319
|
-
var fontStyle = {"text-anchor":"start", 'font-size':12*printer.scale, 'font-weight':'bold'};
|
331
|
+
var fontStyle = {"text-anchor":"start", 'font-size':12*printer.scale, 'font-weight':'bold', 'class': this.addClasses('tempo')};
|
320
332
|
if (tempo.preString) {
|
321
333
|
var text = paper.text(x*printer.scale, y*printer.scale + 20*printer.scale, tempo.preString).attr(fontStyle);
|
322
334
|
x += (text.getBBox().width + 20*printer.scale);
|
@@ -325,7 +337,7 @@ ABCJS.write.Printer.prototype.printTempo = function (tempo, paper, layouter, y,
|
|
325
337
|
var temposcale = 0.75*printer.scale;
|
326
338
|
var tempopitch = 14.5;
|
327
339
|
var duration = tempo.duration[0]; // TODO when multiple durations
|
328
|
-
var abselem = new ABCJS.write.AbsoluteElement(tempo, duration, 1);
|
340
|
+
var abselem = new ABCJS.write.AbsoluteElement(tempo, duration, 1, 'tempo');
|
329
341
|
var durlog = Math.floor(Math.log(duration) / Math.log(2));
|
330
342
|
var dot = 0;
|
331
343
|
for (var tot = Math.pow(2, durlog), inc = tot / 2; tot < duration; dot++, tot += inc, inc /= 2);
|
@@ -364,6 +376,8 @@ ABCJS.write.Printer.prototype.printTempo = function (tempo, paper, layouter, y,
|
|
364
376
|
};
|
365
377
|
|
366
378
|
ABCJS.write.Printer.prototype.printTune = function (abctune) {
|
379
|
+
this.lineNumber = null;
|
380
|
+
this.measureNumber = null;
|
367
381
|
this.layouter = new ABCJS.write.Layout(this.glyphs, abctune.formatting.bagpipes);
|
368
382
|
this.layouter.printer = this; // TODO-PER: this is a hack to get access, but it tightens the coupling.
|
369
383
|
if (abctune.media === 'print') {
|
@@ -390,28 +404,29 @@ ABCJS.write.Printer.prototype.printTune = function (abctune) {
|
|
390
404
|
this.width+=this.paddingleft;
|
391
405
|
if (abctune.formatting.scale) { this.scale=abctune.formatting.scale; }
|
392
406
|
if (abctune.metaText.title)
|
393
|
-
this.paper.text(this.width*this.scale/2, this.y, abctune.metaText.title).attr({"font-size":20*this.scale, "font-family":"serif"});
|
407
|
+
this.paper.text(this.width*this.scale/2, this.y, abctune.metaText.title).attr({"font-size":20*this.scale, "font-family":"serif", 'class': this.addClasses('title meta-top')});
|
394
408
|
this.y+=20*this.scale;
|
395
409
|
if (abctune.lines[0] && abctune.lines[0].subtitle) {
|
396
410
|
this.printSubtitleLine(abctune.lines[0]);
|
397
411
|
this.y+=20*this.scale;
|
398
412
|
}
|
399
413
|
if (abctune.metaText.rhythm) {
|
400
|
-
this.paper.text(this.paddingleft, this.y, abctune.metaText.rhythm).attr({"text-anchor":"start","font-style":"italic","font-family":"serif", "font-size":12*this.scale});
|
414
|
+
this.paper.text(this.paddingleft, this.y, abctune.metaText.rhythm).attr({"text-anchor":"start","font-style":"italic","font-family":"serif", "font-size":12*this.scale, 'class': this.addClasses('meta-top')});
|
401
415
|
!(abctune.metaText.author || abctune.metaText.origin || abctune.metaText.composer) && (this.y+=15*this.scale);
|
402
416
|
}
|
403
417
|
var composerLine = "";
|
404
418
|
if (abctune.metaText.composer) composerLine += abctune.metaText.composer;
|
405
419
|
if (abctune.metaText.origin) composerLine += ' (' + abctune.metaText.origin + ')';
|
406
|
-
if (composerLine.length > 0) {this.paper.text(this.width*this.scale, this.y, composerLine).attr({"text-anchor":"end","font-style":"italic","font-family":"serif", "font-size":12*this.scale});this.y+=15;}
|
407
|
-
if (abctune.metaText.author) {this.paper.text(this.width*this.scale, this.y, abctune.metaText.author).attr({"text-anchor":"end","font-style":"italic","font-family":"serif", "font-size":12*this.scale}); this.y+=15;}
|
420
|
+
if (composerLine.length > 0) {this.paper.text(this.width*this.scale, this.y, composerLine).attr({"text-anchor":"end","font-style":"italic","font-family":"serif", "font-size":12*this.scale, 'class': this.addClasses('meta-top')});this.y+=15;}
|
421
|
+
if (abctune.metaText.author) {this.paper.text(this.width*this.scale, this.y, abctune.metaText.author).attr({"text-anchor":"end","font-style":"italic","font-family":"serif", "font-size":12*this.scale, 'class': this.addClasses('meta-top')}); this.y+=15;}
|
408
422
|
if (abctune.metaText.tempo && !abctune.metaText.tempo.suppress) {
|
409
|
-
this.y = this.printTempo(abctune.metaText.tempo, this.paper, this.layouter, this.y, this, 50);
|
423
|
+
this.y = this.printTempo(abctune.metaText.tempo, this.paper, this.layouter, this.y, this, 50, -1);
|
410
424
|
this.y += 20*this.scale;
|
411
425
|
}
|
412
426
|
this.staffgroups = [];
|
413
427
|
var maxwidth = this.width;
|
414
428
|
for(var line=0; line<abctune.lines.length; line++) {
|
429
|
+
this.lineNumber = line;
|
415
430
|
var abcline = abctune.lines[line];
|
416
431
|
if (abcline.staff) {
|
417
432
|
staffgroup = this.printStaffLine(abctune, abcline, line);
|
@@ -421,18 +436,20 @@ ABCJS.write.Printer.prototype.printTune = function (abctune) {
|
|
421
436
|
this.y+=20*this.scale; //hardcoded
|
422
437
|
} else if (abcline.text) {
|
423
438
|
if (typeof abcline.text === 'string')
|
424
|
-
this.paper.text(100, this.y, "TEXT: " + abcline.text);
|
439
|
+
this.paper.text(100, this.y, "TEXT: " + abcline.text).attr({'class': this.addClasses('defined-text')});
|
425
440
|
else {
|
426
441
|
var str = "";
|
427
442
|
for (var i = 0; i < abcline.text.length; i++) {
|
428
443
|
str += " FONT " + abcline.text[i].text;
|
429
444
|
}
|
430
|
-
this.paper.text(100, this.y, "TEXT: " + str);
|
445
|
+
this.paper.text(100, this.y, "TEXT: " + str).attr({'class': this.addClasses('defined-text')});
|
431
446
|
}
|
432
447
|
this.y+=20*this.scale; //hardcoded
|
433
448
|
}
|
434
449
|
}
|
435
|
-
|
450
|
+
this.lineNumber = null;
|
451
|
+
this.measureNumber = null;
|
452
|
+
var extraText = "";
|
436
453
|
var text2;
|
437
454
|
var height;
|
438
455
|
if (abctune.metaText.partOrder) extraText += "Part Order: " + abctune.metaText.partOrder + "\n";
|
@@ -447,7 +464,7 @@ ABCJS.write.Printer.prototype.printTune = function (abctune) {
|
|
447
464
|
extraText += "\n";
|
448
465
|
}
|
449
466
|
}
|
450
|
-
text2 = this.paper.text(this.paddingleft*this.scale+50*this.scale, this.y*this.scale+25*this.scale, extraText).attr({"text-anchor":"start", "font-family":"serif", "font-size":17*this.scale});
|
467
|
+
text2 = this.paper.text(this.paddingleft*this.scale+50*this.scale, this.y*this.scale+25*this.scale, extraText).attr({"text-anchor":"start", "font-family":"serif", "font-size":17*this.scale, 'class': this.addClasses('meta-bottom')});
|
451
468
|
height = text2.getBBox().height + 17*this.scale;
|
452
469
|
text2.translate(0,height/2);
|
453
470
|
this.y+=height;
|
@@ -459,7 +476,7 @@ ABCJS.write.Printer.prototype.printTune = function (abctune) {
|
|
459
476
|
if (abctune.metaText.notes) extraText += "Notes: " + abctune.metaText.notes + "\n";
|
460
477
|
if (abctune.metaText.transcription) extraText += "Transcription: " + abctune.metaText.transcription + "\n";
|
461
478
|
if (abctune.metaText.history) extraText += "History: " + abctune.metaText.history + "\n";
|
462
|
-
text2 = this.paper.text(this.paddingleft, this.y*this.scale+25*this.scale, extraText).attr({"text-anchor":"start", "font-family":"serif", "font-size":17*this.scale});
|
479
|
+
text2 = this.paper.text(this.paddingleft, this.y*this.scale+25*this.scale, extraText).attr({"text-anchor":"start", "font-family":"serif", "font-size":17*this.scale, 'class': this.addClasses('meta-bottom')});
|
463
480
|
height = text2.getBBox().height;
|
464
481
|
if (!height) height = 25*this.scale; // TODO-PER: Hack! Don't know why Raphael chokes on this sometimes and returns NaN. Perhaps only when printing to PDF? Possibly if the SVG is hidden?
|
465
482
|
text2.translate(0,height/2);
|
@@ -476,9 +493,28 @@ ABCJS.write.Printer.prototype.printTune = function (abctune) {
|
|
476
493
|
};
|
477
494
|
|
478
495
|
ABCJS.write.Printer.prototype.printSubtitleLine = function(abcline) {
|
479
|
-
this.paper.text(this.width/2, this.y, abcline.subtitle).attr({"font-size":16}).scale(this.scale, this.scale, 0,0);
|
496
|
+
this.paper.text(this.width/2, this.y, abcline.subtitle).attr({"font-size":16, 'class': 'text meta-top'}).scale(this.scale, this.scale, 0,0);
|
480
497
|
};
|
481
498
|
|
499
|
+
function centerWholeRests(voices) {
|
500
|
+
// whole rests are a special case: if they are by themselves in a measure, then they should be centered.
|
501
|
+
// (If they are not by themselves, that is probably a user error, but we'll just center it between the two items to either side of it.)
|
502
|
+
for (var i = 0; i < voices.length; i++) {
|
503
|
+
var voice = voices[i];
|
504
|
+
// Look through all of the elements except for the first and last. If the whole note appears there then there isn't anything to center it between anyway.
|
505
|
+
for (var j = 1; j < voice.children.length-1; j++) {
|
506
|
+
var absElem = voice.children[j];
|
507
|
+
if (absElem.abcelem.rest && absElem.abcelem.rest.type === 'whole') {
|
508
|
+
var before = voice.children[j-1];
|
509
|
+
var after = voice.children[j+1];
|
510
|
+
var midpoint = (after.x - before.x) / 2 + before.x;
|
511
|
+
absElem.x = midpoint - absElem.w / 2;
|
512
|
+
|
513
|
+
}
|
514
|
+
}
|
515
|
+
}
|
516
|
+
}
|
517
|
+
|
482
518
|
ABCJS.write.Printer.prototype.printStaffLine = function (abctune, abcline, line) {
|
483
519
|
var staffgroup = this.layouter.printABCLine(abcline.staff);
|
484
520
|
var newspace = this.space;
|
@@ -494,9 +530,10 @@ ABCJS.write.Printer.prototype.printStaffLine = function (abctune, abcline, line)
|
|
494
530
|
}
|
495
531
|
}
|
496
532
|
}
|
533
|
+
centerWholeRests(staffgroup.voices);
|
497
534
|
staffgroup.draw(this, this.y);
|
498
535
|
this.staffgroups[this.staffgroups.length] = staffgroup;
|
499
536
|
this.y = staffgroup.y + staffgroup.height;
|
500
537
|
this.y += ABCJS.write.spacing.STAVEHEIGHT * 0.2;
|
501
538
|
return staffgroup;
|
502
|
-
}
|
539
|
+
};
|