text:
//
// This software is released under the 3-clause BSD license.
//
// Copyright (c) 2015, Xin Chen <txchen@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the author nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

/*******************************************************************************
 * 1) MidiPlayer.js.
 ******************************************************************************/

/**
 * MidiPlayer class. Used to play midi by javascript, without any plugin.
 * Requires a HTML5 browser: firefox, chrome, safari, opera, IE10+.
 *
 * The other 5 js files are from [2][3], which is a demo of [1]:
 * [1] http://matt.west.co.tt/music/jasmid-midi-synthesis-with-javascript-and-html5-audio/
 * [2] http://jsspeccy.zxdemo.org/jasmid/
 * [3] https://github.com/gasman/jasmid
 *
 * Modification is done to audio.js:
 * - added function fireEventEnded().
 * - added 'ended' event firing when generator.finished is true.
 * - move 'context' outside function AudioPlayer, so in chrome it won't have this error
 *   when you loop the play:
 *   Failed to construct 'AudioContext': number of hardware contexts reached maximum (6)
 *
 * Github site: https://github.com/chenx/MidiPlayer
 *
 * @by: X. Chen
 * @Create on: 4/1/2015
 * @Last modified: 4/3/2015
 */
/*
     * Constructor of MidiPlayer class.
     * @param midi    MIDI file path.
     * @param target  Target html element that this MIDI player is attached to.
     * @param loop    Optinoal. Whether loop the play. Value is true/false, default is false.
     * @param maxLoop Optional. max number of loops to play when loop is true.
     *                Negative or 0 means infinite. Default is 1.
     * @param end_callback Optional. Callback function when MIDI ends.
     * @author X. Chen. April 2015.
     */
 window.MidiPlayer = function(midi, target, loop, maxLoop, end_callback) {
        this.midi = midi;
        this.target = document.getElementById(target);
        this.loop = (typeof (loop) == 'undefined') ? false : loop;

        if (! loop) {
            this.max_loop_ct = 1;
        } else {
            this.max_loop_ct = (typeof (maxLoop) == 'undefined') ? 1 : (maxLoop <= 0 ? 0 : maxLoop);
        }

        this.end_callback = (typeof (end_callback) == 'function') ? end_callback : null;

        this.debug_div = null;
        this.midiFile = null;
        this.synth = null;
        this.replayer = null;
        this.audio = null;
        this.ct = 0;          // loop counter.
        this.started = false; // state of play: started/stopped.
        this.listener_added = false;
    }

    MidiPlayer.prototype.setDebugDiv = function(debug_div_id) {
        this.debug_div = (typeof (debug_div_id) == 'undefined') ?
                         null : document.getElementById(debug_div_id);
    }

    MidiPlayer.prototype.debug = function(msg) {
        if (this.debug_div) {
            this.debug_div.innerHTML += msg + '<br/>';
        }
    }

    MidiPlayer.prototype.stop = function() {
        this.started = false;
        this.ct = 0;
        if (this.audio) {
            this.audio.stop();
            this.audio = null;
        }
        if (this.max_loop_ct > 0) {
            if (this.end_callback) { this.end_callback(); }
        }
    }

    MidiPlayer.prototype.play = function() {
        if (this.started) {
            this.stop();
            //return;
        }

        this.started = true;
        var o = this.target;

        var _this = this; // must be 'var', otherwise _this is public, and causes problem.
        var file = this.midi;
        var loop = this.loop;

        if (window.addEventListener) {
            // Should not add more than one listener after first call, otherwise o has more
            // and more listeners attached, and will fire n events the n-th time calling play.
            if (! this.listener_added) {
                this.listener_added = true;
                if (o) { // If o does not exist, don't add listener.
                    o.addEventListener('ended', function() { // addEventListener not work for IE8.
                        //alert('ended');
                        if (_this.max_loop_ct <= 0 || (++ _this.ct) < _this.max_loop_ct) {
                            _this.replayer = Replayer(_this.midiFile, _this.synth);
                            _this.audio = AudioPlayer(_this.replayer, o, loop);
                            _this.debug( file + ': loop ' + (1 +_this.ct) );
                        }
                        else if (_this.max_loop_ct > 0) {
                            _this.stop();
                        }
                    }, false);
                }
             }
         } else if (window.attachEvent) { // IE don't work anyway.
             //document.getElementById('music').attachEvent(
             //    'onclick', function(e) { alert('IE end'); }, true);
         }

         loadRemote(file, function(data) {
             if (_this.ct == 0) {
                 _this.midiFile = MidiFile(data);
                 _this.synth = Synth(44100);
             }
             _this.replayer = Replayer(_this.midiFile, _this.synth);
             _this.audio = AudioPlayer(_this.replayer, o, loop);
             _this.debug( file + ': loop ' + (1 + _this.ct) );
             //alert(_this.audio.type); // webkit for firefox, chrome; flash for opera/safari.
        });
    }

    // This function is modified from [2] by adding support for IE. See:
    // http://stackoverflow.com/questions/1919972/how-do-i-access-xhr-responsebody-for-binary-data-from-javascript-in-ie
    // https://code.google.com/p/jsdap/source/browse/trunk/?r=64
    //
    // However, IE8 and before do not support HTML5 Audio tag so this still will not work.
    // See: http://www.impressivewebs.com/html5-support-ie9/
    //
    // A private function, defined by 'var'.
    // Original definition in [2] is: function loadRemote(path, callback) {
    var loadRemote = function(path, callback) {
        var fetch = new XMLHttpRequest();
        fetch.open('GET', path);

        if (fetch.overrideMimeType) {
            fetch.overrideMimeType("text/plain; charset=x-user-defined"); // for non-IE.
        }
        else {
            fetch.setRequestHeader('Accept-Charset', 'x-user-defined');   // for IE.
        }

        fetch.onreadystatechange = function() {
            if(this.readyState == 4 && this.status == 200) {
                // munge response into a binary string
                if (IE_HACK) { // for IE.
                    var t = BinaryToArray(fetch.responseBody).toArray();
                    var ff = [];
                    var mx = t.length;
                    var scc= String.fromCharCode;
                    for (var z = 0; z < mx; z++) {
                        // t[z] here is equivalent to 't.charCodeAt(z) & 255' below.
                        // e.g., t[z] is 238, below t.charCodeAt[z] is 63470. 63470 & 255 = 238.
                        // But IE8 has no Audio element, so can't play anyway,
                        // and will report this error in audio.js: 'Audio' is undefined.
                        ff[z] = scc(t[z]);
                    }
                    callback(ff.join(""));
                } else {  // for non-IE.
                    var t = this.responseText || "" ;
                    var ff = [];
                    var mx = t.length;
                    var scc= String.fromCharCode;
                    for (var z = 0; z < mx; z++) {
                        ff[z] = scc(t.charCodeAt(z) & 255);
                    }
                    callback(ff.join(""));
                }
            }
        }
        fetch.send();
    }

// Now expand definition of MidiPlayer to include the other 6 files below.
// So comment out the line below, and add the back curly bracket at the end of file.
//} // (previous) end of: if (typeof (MidiPlayer) == 'undefined')

/*******************************************************************************
 * 2) vbscript.js
 ******************************************************************************/
/**
 * Convert binary string to array.
 *
 * See:
 * [1] http://stackoverflow.com/questions/1919972/how-do-i-access-xhr-responsebody-for-binary-data-from-javascript-in-ie
 * [2] https://code.google.com/p/jsdap/source/browse/trunk/?r=64
 */

var IE_HACK = (/msie/i.test(navigator.userAgent) &&
               !/opera/i.test(navigator.userAgent));
if (IE_HACK) {
    //alert('IE hack');
    document.write('<script type="text/vbscript">\n\
    Function BinaryToArray(Binary)\n\
        Dim i\n\
        ReDim byteArray(LenB(Binary))\n\
        For i = 1 To LenB(Binary)\n\
            byteArray(i-1) = AscB(MidB(Binary, i, 1))\n\
        Next\n\
        BinaryToArray = byteArray\n\
    End Function\n\
</script>');
}

/*******************************************************************************
 * Files 3) to 7) below are by:
 * Matt Westcott <matt@west.co.tt> - @gasmanic - http://matt.west.co.tt/
 * See:
 * - http://matt.west.co.tt/music/jasmid-midi-synthesis-with-javascript-and-html5-audio/
 * - http://jsspeccy.zxdemo.org/jasmid/
 * - https://github.com/gasman/jasmid
 ******************************************************************************/

/*******************************************************************************
 * 3) audio.js.
 ******************************************************************************/

var sampleRate = 44100; /* hard-coded in Flash player */
var context = null // XC.
// Note this may cause name conflict. So be careful of variable name "context".

//
// http://stackoverflow.com/questions/2856513/how-can-i-trigger-an-onchange-event-manually
// Added by XC.
//
function fireEventEnded(target) {
    if (! target) return;

    if (document.createEvent) {
        var evt = document.createEvent("HTMLEvents");
        evt.initEvent("ended", false, true);
        target.dispatchEvent(evt);
    }
    else if (document.createEventObject) {   // IE before version 9
        var myEvent = document.createEventObject();
        target.fireEvent('onclick', myEvent);
    }
}

//function AudioPlayer(generator, opts) {
function AudioPlayer(generator, targetElement, opts) {
    if (!opts) opts = {};
    var latency = opts.latency || 1;
    var checkInterval = latency * 100 /* in ms */

    var audioElement = new Audio();
    var webkitAudio = window.AudioContext || window.webkitAudioContext;
    var requestStop = false;

    if (audioElement.mozSetup) {
        audioElement.mozSetup(2, sampleRate); /* channels, sample rate */

        var buffer = []; /* data generated but not yet written */
        var minBufferLength = latency * 2 * sampleRate; /* refill buffer when there are only this many elements remaining */
        var bufferFillLength = Math.floor(latency * sampleRate);

        function checkBuffer() {
            if (requestStop) return; // no more data feed after request stop. xc.
            if (buffer.length) {
                var written = audioElement.mozWriteAudio(buffer);
                buffer = buffer.slice(written);
            }
            if (buffer.length < minBufferLength && !generator.finished) {
                buffer = buffer.concat(generator.generate(bufferFillLength));
            }
            if (!requestStop && (!generator.finished || buffer.length)) {
                setTimeout(checkBuffer, checkInterval);
            }
            if (!requestStop && generator.finished) {
                fireEventEnded(targetElement); // xc.
            }
        }
        checkBuffer();

        return {
            'type': 'Firefox Audio',
            'stop': function() {
                requestStop = true;
            }
        }
    } else if (webkitAudio) {
        // Uses Webkit Web Audio API if available

        // chrome stops after 5 invocation. XC. Error is:
        // Failed to construct 'AudioContext': number of hardware contexts reached maximum (6)
        //var context = new webkitAudio();
        if (! context) context = new webkitAudio(); // fixed by this. XC.
        sampleRate = context.sampleRate;

        var channelCount = 2;
        var bufferSize = 4096*4; // Higher for less gitches, lower for less latency

        var node = context.createScriptProcessor(bufferSize, 0, channelCount);

        node.onaudioprocess = function(e) { process(e) };

        function process(e) {
            if (generator.finished) {
                node.disconnect();
                //alert('done: ' + targetElement); // xc.
                fireEventEnded(targetElement); // xc.
                return;
            }

            var dataLeft = e.outputBuffer.getChannelData(0);
            var dataRight = e.outputBuffer.getChannelData(1);

            var generate = generator.generate(bufferSize);

            for (var i = 0; i < bufferSize; ++i) {
                dataLeft[i] = generate[i*2];
                dataRight[i] = generate[i*2+1];
            }
        }

        // start
        node.connect(context.destination);

        return {
            'stop': function() {
                // pause
                node.disconnect();
                requestStop = true;
            },
            'type': 'Webkit Audio'
        }

    } else {
        // Fall back to creating flash player
        var c = document.createElement('div');
        c.innerHTML = '<embed type="application/x-shockwave-flash" id="da-swf" src="da.swf" width="8" height="8" allowScriptAccess="always" style="position: fixed; left:-10px;" />';
        document.body.appendChild(c);
        var swf = document.getElementById('da-swf');

        var minBufferDuration = latency * 1000; /* refill buffer when there are only this many ms remaining */
        var bufferFillLength = latency * sampleRate;

        function write(data) {
            var out = new Array(data.length);
            for (var i = data.length-1; i != 0; i--) {
                out[i] = Math.floor(data[i]*32768);
            }
            return swf.write(out.join(' '));
        }

        function checkBuffer() {
            if (requestStop) return; // no more data feed after request stop. xc.
            if (swf.bufferedDuration() < minBufferDuration) {
                write(generator.generate(bufferFillLength));
            };
            if (!requestStop && !generator.finished) setTimeout(checkBuffer, checkInterval);
            if (!requestStop && generator.finished) fireEventEnded(targetElement); // xc.
        }

        function checkReady() {
            if (swf.write) {
                checkBuffer();
            } else {
                setTimeout(checkReady, 10);
            }
        }
        checkReady();

        return {
            'stop': function() {
                swf.stop();
                requestStop = true;
            },
            'bufferedDuration': function() {
                return swf.bufferedDuration();
            },
            'type': 'Flash Audio'
        }
    }
}

/*******************************************************************************
 * 4) midifile.js
 ******************************************************************************/
/**
 * Class to parse the .mid file format. Depends on stream.js.
 */

function MidiFile(data) {
    function readChunk(stream) {
        var id = stream.read(4);
        var length = stream.readInt32();
        return {
            'id': id,
            'length': length,
            'data': stream.read(length)
        };
    }

    var lastEventTypeByte;

    function readEvent(stream) {
        var event = {};
        event.deltaTime = stream.readVarInt();
        var eventTypeByte = stream.readInt8();
        if ((eventTypeByte & 0xf0) == 0xf0) {
            /* system / meta event */
            if (eventTypeByte == 0xff) {
                /* meta event */
                event.type = 'meta';
                var subtypeByte = stream.readInt8();
                var length = stream.readVarInt();
                switch(subtypeByte) {
                    case 0x00:
                        event.subtype = 'sequenceNumber';
                        if (length != 2) throw "Expected length for sequenceNumber event is 2, got " + length;
                        event.number = stream.readInt16();
                        return event;
                    case 0x01:
                        event.subtype = 'text';
                        event.text = stream.read(length);
                        return event;
                    case 0x02:
                        event.subtype = 'copyrightNotice';
                        event.text = stream.read(length);
                        return event;
                    case 0x03:
                        event.subtype = 'trackName';
                        event.text = stream.read(length);
                        return event;
                    case 0x04:
                        event.subtype = 'instrumentName';
                        event.text = stream.read(length);
                        return event;
                    case 0x05:
                        event.subtype = 'lyrics';
                        event.text = stream.read(length);
                        return event;
                    case 0x06:
                        event.subtype = 'marker';
                        event.text = stream.read(length);
                        return event;
                    case 0x07:
                        event.subtype = 'cuePoint';
                        event.text = stream.read(length);
                        return event;
                    case 0x20:
                        event.subtype = 'midiChannelPrefix';
                        if (length != 1) throw "Expected length for midiChannelPrefix event is 1, got " + length;
                        event.channel = stream.readInt8();
                        return event;
                    case 0x2f:
                        event.subtype = 'endOfTrack';
                        if (length != 0) throw "Expected length for endOfTrack event is 0, got " + length;
                        return event;
                    case 0x51:
                        event.subtype = 'setTempo';
                        if (length != 3) throw "Expected length for setTempo event is 3, got " + length;
                        event.microsecondsPerBeat = (
                            (stream.readInt8() << 16)
                            + (stream.readInt8() << 8)
                            + stream.readInt8()
                        )
                        return event;
                    case 0x54:
                        event.subtype = 'smpteOffset';
                        if (length != 5) throw "Expected length for smpteOffset event is 5, got " + length;
                        var hourByte = stream.readInt8();
                        event.frameRate = {
                            0x00: 24, 0x20: 25, 0x40: 29, 0x60: 30
                        }[hourByte & 0x60];
                        event.hour = hourByte & 0x1f;
                        event.min = stream.readInt8();
                        event.sec = stream.readInt8();
                        event.frame = stream.readInt8();
                        event.subframe = stream.readInt8();
                        return event;
                    case 0x58:
                        event.subtype = 'timeSignature';
                        if (length != 4) throw "Expected length for timeSignature event is 4, got " + length;
                        event.numerator = stream.readInt8();
                        event.denominator = Math.pow(2, stream.readInt8());
                        event.metronome = stream.readInt8();
                        event.thirtyseconds = stream.readInt8();
                        return event;
                    case 0x59:
                        event.subtype = 'keySignature';
                        if (length != 2) throw "Expected length for keySignature event is 2, got " + length;
                        event.key = stream.readInt8(true);
                        event.scale = stream.readInt8();
                        return event;
                    case 0x7f:
                        event.subtype = 'sequencerSpecific';
                        event.data = stream.read(length);
                        return event;
                    default:
                        // console.log("Unrecognised meta event subtype: " + subtypeByte);
                        event.subtype = 'unknown'
                        event.data = stream.read(length);
                        return event;
                }
                event.data = stream.read(length);
                return event;
            } else if (eventTypeByte == 0xf0) {
                event.type = 'sysEx';
                var length = stream.readVarInt();
                event.data = stream.read(length);
                return event;
            } else if (eventTypeByte == 0xf7) {
                event.type = 'dividedSysEx';
                var length = stream.readVarInt();
                event.data = stream.read(length);
                return event;
            } else {
                throw "Unrecognised MIDI event type byte: " + eventTypeByte;
            }
        } else {
            /* channel event */
            var param1;
            if ((eventTypeByte & 0x80) == 0) {
                /* running status - reuse lastEventTypeByte as the event type.
                    eventTypeByte is actually the first parameter
                */
                param1 = eventTypeByte;
                eventTypeByte = lastEventTypeByte;
            } else {
                param1 = stream.readInt8();
                lastEventTypeByte = eventTypeByte;
            }
            var eventType = eventTypeByte >> 4;
            event.channel = eventTypeByte & 0x0f;
            event.type = 'channel';
            switch (eventType) {
                case 0x08:
                    event.subtype = 'noteOff';
                    event.noteNumber = param1;
                    event.velocity = stream.readInt8();
                    return event;
                case 0x09:
                    event.noteNumber = param1;
                    event.velocity = stream.readInt8();
                    if (event.velocity == 0) {
                        event.subtype = 'noteOff';
                    } else {
                        event.subtype = 'noteOn';
                    }
                    return event;
                case 0x0a:
                    event.subtype = 'noteAftertouch';
                    event.noteNumber = param1;
                    event.amount = stream.readInt8();
                    return event;
                case 0x0b:
                    event.subtype = 'controller';
                    event.controllerType = param1;
                    event.value = stream.readInt8();
                    return event;
                case 0x0c:
                    event.subtype = 'programChange';
                    event.programNumber = param1;
                    return event;
                case 0x0d:
                    event.subtype = 'channelAftertouch';
                    event.amount = param1;
                    return event;
                case 0x0e:
                    event.subtype = 'pitchBend';
                    event.value = param1 + (stream.readInt8() << 7);
                    return event;
                default:
                    throw "Unrecognised MIDI event type: " + eventType
                    /*
                    console.log("Unrecognised MIDI event type: " + eventType);
                    stream.readInt8();
                    event.subtype = 'unknown';
                    return event;
                    */
            }
        }
    }

    stream = Stream(data);
    var headerChunk = readChunk(stream);
    if (headerChunk.id != 'MThd' || headerChunk.length != 6) {
        throw "Bad .mid file - header not found";
    }
    var headerStream = Stream(headerChunk.data);
    var formatType = headerStream.readInt16();
    var trackCount = headerStream.readInt16();
    var timeDivision = headerStream.readInt16();

    if (timeDivision & 0x8000) {
        throw "Expressing time division in SMTPE frames is not supported yet"
    } else {
        ticksPerBeat = timeDivision;
    }

    var header = {
        'formatType': formatType,
        'trackCount': trackCount,
        'ticksPerBeat': ticksPerBeat
    }
    var tracks = [];
    for (var i = 0; i < header.trackCount; i++) {
        tracks[i] = [];
        var trackChunk = readChunk(stream);
        if (trackChunk.id != 'MTrk') {
            throw "Unexpected chunk - expected MTrk, got "+ trackChunk.id;
        }
        var trackStream = Stream(trackChunk.data);
        while (!trackStream.eof()) {
            var event = readEvent(trackStream);
            tracks[i].push(event);
            //console.log(event);
        }
    }

    return {
        'header': header,
        'tracks': tracks
    }
}

/*******************************************************************************
 * 5) replayer.js
 ******************************************************************************/

function Replayer(midiFile, synth) {
    var trackStates = [];
    var beatsPerMinute = 120;
    var ticksPerBeat = midiFile.header.ticksPerBeat;
    var channelCount = 16;

    for (var i = 0; i < midiFile.tracks.length; i++) {
        trackStates[i] = {
            'nextEventIndex': 0,
            'ticksToNextEvent': (
                midiFile.tracks[i].length ?
                    midiFile.tracks[i][0].deltaTime :
                    null
            )
        };
    }

    function Channel(number) {
        this.number = number;
        var generatorsByNote = {};
        var currentProgram = PianoProgram;

        function noteOn(note, velocity) {
            window.superSpecialNoteCallback("on", this.number, note);

            if (generatorsByNote[note] && !generatorsByNote[note].released) {
                /* playing same note before releasing the last one. BOO */
                generatorsByNote[note].noteOff(); /* TODO: check whether we ought to be passing a velocity in */
            }
            generator = currentProgram.createNote(note, velocity);
            synth.addGenerator(generator);
            generatorsByNote[note] = generator;
        }

        function noteOff(note, velocity) {
            if (generatorsByNote[note] && !generatorsByNote[note].released) {
                window.superSpecialNoteCallback("off", this.number, note);
                generatorsByNote[note].noteOff(velocity);
            }
        }

        function setProgram(programNumber) {
            currentProgram = PROGRAMS[programNumber] || PianoProgram;
        }

        return {
            'noteOn': noteOn,
            'noteOff': noteOff,
            'setProgram': setProgram
        }
    }

    var channels = [];
    for (var i = 0; i < channelCount; i++) {
        channels[i] = Channel(i);
    }

    var nextEventInfo;
    var samplesToNextEvent = 0;

    function getNextEvent() {
        var ticksToNextEvent = null;
        var nextEventTrack = null;
        var nextEventIndex = null;

        for (var i = 0; i < trackStates.length; i++) {
            if (
                trackStates[i].ticksToNextEvent != null
                && (ticksToNextEvent == null || trackStates[i].ticksToNextEvent < ticksToNextEvent)
            ) {
                ticksToNextEvent = trackStates[i].ticksToNextEvent;
                nextEventTrack = i;
                nextEventIndex = trackStates[i].nextEventIndex;
            }
        }
        if (nextEventTrack != null) {
            /* consume event from that track */
            var nextEvent = midiFile.tracks[nextEventTrack][nextEventIndex];
            if (midiFile.tracks[nextEventTrack][nextEventIndex + 1]) {
                trackStates[nextEventTrack].ticksToNextEvent += midiFile.tracks[nextEventTrack][nextEventIndex + 1].deltaTime;
            } else {
                trackStates[nextEventTrack].ticksToNextEvent = null;
            }
            trackStates[nextEventTrack].nextEventIndex += 1;
            /* advance timings on all tracks by ticksToNextEvent */
            for (var i = 0; i < trackStates.length; i++) {
                if (trackStates[i].ticksToNextEvent != null) {
                    trackStates[i].ticksToNextEvent -= ticksToNextEvent
                }
            }
            nextEventInfo = {
                'ticksToEvent': ticksToNextEvent,
                'event': nextEvent,
                'track': nextEventTrack
            }
            var beatsToNextEvent = ticksToNextEvent / ticksPerBeat;
            var secondsToNextEvent = beatsToNextEvent / (beatsPerMinute / 60);
            samplesToNextEvent += secondsToNextEvent * synth.sampleRate;
        } else {
            nextEventInfo = null;
            samplesToNextEvent = null;
            self.finished = true;
        }
    }

    getNextEvent();

    function generate(samples) {
        var data = new Array(samples*2);
        var samplesRemaining = samples;
        var dataOffset = 0;

        while (true) {
            if (samplesToNextEvent != null && samplesToNextEvent <= samplesRemaining) {
                /* generate samplesToNextEvent samples, process event and repeat */
                var samplesToGenerate = Math.ceil(samplesToNextEvent);
                if (samplesToGenerate > 0) {
                    synth.generateIntoBuffer(samplesToGenerate, data, dataOffset);
                    dataOffset += samplesToGenerate * 2;
                    samplesRemaining -= samplesToGenerate;
                    samplesToNextEvent -= samplesToGenerate;
                }

                handleEvent();
                getNextEvent();
            } else {
                /* generate samples to end of buffer */
                if (samplesRemaining > 0) {
                    synth.generateIntoBuffer(samplesRemaining, data, dataOffset);
                    samplesToNextEvent -= samplesRemaining;
                }
                break;
            }
        }
        return data;
    }

    function handleEvent() {
        var event = nextEventInfo.event;
        switch (event.type) {
            case 'meta':
                switch (event.subtype) {
                    case 'setTempo':
                        beatsPerMinute = 60000000 / event.microsecondsPerBeat
                }
                break;
            case 'channel':
                switch (event.subtype) {
                    case 'noteOn':
                        channels[event.channel].noteOn(event.noteNumber, event.velocity);
                        break;
                    case 'noteOff':
                        channels[event.channel].noteOff(event.noteNumber, event.velocity);
                        break;
                    case 'programChange':
                        //console.log('program change to ' + event.programNumber);
                        channels[event.channel].setProgram(event.programNumber);
                        break;
                }
                break;
        }
    }

    function replay(audio) {
        console.log('replay');
        audio.write(generate(44100));
        setTimeout(function() {replay(audio)}, 10);
    }

    var self = {
        'replay': replay,
        'generate': generate,
        'finished': false
    }
    return self;
}

/*******************************************************************************
 * 6) stream.js
 ******************************************************************************/
/**
 * Wrapper for accessing strings through sequential reads.
 */

function Stream(str) {
    var position = 0;

    function read(length) {
        var result = str.substr(position, length);
        position += length;
        return result;
    }

    /* read a big-endian 32-bit integer */
    function readInt32() {
        var result = (
            (str.charCodeAt(position) << 24)
            + (str.charCodeAt(position + 1) << 16)
            + (str.charCodeAt(position + 2) << 8)
            + str.charCodeAt(position + 3));
        position += 4;
        return result;
    }

    /* read a big-endian 16-bit integer */
    function readInt16() {
        var result = (
            (str.charCodeAt(position) << 8)
            + str.charCodeAt(position + 1));
        position += 2;
        return result;
    }

    /* read an 8-bit integer */
    function readInt8(signed) {
        var result = str.charCodeAt(position);
        if (signed && result > 127) result -= 256;
        position += 1;
        return result;
    }

    function eof() {
        return position >= str.length;
    }

    /* read a MIDI-style variable-length integer
        (big-endian value in groups of 7 bits,
        with top bit set to signify that another byte follows)
    */
    function readVarInt() {
        var result = 0;
        while (true) {
            var b = readInt8();
            if (b & 0x80) {
                result += (b & 0x7f);
                result <<= 7;
            } else {
                /* b is the last byte */
                return result + b;
            }
        }
    }

    return {
        'eof': eof,
        'read': read,
        'readInt32': readInt32,
        'readInt16': readInt16,
        'readInt8': readInt8,
        'readVarInt': readVarInt
    }
}

/*******************************************************************************
 * 7) synth.js
 ******************************************************************************/

function SineGenerator(freq) {
    var self = {'alive': true};
    var period = sampleRate / freq;
    var t = 0;

    self.generate = function(buf, offset, count) {
        for (; count; count--) {
            var phase = t / period;
            var result = Math.sin(phase * 2 * Math.PI);
            buf[offset++] += result;
            buf[offset++] += result;
            t++;
        }
    }

    return self;
}

function SquareGenerator(freq, phase) {
    var self = {'alive': true};
    var period = sampleRate / freq;
    var t = 0;

    self.generate = function(buf, offset, count) {
        for (; count; count--) {
            var result = ( (t / period) % 1 > phase ? 1 : -1);
            buf[offset++] += result;
            buf[offset++] += result;
            t++;
        }
    }

    return self;
}

function ADSRGenerator(child, attackAmplitude, sustainAmplitude, attackTimeS, decayTimeS, releaseTimeS) {
    var self = {'alive': true}
    var attackTime = sampleRate * attackTimeS;
    var decayTime = sampleRate * (attackTimeS + decayTimeS);
    var decayRate = (attackAmplitude - sustainAmplitude) / (decayTime - attackTime);
    var releaseTime = null; /* not known yet */
    var endTime = null; /* not known yet */
    var releaseRate = sustainAmplitude / (sampleRate * releaseTimeS);
    var t = 0;

    self.noteOff = function() {
        if (self.released) return;
        releaseTime = t;
        self.released = true;
        endTime = releaseTime + sampleRate * releaseTimeS;
    }

    self.generate = function(buf, offset, count) {
        if (!self.alive) return;
        var input = new Array(count * 2);
        for (var i = 0; i < count*2; i++) {
            input[i] = 0;
        }
        child.generate(input, 0, count);

        childOffset = 0;
        while(count) {
            if (releaseTime != null) {
                if (t < endTime) {
                    /* release */
                    while(count && t < endTime) {
                        var ampl = sustainAmplitude - releaseRate * (t - releaseTime);
                        buf[offset++] += input[childOffset++] * ampl;
                        buf[offset++] += input[childOffset++] * ampl;
                        t++;
                        count--;
                    }
                } else {
                    /* dead */
                    self.alive = false;
                    return;
                }
            } else if (t < attackTime) {
                /* attack */
                while(count && t < attackTime) {
                    var ampl = attackAmplitude * t / attackTime;
                    buf[offset++] += input[childOffset++] * ampl;
                    buf[offset++] += input[childOffset++] * ampl;
                    t++;
                    count--;
                }
            } else if (t < decayTime) {
                /* decay */
                while(count && t < decayTime) {
                    var ampl = attackAmplitude - decayRate * (t - attackTime);
                    buf[offset++] += input[childOffset++] * ampl;
                    buf[offset++] += input[childOffset++] * ampl;
                    t++;
                    count--;
                }
            } else {
                /* sustain */
                while(count) {
                    buf[offset++] += input[childOffset++] * sustainAmplitude;
                    buf[offset++] += input[childOffset++] * sustainAmplitude;
                    t++;
                    count--;
                }
            }
        }
    }

    return self;
}

function midiToFrequency(note) {
    return 440 * Math.pow(2, (note-69)/12);
}

PianoProgram = {
    'attackAmplitude': 0.2,
    'sustainAmplitude': 0.1,
    'attackTime': 0.02,
    'decayTime': 0.3,
    'releaseTime': 0.02,
    'createNote': function(note, velocity) {
        var frequency = midiToFrequency(note);
        return ADSRGenerator(
            SineGenerator(frequency),
            this.attackAmplitude * (velocity / 128), this.sustainAmplitude * (velocity / 128),
            this.attackTime, this.decayTime, this.releaseTime
        );
    }
}

StringProgram = {
    'createNote': function(note, velocity) {
        var frequency = midiToFrequency(note);
        return ADSRGenerator(
            SineGenerator(frequency),
            0.5 * (velocity / 128), 0.2 * (velocity / 128),
            0.4, 0.8, 0.4
        );
    }
}

PROGRAMS = {
    41: StringProgram,
    42: StringProgram,
    43: StringProgram,
    44: StringProgram,
    45: StringProgram,
    46: StringProgram,
    47: StringProgram,
    49: StringProgram,
    50: StringProgram
};

function Synth(sampleRate) {

    var generators = [];

    function addGenerator(generator) {
        generators.push(generator);
    }

    function generate(samples) {
        var data = new Array(samples*2);
        generateIntoBuffer(samples, data, 0);
        return data;
    }

    function generateIntoBuffer(samplesToGenerate, buffer, offset) {
        for (var i = offset; i < offset + samplesToGenerate * 2; i++) {
            buffer[i] = 0;
        }
        for (var i = generators.length - 1; i >= 0; i--) {
            generators[i].generate(buffer, offset, samplesToGenerate);
            if (!generators[i].alive) generators.splice(i, 1);
        }
    }

    return {
        'sampleRate': sampleRate,
        'addGenerator': addGenerator,
        'generate': generate,
        'generateIntoBuffer': generateIntoBuffer
    }
}


text:
Skaele

            login with

            or

        <form name="loginForm" ng-submit="login(email, password, rememberme)">

                <input type="email" class="form-control" id="email" ng-model="email" placeholder="Email" required>

                <input type="password" class="form-control" id="password" ng-model="password" placeholder="Password" required>

                    <input type="checkbox" value="">
                    Remember me

                <button type="submit" class="btn btn-success btn-lg btn-block">Login</button>

        </form>

redpajama_set_name: RedPajamaGithub


text:
import _extends from "@babel/runtime/helpers/extends";
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
var _excluded = ["viewDate", "value", "weekStartsOn", "onDayChange", "isDaySelected", "isDayActive", "isDaySelectionEnd", "isDaySelectionStart", "onDayEnter", "onDayLeave", "isDayHinted", "isHintedDaySelectionStart", "isHintedDaySelectionEnd", "isDayFocused", "isDayDisabled", "size", "showNeighboringMonth", "dayProps", "className"];
import * as React from "react";
import { isSameDay, isSameMonth } from "../../lib/date";
import { CalendarDay } from "../CalendarDay/CalendarDay";
import { getDaysNames, getWeeks } from "../../lib/calendar";
import { LocaleProviderContext } from "../LocaleProviderContext/LocaleProviderContext";
import { classNamesString } from "../../lib/classNames";
import { Footnote } from "../Typography/Footnote/Footnote";
export var CalendarDays = function CalendarDays(_ref) {
  var viewDate = _ref.viewDate,
      value = _ref.value,
      weekStartsOn = _ref.weekStartsOn,
      onDayChange = _ref.onDayChange,
      isDaySelected = _ref.isDaySelected,
      isDayActive = _ref.isDayActive,
      isDaySelectionEnd = _ref.isDaySelectionEnd,
      isDaySelectionStart = _ref.isDaySelectionStart,
      onDayEnter = _ref.onDayEnter,
      onDayLeave = _ref.onDayLeave,
      isDayHinted = _ref.isDayHinted,
      isHintedDaySelectionStart = _ref.isHintedDaySelectionStart,
      isHintedDaySelectionEnd = _ref.isHintedDaySelectionEnd,
      isDayFocused = _ref.isDayFocused,
      isDayDisabled = _ref.isDayDisabled,
      size = _ref.size,
      _ref$showNeighboringM = _ref.showNeighboringMonth,
      showNeighboringMonth = _ref$showNeighboringM === void 0 ? false : _ref$showNeighboringM,
      dayProps = _ref.dayProps,
      className = _ref.className,
      props = _objectWithoutProperties(_ref, _excluded);

  var locale = React.useContext(LocaleProviderContext);
  var ref = React.useRef(null);

  var _React$useState = React.useState(new Date()),
      _React$useState2 = _slicedToArray(_React$useState, 1),
      now = _React$useState2[0];

  var weeks = React.useMemo(function () {
    return getWeeks(viewDate, weekStartsOn);
  }, [weekStartsOn, viewDate]);
  var daysNames = React.useMemo(function () {
    return getDaysNames(now, weekStartsOn, locale);
  }, [locale, now, weekStartsOn]);
  var handleDayChange = React.useCallback(function (date) {
    var _ref$current;

    onDayChange(date);
    (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.focus();
  }, [onDayChange]);
  return /*#__PURE__*/React.createElement("div", _extends({}, props, {
    className: classNamesString("vkuiCalendarDays", className),
    ref: ref
  }), /*#__PURE__*/React.createElement("div", {
    className: classNamesString("vkuiCalendarDays__row", size && styles["CalendarDays__row--size-".concat(size)])
  }, daysNames.map(function (dayName) {
    return /*#__PURE__*/React.createElement(Footnote, {
      key: dayName,
      className: "vkuiCalendarDays__weekday"
    }, dayName);
  })), weeks.map(function (week, i) {
    return /*#__PURE__*/React.createElement("div", {
      className: classNamesString("vkuiCalendarDays__row", size && styles["CalendarDays__row--size-".concat(size)]),
      key: i
    }, week.map(function (day, i) {
      var sameMonth = isSameMonth(day, viewDate);
      return /*#__PURE__*/React.createElement(CalendarDay, _extends({
        key: day.toISOString(),
        day: day,
        today: isSameDay(day, now),
        active: isDayActive(day),
        onChange: handleDayChange,
        hidden: !showNeighboringMonth && !sameMonth,
        disabled: isDayDisabled(day),
        selectionStart: isDaySelectionStart(day, i),
        selectionEnd: isDaySelectionEnd(day, i),
        hintedSelectionStart: isHintedDaySelectionStart === null || isHintedDaySelectionStart === void 0 ? void 0 : isHintedDaySelectionStart(day, i),
        hintedSelectionEnd: isHintedDaySelectionEnd === null || isHintedDaySelectionEnd === void 0 ? void 0 : isHintedDaySelectionEnd(day, i),
        selected: isDaySelected === null || isDaySelected === void 0 ? void 0 : isDaySelected(day),
        focused: isDayFocused(day),
        onEnter: onDayEnter,
        onLeave: onDayLeave,
        hinted: isDayHinted === null || isDayHinted === void 0 ? void 0 : isDayHinted(day),
        sameMonth: sameMonth,
        size: size
      }, dayProps));
    }));
  }));
};
var styles = {
  "CalendarDays__row--size-s": "vkuiCalendarDays__row--size-s",
  "CalendarDays__row--size-m": "vkuiCalendarDays__row--size-m"
};
//# sourceMappingURL=CalendarDays.js.map


text:
<?php

declare(strict_types=1);

namespace Humbug\PhpScoper\Scoper\Composer;

use Humbug\PhpScoper\Scoper\Scoper;
use InvalidArgumentException;
use stdClass;
use function array_map;
use function gettype;
use function is_array;
use function preg_match as native_preg_match;
use function Safe\json_decode;
use function Safe\json_encode;
use function sprintf;
use const JSON_PRETTY_PRINT;
use const JSON_THROW_ON_ERROR;

final class InstalledPackagesScoper implements Scoper
{
    private const COMPOSER_INSTALLED_FILE_PATTERN = '/composer(\/|\\\\)installed\.json$/';

    public function __construct(
        private readonly Scoper $decoratedScoper,
        private readonly AutoloadPrefixer $autoloadPrefixer,
    ) {
    }

    /**
     * Scopes PHP and JSON files related to Composer.
     */
    public function scope(string $filePath, string $contents): string
    {
        if (1 !== native_preg_match(self::COMPOSER_INSTALLED_FILE_PATTERN, $filePath)) {
            return $this->decoratedScoper->scope($filePath, $contents);
        }

        $decodedJson = self::decodeContents($contents);

        if (!isset($decodedJson->packages) || !is_array($decodedJson->packages)) {
            throw new InvalidArgumentException('Expected the decoded JSON to contain the list of installed packages');
        }

        $decodedJson->packages = $this->prefixLockPackages($decodedJson->packages);

        return json_encode(
            $decodedJson,
            JSON_PRETTY_PRINT,
        );
    }

    private static function decodeContents(string $contents): stdClass
    {
        $decodedJson = json_decode($contents, false, 512, JSON_THROW_ON_ERROR);

        if ($decodedJson instanceof stdClass) {
            return $decodedJson;
        }

        throw new InvalidArgumentException(
            sprintf(
                'Expected the decoded JSON to be an stdClass instance, got "%s" instead',
                gettype($decodedJson),
            ),
        );
    }

    /**
     * @param array<string, stdClass> $packages
     *
     * @return array<string, stdClass>
     */
    private function prefixLockPackages(array $packages): array
    {
        return array_map(
            fn (stdClass $package) => $this->autoloadPrefixer->prefixPackageAutoloadStatements($package),
            $packages,
        );
    }
}


text:
tvrenamer.cache package
=======================

Submodules
----------

tvrenamer.cache.api module
--------------------------

.. automodule:: tvrenamer.cache.api
    :members:
    :undoc-members:
    :show-inheritance:

Module contents
---------------

.. automodule:: tvrenamer.cache
    :members:
    :undoc-members:
    :show-inheritance:


text:
class AssignmentsController < ApplicationController
  include RepositoryHelper
  responders :flash
  before_action { authorize! }

  content_security_policy only: [:edit, :new] do |p|
    # required because jquery-ui-timepicker-addon inserts style
    # dynamically. TODO: remove this when possible
    p.style_src :self, "'unsafe-inline'"
  end

  # Publicly accessible actions ---------------------------------------

  def show
    assignment = Assignment.find(params[:id])
    @assignment = assignment.is_peer_review? ? assignment.parent_assignment : assignment
    if @assignment.is_hidden
      render 'shared/http_status',
             formats: [:html],
             locals: {
               code: '404',
               message: HttpStatusHelper::ERROR_CODE['message']['404']
             },
             status: 404,
             layout: false
      return
    end

    @grouping = current_user.accepted_grouping_for(@assignment.id)

    if @grouping.nil?
      if @assignment.scanned_exam
        flash_now(:notice, t('assignments.scanned_exam.under_review'))
      elsif @assignment.group_max == 1 && (!@assignment.is_timed ||
                                           Time.current > assignment.section_due_date(@current_user&.section))
        begin
          current_user.create_group_for_working_alone_student(@assignment.id)
        rescue StandardError => e
          flash_message(:error, e.message)
          redirect_to controller: :assignments
          return
        end
        @grouping = @current_user.accepted_grouping_for(@assignment.id)
      end
    end
    unless @grouping.nil?
      flash_message(:warning, I18n.t('assignments.starter_file.changed_warning')) if @grouping.starter_file_changed
      if @assignment.is_timed && !@grouping.start_time.nil? && !@grouping.past_collection_date?
        flash_message(:note, I18n.t('assignments.timed.started_message_html'))
        flash_message(:note, I18n.t('assignments.timed.starter_file_prompt'))
      end
      set_repo_vars(@assignment, @grouping)
    end
    render layout: 'assignment_content'
  end

  def peer_review
    assignment = Assignment.find(params[:id])
    @assignment = assignment.is_peer_review? ? assignment : assignment.pr_assignment
    if @assignment.nil? || @assignment.is_hidden
      render 'shared/http_status',
             formats: [:html],
             locals: {
                 code: '404',
                 message: HttpStatusHelper::ERROR_CODE['message']['404']
             },
             status: 404,
             layout: false
      return
    end

    @student = current_user
    @grouping = current_user.accepted_grouping_for(@assignment.id)
    @prs = current_user.grouping_for(@assignment.parent_assignment.id)&.
        peer_reviews&.where(results: { released_to_students: true })
    if @prs.nil?
      @prs = []
    end

    if @assignment.past_all_collection_dates?
      flash_now(:notice, t('submissions.grading_can_begin'))
    else
      if @assignment.section_due_dates_type
        section_due_dates = Hash.new
        now = Time.current
        Section.all.each do |section|
          collection_time = @assignment.submission_rule.calculate_collection_time(section)
          collection_time = now if now >= collection_time
          if section_due_dates[collection_time].nil?
            section_due_dates[collection_time] = Array.new
          end
          section_due_dates[collection_time].push(section.name)
        end
        section_due_dates.each do |collection_time, sections|
          sections = sections.join(', ')
          if collection_time == now
            flash_now(:notice, t('submissions.grading_can_begin_for_sections',
                                 sections: sections))
          else
            flash_now(:notice, t('submissions.grading_can_begin_after_for_sections',
                                 time: l(collection_time),
                                 sections: sections))
          end
        end
      else
        collection_time = @assignment.submission_rule.calculate_collection_time
        flash_now(:notice, t('submissions.grading_can_begin_after',
                             time: l(collection_time)))
      end
    end
  end

  # Displays "Manage Assignments" page for creating and editing assignment information.
  # Acts as dashboard for students and TAs.
  def index
    if current_user.student?
      @a_id_results = {}
      accepted_groupings = current_user.accepted_groupings.includes(:assignment, { current_submission_used: :results })
      accepted_groupings.each do |grouping|
        if !grouping.assignment.is_hidden && grouping.has_submission?
          submission = grouping.current_submission_used
          if submission.has_remark? && submission.remark_result.released_to_students
            @a_id_results[grouping.assignment.id] = submission.remark_result
          elsif submission.has_result? && submission.get_original_result.released_to_students
            @a_id_results[grouping.assignment.id] = submission.get_original_result
          end
        end
      end

      @g_id_entries = {}
      current_user.grade_entry_students.where(released_to_student: true).includes(:grade_entry_form).each do |g|
        unless g.grade_entry_form.is_hidden
          @g_id_entries[g.assessment_id] = g
        end
      end

      render :student_assignment_list, layout: 'assignment_content'
    else
      render :index, layout: 'assignment_content'
    end
  end

  # Called on editing assignments (GET)
  def edit
    @assignment = Assignment.find_by_id(params[:id])
    past_date = @assignment.section_names_past_due_date
    @assignments = Assignment.all
    @sections = Section.all

    unless @assignment.scanned_exam
      if @assignment.past_collection_date?
        flash_now(:notice, t('assignments.due_date.final_due_date_passed'))
      elsif !past_date.blank?
        flash_now(:notice, t('assignments.due_date.past_due_date_notice') + past_date.join(', '))
      end
    end

    # build section_due_dates for each section that doesn't already have a due date
    Section.all.each do |s|
      unless SectionDueDate.find_by(assessment_id: @assignment.id, section_id: s.id)
        @assignment.section_due_dates.build(section: s)
      end
    end
    @section_due_dates = @assignment.section_due_dates
                                    .sort_by { |s| [SectionDueDate.due_date_for(s.section, @assignment), s.section.name] }
  end

  # Called when editing assignments form is submitted (PUT).
  def update
    @assignment = Assignment.find_by_id(params[:id])
    @assignments = Assignment.all
    @sections = Section.all

    begin
      new_required_files = false
      @assignment.transaction do
        @assignment, new_required_files = process_assignment_form(@assignment)
        @assignment.save!
      end
      if new_required_files && Settings.repository.type == 'git'
        # update list of required files in all repos only if there is a hook that will use that list
        @current_job = UpdateRepoRequiredFilesJob.perform_later(@assignment.id, current_user.user_name)
        session[:job_id] = @current_job.job_id
      end
    rescue
    end
    respond_with @assignment, location: -> { edit_assignment_path(@assignment) }
  end

  # Called in order to generate a form for creating a new assignment.
  # i.e. GET request on assignments/new
  def new
    @assignments = Assignment.all
    @assignment = Assignment.new
    if params[:scanned].present?
      @assignment.scanned_exam = true
    end
    if params[:timed].present?
      @assignment.is_timed = true
    end
    @clone_assignments = Assignment.joins(:assignment_properties)
                                   .where(assignment_properties: { vcs_submit: true })
                                   .order(:id)
    @sections = Section.all

    # build section_due_dates for each section
    Section.all.each { |s| @assignment.section_due_dates.build(section: s)}
    @section_due_dates = @assignment.section_due_dates
                                    .sort_by { |s| s.section.name }
    render :new
  end

  # Called after a new assignment form is submitted.
  def create
    @assignment = Assignment.new
    @assignment.transaction do
      begin
        @assignment, new_required_files = process_assignment_form(@assignment)
        @assignment.token_start_date = @assignment.due_date
        @assignment.token_period = 1
      rescue Exception, RuntimeError => e
        @assignment.errors.add(:base, e.message)
        new_required_files = false
      end
      unless @assignment.save
        @assignments = Assignment.all
        @sections = Section.all
        @clone_assignments = Assignment.joins(:assignment_properties)
                                       .where(assignment_properties: { vcs_submit: true })
                                       .order(:id)
        respond_with @assignment, location: -> { new_assignment_path(@assignment) }
        return
      end
      if params[:persist_groups_assignment]
        clone_warnings = @assignment.clone_groupings_from(params[:persist_groups_assignment])
        unless clone_warnings.empty?
          clone_warnings.each { |w| flash_message(:warning, w) }
        end
      end
      if new_required_files && Settings.repository.type == 'git'
        # update list of required files in all repos only if there is a hook that will use that list
        @current_job = UpdateRepoRequiredFilesJob.perform_later(@assignment.id, current_user.user_name)
        session[:job_id] = @current_job.job_id
      end
    end
    respond_with @assignment, location: -> { edit_assignment_path(@assignment) }
  end

  def summary
    @assignment = Assignment.find(params[:id])
    respond_to do |format|
      format.html { render layout: 'assignment_content' }
      format.json { render json: @assignment.summary_json(@current_user) }
      format.csv do
        data = @assignment.summary_csv(@current_user)
        filename = "#{@assignment.short_identifier}_summary.csv"
        send_data data,
                  disposition: 'attachment',
                  type: 'text/csv',
                  filename: filename
      end
    end
  end

  def stop_test
    test_id = params[:test_run_id].to_i
    assignment_id = params[:id]
    @current_job = AutotestCancelJob.perform_later(request.protocol + request.host_with_port, assignment_id, [test_id])
    session[:job_id] = @current_job.job_id
    redirect_back(fallback_location: root_path)
  end

  def stop_batch_tests
    test_runs = TestRun.where(test_batch_id: params[:test_batch_id]).pluck(:id)
    assignment_id = params[:id]
    @current_job = AutotestCancelJob.perform_later(request.protocol + request.host_with_port, assignment_id, test_runs)
    session[:job_id] = @current_job.job_id
    redirect_back(fallback_location: root_path)
  end

  def batch_runs
    @assignment = Assignment.find(params[:id])

    respond_to do |format|
      format.html
      format.json do
        user_ids = current_user.admin? ? Admin.pluck(:id) : current_user.id
        test_runs = TestRun.left_outer_joins(:test_batch, grouping: [:group, :current_result])
                           .where(test_runs: {user_id: user_ids},
                                  'groupings.assessment_id': @assignment.id)
                           .pluck_to_hash(:id,
                                          :test_batch_id,
                                          :time_to_service,
                                          :grouping_id,
                                          :submission_id,
                                          'test_batches.created_at',
                                          'test_runs.created_at',
                                          'groups.group_name',
                                          'results.id')
        status_hash = TestRun.statuses(test_runs.map { |tr| tr[:id] })
        test_batches = TestBatch.where(id: (test_runs.map { |tr| tr[:test_batch_id] }).compact.uniq)
        time_to_completion_hashes = test_batches.map(&:time_to_completion_hash)
        time_estimates = time_to_completion_hashes.empty? ? Hash.new : time_to_completion_hashes.inject(&:merge)
        test_runs.each do |test_run|
          created_at_raw = test_run.delete('test_batches.created_at') || test_run.delete('test_runs.created_at')
          test_run['created_at'] = I18n.l(created_at_raw)
          test_run['status'] = status_hash[test_run['id']]
          test_run['time_to_completion'] = time_estimates[test_run['id']] || ''
          test_run['group_name'] = test_run.delete('groups.group_name')
          test_run['result_id'] = test_run.delete('results.id')
        end
        render json: test_runs
      end
    end
  end

  # Refreshes the grade distribution graph
  def refresh_graph
    @assignment = Assignment.find(params[:id])
    respond_to do |format|
      format.js
    end
  end

  def view_summary
    @assignment = Assignment.find(params[:id])
    @current_ta = @assignment.tas.first
    @tas = @assignment.tas unless @assignment.nil?
  end

  def download
    format = params[:format]
    case format
    when 'csv'
      output = Assignment.get_assignment_list(format)
      send_data(output,
                filename: 'assignments.csv',
                type: 'text/csv',
                disposition: 'attachment')
    when 'yml'
      output = Assignment.get_assignment_list(format)
      send_data(output,
                filename: 'assignments.yml',
                type: 'text/yml',
                disposition: 'attachment')
    else
      flash[:error] = t('download_errors.unrecognized_format', format: format)
      redirect_to action: 'index', id: params[:id]
    end
  end

  def upload
    begin
      data = process_file_upload
    rescue Psych::SyntaxError => e
      flash_message(:error, t('upload_errors.syntax_error', error: e.to_s))
    rescue StandardError => e
      flash_message(:error, e.message)
    else
      if data[:type] == '.csv'
        result = Assignment.upload_assignment_list('csv', data[:file].read)
        flash_message(:error, result[:invalid_lines]) unless result[:invalid_lines].empty?
        flash_message(:success, result[:valid_lines]) unless result[:valid_lines].empty?
      elsif data[:type] == '.yml'
        result = Assignment.upload_assignment_list('yml', data[:contents])
        if result.is_a?(StandardError)
          flash_message(:error, result.message)
        end
      end
    end
    redirect_to action: 'index'
  end

  def starter_file
    @assignment = Assignment.find_by_id(params[:id])
    if @assignment.nil?
      render 'shared/http_status',
             locals: { code: '404', message: HttpStatusHelper::ERROR_CODE['message']['404'] },
             status: 404
    else
      render layout: 'assignment_content'
    end
  end

  def populate_starter_file_manager
    assignment = Assignment.find(params[:id])
    if assignment.groupings.exists?
      flash_message(:warning,
                    I18n.t('assignments.starter_file.groupings_exist_warning_html'))
    end
    file_data = []
    assignment.starter_file_groups.order(:id).each do |g|
      file_data << { id: g.id,
                     name: g.name,
                     entry_rename: g.entry_rename,
                     use_rename: g.use_rename,
                     files: starter_file_group_file_data(g) }
    end
    section_data = Section.left_outer_joins(:starter_file_groups)
                          .order(:id)
                          .pluck_to_hash('sections.id as section_id',
                                         'sections.name as section_name',
                                         'starter_file_groups.id as group_id',
                                         'starter_file_groups.name as group_name')
    data = { files: file_data,
             sections: section_data,
             available_after_due: assignment.starter_files_after_due,
             starterfileType: assignment.starter_file_type,
             defaultStarterFileGroup: assignment.default_starter_file_group&.id || '' }
    render json: data
  end

  def update_starter_file
    assignment = Assignment.find(params[:id])
    all_changed = false
    success = true
    ApplicationRecord.transaction do
      assignment.assignment_properties.update!(starter_file_assignment_params)
      all_changed = assignment.assignment_properties.saved_changes?
      params[:sections].each do |section_params|
        Section.find_by(id: section_params[:section_id])
               &.update_starter_file_group(assignment.id, section_params[:group_id])
      end
      starter_file_group_params.each do |group_params|
        starter_file_group = assignment.starter_file_groups.find_by(id: group_params[:id])
        starter_file_group.update!(group_params)
        all_changed ||= starter_file_group.saved_changes? || assignment.assignment_properties.saved_changes?
      end
      assignment.assignment_properties.update!(starter_file_updated_at: Time.current)
    rescue ActiveRecord::RecordInvalid => e
      flash_message(:error, e.message)
      success = false
      raise ActiveRecord::Rollback
    rescue StandardError => e
      flash_message(:error, e.message)
      success = false
      raise ActiveRecord::Rollback
    end
    if success
      flash_message(:success, I18n.t('flash.actions.update.success',
                                     resource_name: I18n.t('assignments.starter_file.title')))
    end
    # mark all groupings with starter files that were changed as changed
    assignment.groupings.update_all(starter_file_changed: true) if success && all_changed
  end

  def download_starter_file_mappings
    assignment = Assignment.find(params[:id])
    mappings = assignment.starter_file_mappings
    file_out = MarkusCsv.generate(mappings, [mappings.first&.keys].compact, &:values)
    send_data(file_out,
              type: 'text/csv',
              filename: "#{assignment.short_identifier}_starter_file_mappings.csv",
              disposition: 'inline')
  end

  def switch_assignment
    # TODO: Make this dependent on the referer URL.
    if current_user.admin?
      redirect_to edit_assignment_path(params[:id])
    elsif current_user.ta?
      redirect_to summary_assignment_path(params[:id])
    else # current_user.student?
      redirect_to assignment_path(params[:id])
    end
  end

  def set_boolean_graders_options
    assignment = Assignment.find(params[:id])
    attributes = graders_options_params
    return head 400 if attributes.empty? || attributes[:assignment_properties_attributes].empty?

    unless assignment.update(attributes)
      flash_now(:error, assignment.errors.full_messages.join(' '))
      head 422
      return
    end
    head :ok
  end

  # Start timed assignment for the current user's grouping for this assignment
  def start_timed_assignment
    assignment = Assignment.find(params[:id])
    grouping = current_user.accepted_grouping_for(assignment.id)
    if grouping.nil? && assignment.group_max == 1
      begin
        current_user.create_group_for_working_alone_student(assignment.id)
        grouping = current_user.accepted_grouping_for(assignment.id)
        set_repo_vars(assignment, grouping)
      rescue StandardError => e
        flash_message(:error, e.message)
      end
    end
    return head 400 if grouping.nil?
    authorize! grouping
    unless grouping.update(start_time: Time.current)
      flash_message(:error, grouping.errors.full_messages.join(' '))
    end
    redirect_to action: :show
  end

  private

  def set_repo_vars(assignment, grouping)
    grouping.access_repo do |repo|
      @revision = repo.get_revision_by_timestamp(Time.current, assignment.repository_folder)
      @last_modified_date = @revision&.server_timestamp
      files = @revision.tree_at_path(assignment.repository_folder, with_attrs: false)
                       .select do |_, obj|
                         obj.is_a?(Repository::RevisionFile) &&
                           !Repository.get_class.internal_file_names.include?(obj.name)
                       end
      @num_submitted_files = files.length
      missing_assignment_files = grouping.missing_assignment_files(@revision)
      @num_missing_assignment_files = missing_assignment_files.length
    end
  end

  def process_assignment_form(assignment)
    num_files_before = assignment.assignment_files.length
    short_identifier = assignment_params[:short_identifier]
    # remove potentially invalid periods before updating
    unless assignment_params[:assignment_properties_attributes][:scanned_exam] == 'true'
      period_attrs = submission_rule_params['submission_rule_attributes']['periods_attributes']
      periods = period_attrs.to_h.values.map { |h| h[:id].blank? ? nil : h[:id] }
      assignment.submission_rule.periods.where.not(id: periods).each(&:destroy)
    end
    assignment.assign_attributes(assignment_params)
    SubmissionRule.where(assignment: assignment).where.not(id: assignment.submission_rule.id).each(&:destroy)
    process_timed_duration(assignment) if assignment.is_timed
    assignment.repository_folder = short_identifier unless assignment.is_peer_review?
    assignment.save!
    new_required_files = assignment.saved_change_to_only_required_files? ||
                         assignment.saved_change_to_is_hidden? ||
                         assignment.assignment_files.any?(&:saved_changes?) ||
                         num_files_before != assignment.assignment_files.length
    # if there are no section due dates, destroy the objects that were created
    if ['0', nil].include? params[:assignment][:assignment_properties_attributes][:section_due_dates_type]
      assignment.section_due_dates.each(&:destroy)
      assignment.section_due_dates_type = false
      assignment.section_groups_only = false
    else
      assignment.section_due_dates_type = true
      assignment.section_groups_only = true
    end

    if params[:is_group_assignment] == 'true'
      # Is the instructor forming groups?
      if assignment_params[:assignment_properties_attributes][:student_form_groups] == '0'
        assignment.invalid_override = true
      else
        assignment.student_form_groups = true
        assignment.invalid_override = false
        assignment.group_name_autogenerated = true
      end
    else
      assignment.student_form_groups = false
      assignment.invalid_override = false
      assignment.group_min = 1
      assignment.group_max = 1
    end

    return assignment, new_required_files
  end

  # Convert the hours and minutes value given in the params to a duration value
  # and assign it to the duration attribute of +assignment+.
  def process_timed_duration(assignment)
    durs = duration_params['assignment_properties_attributes']['duration']
    assignment.duration = durs['hours'].to_i.hours + durs['minutes'].to_i.minutes
  end

  def starter_file_group_file_data(starter_file_group)
    starter_file_group.files_and_dirs.map do |file|
      if (starter_file_group.path + file).directory?
        { key: "#{file}/" }
      else
        { key: file, size: 1,
          url: download_file_assignment_starter_file_group_url(starter_file_group.assignment.id,
                                                               starter_file_group.id,
                                                               file_name: file) }
      end
    end
  end

  def graders_options_params
    params.require(:attribute)
          .permit(assignment_properties_attributes: [
                    :assign_graders_to_criteria,
                    :anonymize_groups,
                    :hide_unassigned_criteria
                  ])
  end

  def assignment_params
    params.require(:assignment).permit(
      :short_identifier,
      :description,
      :message,
      :due_date,
      :is_hidden,
      assignment_properties_attributes: [
        :id,
        :allow_web_submits,
        :vcs_submit,
        :display_median_to_students,
        :display_grader_names_to_students,
        :group_min,
        :group_max,
        :student_form_groups,
        :group_name_autogenerated,
        :allow_remarks,
        :remark_due_date,
        :remark_message,
        :section_groups_only,
        :enable_test,
        :enable_student_tests,
        :has_peer_review,
        :assign_graders_to_criteria,
        :group_name_displayed,
        :invalid_override,
        :section_groups_only,
        :only_required_files,
        :section_due_dates_type,
        :scanned_exam,
        :is_timed,
        :start_time
      ],
      section_due_dates_attributes: [
        :_destroy,
        :id,
        :section_id,
        :due_date,
        :start_time
      ],
      assignment_files_attributes:  [
        :_destroy,
        :id,
        :filename
      ],
      submission_rule_attributes: [
        :_destroy,
        :id,
        :type,
        { periods_attributes: [
          :id,
          :deduction,
          :interval,
          :hours,
          :_destroy
        ] }
      ]
    )
  end

  def duration_params
    params.require(:assignment).permit(
      assignment_properties_attributes: [
        duration: [
          :hours,
          :minutes
        ]
      ]
    )
  end

  def submission_rule_params
    params.require(:assignment)
          .permit(submission_rule_attributes: [
            :_destroy,
            :id,
            :type,
            { periods_attributes: [
              :id,
              :deduction,
              :interval,
              :hours,
              :_destroy
            ] }
          ])
  end

  def starter_file_assignment_params
    params.require(:assignment).permit(:starter_file_type, :default_starter_file_group_id, :starter_files_after_due)
  end

  def starter_file_group_params
    params.permit(starter_file_groups: [:id, :name, :entry_rename, :use_rename])
          .require(:starter_file_groups)
  end

  def flash_interpolation_options
    { resource_name: @assignment.short_identifier.blank? ? @assignment.model_name.human : @assignment.short_identifier,
      errors: @assignment.errors.full_messages.join('; ') }
  end
end


text:
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**status_code** | **str** |  | [optional]
**message** | **str** |  | [optional]
**list** | [**list[Answer]**](Answer.md) |  | [optional]
**data** | [**Answer**](Answer.md) |  | [optional]
**records** | **int** |  | [optional]

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


text:
#if GOOGLE_CUDA

#define EIGEN_USE_GPU

#include "tensorflow/core/kernels/xent_op.h"

#include "tensorflow/core/framework/tensor_types.h"
#include "tensorflow/core/platform/types.h"

namespace tensorflow {

typedef Eigen::GpuDevice GPUDevice;

// Partial specialization for a GPUDevice, that uses the Eigen implementation
// from XentEigenImpl.
namespace functor {
template <typename T>
struct XentFunctor<GPUDevice, T> {
  void operator()(const GPUDevice &d,
                  const Eigen::DSizes<Eigen::DenseIndex, 2> &shape,
                  const Eigen::array<Eigen::DenseIndex, 2> &logits_bcast,
                  const Eigen::array<Eigen::DenseIndex, 2> &labels_bcast,
                  typename TTypes<T>::ConstMatrix logits,
                  typename TTypes<T>::ConstMatrix labels,
                  typename TTypes<T>::Matrix scratch,
                  typename TTypes<T>::Vec loss,
                  typename TTypes<T>::Matrix backprop) {
    XentEigenImpl<GPUDevice, T>::Compute(d, shape, logits_bcast, labels_bcast,
                                         logits, labels, scratch, loss,
                                         backprop);
  }
};
}  // end namespace functor

// Instantiate the GPU implementation for half, float and double.
template struct functor::XentFunctor<GPUDevice, Eigen::half>;
template struct functor::XentFunctor<GPUDevice, float>;
template struct functor::XentFunctor<GPUDevice, double>;

}  // end namespace tensorflow

#endif  // GOOGLE_CUDA