freakimkaefig/Music-XML-Analyzer

View on GitHub
public/js/views/NotationView.js

Summary

Maintainability
F
4 days
Test Coverage
/** @constructor */
MusicXMLAnalyzer.NotationView = function(){

    var that = {},
    context = null,
    canvas = null,
    canvasLeft = null,
    canvasTop = null,
    renderer = null,
    stave = null,

    hoveredNote = null,
    hoveredOctave = null,

    paddingTopStaves = 0,
    spaceBetweenLines = 0,

    topValsNoteElements = null,

    VEXFLOW_REST_SIGN = "r",

    /**
     * Init function
     * @function
     * @public
     */
    init = function() {
        patternController = MusicXMLAnalyzer.PatternController();
        patternModel = MusicXMLAnalyzer.PatternModel();

        initCanvas();
        setTopNoteValues();
        registerListener();
    },

    /**
     * This method inits canvas and context and sets canvas top and left to variable
     * @function
     * @public
     */
    initCanvas = function() {
        canvas = document.getElementById('myCanvas');
        canvasLeft = canvas.offsetLeft;
        canvasTop = canvas.offsetTop;

          renderer = new Vex.Flow.Renderer(canvas,Vex.Flow.Renderer.Backends.CANVAS);

          context = renderer.getContext();
          stave = new Vex.Flow.Stave(10, 45, 700);
          addClef(patternModel.getCurrentMode());
    },

    /**
     * Method clears canvas, creates new stave and adds a new clef on mode change
     * @function
     * @public
     *
     * @param {event}    event    the triggered event
     *
     * @param {int}      mode     the selected mode
     */
    onChangeMode = function(event, mode) {
        context.clearRect(0, 0, canvas.width, canvas.height);
        stave = new Vex.Flow.Stave(10, 45, 700);
          addClef(patternModel.getCurrentMode());
    },

    /**
     * Method adds a clef according to mode
     * @function
     * @public
     *
     * @param {int}    mode    current selected mode
     */
    addClef = function(mode) {
        if (mode === 1) {
              stave.addClef("percussion").setContext(context).draw();
          } else {
              stave.addClef("treble").setContext(context).draw();
          }
    },

    /**
     * Method registers listener to canvas
     * @function
     * @public
     */
    registerListener = function() {
        $("#myCanvas").on("mousemove", onMouseMoveCanvas);
        $("#myCanvas").on("click", onMouseClickCanvas);

        $(patternController).on('changed_mode', onChangeMode);
    },

    /**
     * Method displays note elements on the canvas and get them from model
     * @function
     * @public
     *
     * @param {object}    vexflowNotes    contains notes for vexflow
     */
    renderNotes = function(vexflowNotes) {

        // delete canvas
        context.clearRect(0, 0, canvas.width, canvas.height);

        stave.setContext(context).draw();

        var voice = new Vex.Flow.Voice({
            resolution: Vex.Flow.RESOLUTION
        });


        //easiest way to disable time-checking
        voice.setStrict(false);

        // Add notes to voice
        voice.addTickables(vexflowNotes);

        // Format and justify the notes to 700 pixels
        var formatter = new Vex.Flow.Formatter().
            joinVoices([voice]).format([voice], 700);

        //TRIPLET
        //get beams and tuplets from model
        var beams = patternModel.getTupletArray();
        var tuplets = patternModel.getBeamArray();

        voice.draw(context, stave);

        //TRIPLET
        for(var i=0; i < tuplets.length; i++) {
            tuplets[i].setContext(context).draw();
        }

        for(var i=0; i < beams.length; i++) {
            for(var j=0; j < beams[i].length; j++) {
                beams[i][j].setContext(context).draw();
            }
        }



    },

    /**
     * Method renders preview note on canvas at mouseover
     * @function
     * @public
     */
    renderVexFlowNotePreview = function() {
        // delete canvas
        context.clearRect(0, 0, canvas.width, canvas.height);
        stave.setContext(context).draw();

        // get all vexflow note elements from model which already exist
          var vexflowNotes = patternModel.getAllVexFlowNoteElements();

          //bugprevention: vexflow changes the position of dots
          //for loop sets the position of all available dots to 0
          for (var i=0;  i < vexflowNotes.length; i++) {

              if (typeof vexflowNotes[i].modifiers[0] !== 'undefined') {
                  vexflowNotes[i].modifiers[0].dot_shiftY = 0;
              }
          }

          var previewNote = null;
        var key = hoveredNote;
          var durationContent = patternModel.getDuration4Vexflow(patternModel.getCurrentNoteDuration());
          // to check if break is selected or not
          // when break is selected: only key "b/4"
          var noteName = patternModel.getCurrentNoteName();
          var accidental = patternModel.getCurrentAccidential();
          var rythSpec = patternModel.getCurrentNoteRythSpecial();

          if (accidental === "#" || accidental === "b") {
            key += accidental;
        }
          // add the preview to to notes array
          // further down it's been removed again
          if (noteName === "break") {
            previewNote = new Vex.Flow.StaveNote({ keys: ["b/4"],
                                duration: durationContent + VEXFLOW_REST_SIGN,
                                auto_stem: true });
        } else {
            var keys = key + "/" + hoveredOctave;
            if (patternModel.getCurrentMode() === 1) {
                if (durationContent === "w" || durationContent === "h" || durationContent === "wd" || durationContent === "hd") {
                    keys += "/d0";
                } else {
                    keys += "/d2";
                }
            }
            previewNote = new Vex.Flow.StaveNote({ keys: [keys],
                                duration: durationContent,
                                auto_stem: true });
        }

        if (accidental === "#" || accidental === "b") {
            previewNote.addAccidental(0, new Vex.Flow.Accidental(accidental));
        }

        if (rythSpec === "dotted") {
            previewNote.addDot(0);
        }

        // set color of preview note
        previewNote.color = "#8B8B8B";

          vexflowNotes.push(previewNote);

        var voice = new Vex.Flow.Voice({
            resolution: Vex.Flow.RESOLUTION
        });

        //disable time-checking
        voice.setStrict(false);
        // Add notes to voice
        voice.addTickables(vexflowNotes);

        // Format and justify the notes to 700 pixels
        var formatter = new Vex.Flow.Formatter().
            joinVoices([voice]).format([voice], 700);

        //TRIPLET
        //get beams and tuplets from model
        var beams = patternModel.getTupletArray();
        var tuplets = patternModel.getBeamArray();

        // Render voice
        voice.draw(context, stave);

        //TRIPLET
        for(var i=0; i < tuplets.length; i++) {
            tuplets[i].setContext(context).draw();
        }

        for(var i=0; i < beams.length; i++) {
            for(var j=0; j < beams[i].length; j++) {
                beams[i][j].setContext(context).draw();
            }
        }

        //delete last array entry
        vexflowNotes.pop();

    },
    /**
     * Method calculates the position of the notes
     * @function
     * @public
     */
    setTopNoteValues = function() {
        spaceBetweenLines = (canvas.height/16);
    },

    /**
     * Method handels the mouseover event on canvas
     * @function
     * @public
     *
     * @param     {Event}     The triggered event
     */
    onMouseMoveCanvas = function(event) {

        var x = event.pageX - canvasLeft,
            y = event.pageY - canvasTop;

        //check if cursor is hover a existing note position
        //if yes the method returns val and not null

        //when rhythm mode -> just note b/4 is displayed when hovering
        if (patternModel.getCurrentMode() === 1) {
            hoveredOctave = "4";
            hoveredNote = "b";
            renderVexFlowNotePreview("b");
        }
        else if (checkHorizontalArea(y)) {
            // call method to render note preview with given noteName
            renderVexFlowNotePreview();
        }
    },

    /**
     * Method handels mouseclick event on canvas
     * @function
     * @public
     */
    onMouseClickCanvas = function() {

        var noteName = patternModel.getCurrentNoteName();

        var hoveredArea = null;

        if (noteName === "break") {
            hoveredArea = "break/4";
            patternController.addNoteByCanvasClick(hoveredArea);
        } else {
            hoveredArea = hoveredNote + "/" + hoveredOctave;
            patternController.addNoteByCanvasClick(hoveredArea);
        }

    },

    /**
     * Method checks on which horitiontal position the mousecursor is and saves the corresponding note
     * @function
     * @public
     *
     * @param {int}            y             horizontal position of cursor
     *
     * @return {string}     hoverdnote   hovered note according to calculated cursor position
     */
    checkHorizontalArea = function(y) {

        if (y > spaceBetweenLines * 1.25 && y <= spaceBetweenLines * 1.75) {
            hoveredNote = "b";
            hoveredOctave = "6";
        } else if (y > spaceBetweenLines * 1.75 && y <= spaceBetweenLines * 2.25) {
            hoveredNote = "a";
            hoveredOctave = "6";
        } else if (y > spaceBetweenLines * 2.25 && y <= spaceBetweenLines * 2.75) {
            hoveredNote = "g";
            hoveredOctave = "6";
        } else if (y > spaceBetweenLines * 2.75 && y <= spaceBetweenLines * 3.25) {
            hoveredNote = "f";
            hoveredOctave = "6";
        } else if (y > spaceBetweenLines * 3.25 && y <= spaceBetweenLines * 3.75) {
            hoveredNote = "e";
            hoveredOctave = "6";
        } else if (y > spaceBetweenLines * 3.75 && y <= spaceBetweenLines * 4.25) {
            hoveredNote = "d";
            hoveredOctave = "6";
        } else if (y > spaceBetweenLines * 4.25 && y <= spaceBetweenLines * 4.75) {
            hoveredNote = "c";
            hoveredOctave = "6";
        } else if (y > spaceBetweenLines * 4.75 && y <= spaceBetweenLines * 5.25) {
            hoveredNote = "b";
            hoveredOctave = "5";
        } else if (y > spaceBetweenLines * 5.25 && y <= spaceBetweenLines * 5.75) {
            hoveredNote = "a";
            hoveredOctave = "5";
        } else if (y > spaceBetweenLines * 5.75 && y <= spaceBetweenLines * 6.25) {
            hoveredNote = "g";
            hoveredOctave = "5";
        } else if (y > spaceBetweenLines * 6.25 && y <= spaceBetweenLines * 6.75) {
            hoveredNote = "f";
            hoveredOctave = "5";
        } else if (y > spaceBetweenLines * 6.75 && y <= spaceBetweenLines * 7.25) {
            hoveredNote = "e";
            hoveredOctave = "5";
        } else if (y > spaceBetweenLines * 7.25 && y <= spaceBetweenLines * 7.75) {
            hoveredNote = "d";
            hoveredOctave = "5";
        } else if (y > spaceBetweenLines * 7.75 && y <= spaceBetweenLines * 8.25) {
            hoveredNote = "c";
            hoveredOctave = "5";
        } else if (y > spaceBetweenLines * 8.25 && y <= spaceBetweenLines * 8.75) {
            hoveredNote = "b";
            hoveredOctave = "4";
        } else if (y > spaceBetweenLines * 8.75 && y <= spaceBetweenLines * 9.25) {
            hoveredNote = "a";
            hoveredOctave = "4";
        } else if (y > spaceBetweenLines * 9.25 && y <= spaceBetweenLines * 9.75) {
            hoveredNote = "g";
            hoveredOctave = "4";
        } else if (y > spaceBetweenLines * 9.75 && y <= spaceBetweenLines * 10.25) {
            // c1
            hoveredNote = "f";
            hoveredOctave = "4";
        } else if (y > spaceBetweenLines * 10.25 && y <= spaceBetweenLines * 10.75) {
            hoveredNote = "e";
            hoveredOctave = "4";
        } else if (y > spaceBetweenLines * 10.75 && y <= spaceBetweenLines * 11.25) {
            hoveredNote = "d";
            hoveredOctave = "4";
        } else if (y > spaceBetweenLines * 11.25 && y <= spaceBetweenLines * 11.75) {
            hoveredNote = "c";
            hoveredOctave = "4";
        } else if (y > spaceBetweenLines * 11.75 && y <= spaceBetweenLines * 12.25) {
            hoveredNote = "b";
            hoveredOctave = "3";
        } else if (y > spaceBetweenLines * 12.25 && y <= spaceBetweenLines * 12.75) {
            hoveredNote = "a";
            hoveredOctave = "3";
        } else if (y > spaceBetweenLines * 12.75 && y <= spaceBetweenLines * 13.25) {
            hoveredNote = "g";
            hoveredOctave = "3";
        } else if (y > spaceBetweenLines * 13.25 && y <= spaceBetweenLines * 13.75) {
            hoveredNote = "f";
            hoveredOctave = "3";
        } else if (y > spaceBetweenLines * 13.75 && y <= spaceBetweenLines * 14.25) {
            hoveredNote = "e";
            hoveredOctave = "3";
        } else if (y > spaceBetweenLines * 14.25 && y <= spaceBetweenLines * 14.75) {
            hoveredNote = "d";
            hoveredOctave = "3";
        } else if (y > spaceBetweenLines * 14.75 && y <= spaceBetweenLines * 15.25) {
            hoveredNote = "c";
            hoveredOctave = "3";
        }

        return hoveredNote;
    },

    /**
     * Method to clear canvas and redraw staves
     * @function
     * @public
     */
    clearCanvas = function() {
        // clear canvas and redraw staves
        context.clearRect(0, 0, canvas.width, canvas.height);
        stave.setContext(context).draw();
    };


    that.init = init;
    that.renderNotes = renderNotes;
    that.clearCanvas = clearCanvas;

    return that;
}