File size: 6,345 Bytes
af6912c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
var tunebook = require('./abc_tunebook');
var Tune = require('../data/abc_tune');

var EngraverController = require('../write/engraver-controller');
var Parse = require('../parse/abc_parse');
var wrap = require('../parse/wrap_lines');


var resizeDivs = {};
function resizeOuter() {
    var width = window.innerWidth;
    for (var id in resizeDivs) {
        if (resizeDivs.hasOwnProperty(id)) {
            var outer = resizeDivs[id];
            var ofs = outer.offsetLeft;
            width -= ofs * 2;
            outer.style.width = width + "px";
        }
    }
}

try {
    window.addEventListener("resize", resizeOuter);
    window.addEventListener("orientationChange", resizeOuter);
} catch(e) {
    // if we aren't in a browser, this code will crash, but it is not needed then either.
}

function renderOne(div, tune, params, tuneNumber, lineOffset) {
    if (params.viewportHorizontal) {
        // Create an inner div that holds the music, so that the passed in div will be the viewport.
        div.innerHTML = '<div class="abcjs-inner"></div>';
        if (params.scrollHorizontal) {
            div.style.overflowX = "auto";
            div.style.overflowY = "hidden";
        } else
            div.style.overflow = "hidden";
        resizeDivs[div.id] = div; // We use a hash on the element's id so that multiple calls won't keep adding to the list.
        div = div.children[0]; // The music should be rendered in the inner div.
    }
    else if (params.viewportVertical) {
        // Create an inner div that holds the music, so that the passed in div will be the viewport.
        div.innerHTML = '<div class="abcjs-inner scroll-amount"></div>';
        div.style.overflowX = "hidden";
        div.style.overflowY = "auto";
        div = div.children[0]; // The music should be rendered in the inner div.
    }
    else
	    div.innerHTML = "";
    var engraver_controller = new EngraverController(div, params);
    engraver_controller.engraveABC(tune, tuneNumber, lineOffset);
    tune.engraver = engraver_controller;
    if (params.viewportVertical || params.viewportHorizontal) {
        // If we added a wrapper around the div, then we need to size the wrapper, too.
        var parent = div.parentNode;
        parent.style.width = div.style.width;
    }
}

// A quick way to render a tune from javascript when interactivity is not required.
// This is used when a javascript routine has some abc text that it wants to render
// in a div or collection of divs. One tune or many can be rendered.
//
// parameters:
//      output: an array of divs that the individual tunes are rendered to.
//          If the number of tunes exceeds the number of divs in the array, then
//          only the first tunes are rendered. If the number of divs exceeds the number
//          of tunes, then the unused divs are cleared. The divs can be passed as either
//          elements or strings of ids. If ids are passed, then the div MUST exist already.
//          (if a single element is passed, then it is an implied array of length one.)
//          (if a null is passed for an element, or the element doesn't exist, then that tune is skipped.)
//      abc: text representing a tune or an entire tune book in ABC notation.
//      renderParams: hash of:
//          startingTune: an index, starting at zero, representing which tune to start rendering at.
//              (If this element is not present, then rendering starts at zero.)
//          width: 800 by default. The width in pixels of the output paper
var renderAbc = function(output, abc, parserParams, engraverParams, renderParams) {
    // Note: all parameters have been condensed into the first ones. It doesn't hurt anything to allow the old format, so just copy them here.
    var params = {};
    var key;
    if (parserParams) {
        for (key in parserParams) {
            if (parserParams.hasOwnProperty(key)) {
                params[key] = parserParams[key];
            }
        }
        if (params.warnings_id && params.tablature) {
            params.tablature.warning_id = params.warnings_id;
        }
    }
    if (engraverParams) {
        for (key in engraverParams) {
            if (engraverParams.hasOwnProperty(key)) {
	            // There is a conflict with the name of the parameter "listener". If it is in the second parameter, then it is for click.
	            if (key === "listener") {
	            	if (engraverParams[key].highlight)
		                params.clickListener = engraverParams[key].highlight;
	            } else
                    params[key] = engraverParams[key];
            }
        }
    }
    if (renderParams) {
        for (key in renderParams) {
            if (renderParams.hasOwnProperty(key)) {
                params[key] = renderParams[key];
            }
        }
    }

    function callback(div, tune, tuneNumber, abcString) {
        var removeDiv = false;
        if (div === "*") {
            removeDiv = true;
            div = document.createElement("div");
            div.setAttribute("style", "visibility: hidden;");
            document.body.appendChild(div);
        }
        if (!removeDiv && params.wrap && params.staffwidth) {
            tune = doLineWrapping(div, tune, tuneNumber, abcString, params);
	        return tune;
        }
        if (params.afterParsing)
            params.afterParsing(tune, tuneNumber, abcString);
        renderOne(div, tune, params, tuneNumber, 0);
        if (removeDiv)
            div.parentNode.removeChild(div);
        return null;
    }

    return tunebook.renderEngine(callback, output, abc, params);
};

function doLineWrapping(div, tune, tuneNumber, abcString, params) {
	var engraver_controller = new EngraverController(div, params);
	var widths = engraver_controller.getMeasureWidths(tune);

	var ret = wrap.calcLineWraps(tune, widths, params);
	if (ret.reParse) {
        var abcParser = new Parse();
        abcParser.parse(abcString, ret.revisedParams);
        tune = abcParser.getTune();
        var warnings = abcParser.getWarnings();
        if (warnings)
            tune.warnings = warnings;
    }
    if (params.afterParsing)
        params.afterParsing(tune, tuneNumber, abcString);
    renderOne(div, tune, ret.revisedParams, tuneNumber, 0);
	tune.explanation = ret.explanation;
	return tune;
}

module.exports = renderAbc;