<commit_before>SceneJS.createScene({
    type: 'scene',
    id: 'Scene',
    canvasId: 'scenejsCanvas',
    loggingElementId: 'scenejsLog',
    flags:
        {
            backfaces: false,
        },
    nodes: [
        {
            type: 'library',
            nodes: [
                {
                    type: 'material',
                    coreId: 'cube-mat',
                    baseColor:
                        {
                            r: 0.64,
                            b: 0.64,
                            g: 0.64,
                        },
                    emit: 0.0,
                },
                {
                    type: 'geometry',
                    coreId: 'cube-mesh',
                    primitive: 'triangles',
                    resource: 'cube-mesh',
                    positions: [1.0,1.0,-1.0,1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,1.0,-1.0,1.0,1.0,1.0,1.0,-1.0,1.0,-1.0,-1.0,1.0,-1.0,1.0,1.0,1.0,1.0,-1.0,1.0,1.0,1.0,1.0,-1.0,1.0,1.0,-1.0,-1.0,1.0,-1.0,-1.0,1.0,-1.0,1.0,-1.0,-1.0,1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,1.0,-1.0,1.0,1.0,-1.0,1.0,-1.0,1.0,1.0,1.0,1.0,1.0,-1.0,-1.0,1.0,-1.0,-1.0,1.0,1.0,],
                    indices: [0,1,2,0,2,3,4,7,6,4,6,5,8,9,10,8,10,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23,],
                },
            ],
        },
        {
            type: 'lookAt',
            id: 'main-lookAt',
            eye:
                {
                    y: -10.0,
                    x: 10.0,
                    z: 5.0,
                },
            look:
                {
                    y: 0.0,
                    x: 0.0,
                    z: 0.0,
                },
            up:
                {
                    y: 0.0,
                    x: 0.0,
                    z: 1.0,
                },
            nodes: [
                {
                    type: 'camera',
                    id: 'main-camera',
                    optics:
                        {
                            type: 'perspective',
                            far: 100.0,
                            near: 0.1,
                            aspect: 1.0,
                            fovy: 27.6380627952,
                        },
                    nodes: [
                        {
                            type: 'renderer',
                            id: 'main-renderer',
                            clear:
                                {
                                    color: true,
                                    depth: true,
                                    stencil: false,
                                },
                            clearColor:
                                {
                                    r: 0.4,
                                    b: 0.4,
                                    g: 0.4,
                                },
                            nodes: [
                                {
                                    type: 'matrix',
                                    id: 'light0-transform',
                                    elements: [-0.290864378214,0.955171227455,-0.055189050734,0.0,-0.771100878716,-0.199883162975,0.604524791241,0.0,0.566393375397,0.218391060829,0.794672250748,0.0,4.07624483109,1.00545394421,5.90386199951,1.0,],
                                    nodes: [
                                        {
                                            type: 'light',
                                            id: 'light0',
                                            color:
                                                {
                                                    r: 1.0,
                                                    b: 1.0,
                                                    g: 1.0,
                                                },
                                            pos:
                                                {
                                                    y: 0.0,
                                                    x: 0.0,
                                                    z: 0.0,
                                                },
                                            quadraticAttenuation: 0.000555556,
                                            linearAttenuation: 0.0,
                                            mode: 'point',
                                            constantAttenuation: 1.0,
                                        },
                                    ],
                                },
                                {
                                    type: 'matrix',
                                    id: 'light1-transform',
                                    elements: [1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,1.32968401909,-2.12760996819,2.74223303795,1.0,],
                                    nodes: [
                                        {
                                            type: 'light',
                                            id: 'light1',
                                            color:
                                                {
                                                    r: 1.0,
                                                    b: 1.0,
                                                    g: 1.0,
                                                },
                                            pos:
                                                {
                                                    y: 0.0,
                                                    x: 0.0,
                                                    z: 0.0,
                                                },
                                            quadraticAttenuation: 0.0008,
                                            linearAttenuation: 0.0,
                                            mode: 'point',
                                            constantAttenuation: 1.0,
                                        },
                                    ],
                                },
                                {
                                    type: 'matrix',
                                    id: 'cube-transform',
                                    elements: [1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,],
                                    nodes: [
                                        {
                                            type: 'material',
                                            coreId: 'cube-mat',
                                            id: 'cube-mat',
                                            nodes: [
                                                {
                                                    type: 'geometry',
                                                    coreId: 'cube-mesh',
                                                },
                                            ],
                                        },
                                    ],
                                },
                            ],
                        },
                    ],
                },
            ],
        },
    ],
});
<commit_msg>Add normals to the cube model (for now) to make lighting parameters available to the custom fragment shader<commit_after>SceneJS.createScene({
    type: 'scene',
    id: 'Scene',
    canvasId: 'scenejsCanvas',
    loggingElementId: 'scenejsLog',
    flags:
        {
            backfaces: false,
        },
    nodes: [
        {
            type: 'library',
            nodes: [
                {
                    type: 'material',
                    coreId: 'cube-mat',
                    baseColor:
                        {
                            r: 0.64,
                            b: 0.64,
                            g: 0.64,
                        },
                    emit: 0.0,
                },
                {
                    type: 'geometry',
                    coreId: 'cube-mesh',
                    primitive: 'triangles',
                    resource: 'cube-mesh',
                    positions: [1.0,1.0,-1.0,1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,1.0,-1.0,1.0,1.0,1.0,1.0,-1.0,1.0,-1.0,-1.0,1.0,-1.0,1.0,1.0,1.0,1.0,-1.0,1.0,1.0,1.0,1.0,-1.0,1.0,1.0,-1.0,-1.0,1.0,-1.0,-1.0,1.0,-1.0,1.0,-1.0,-1.0,1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,1.0,-1.0,1.0,1.0,-1.0,1.0,-1.0,1.0,1.0,1.0,1.0,1.0,-1.0,-1.0,1.0,-1.0,-1.0,1.0,1.0,],
                    normals: [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,],
                    indices: [0,1,2,0,2,3,4,7,6,4,6,5,8,9,10,8,10,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23,],
                },
            ],
        },
        {
            type: 'lookAt',
            id: 'main-lookAt',
            eye:
                {
                    y: -10.0,
                    x: 10.0,
                    z: 5.0,
                },
            look:
                {
                    y: 0.0,
                    x: 0.0,
                    z: 0.0,
                },
            up:
                {
                    y: 0.0,
                    x: 0.0,
                    z: 1.0,
                },
            nodes: [
                {
                    type: 'camera',
                    id: 'main-camera',
                    optics:
                        {
                            type: 'perspective',
                            far: 100.0,
                            near: 0.1,
                            aspect: 1.0,
                            fovy: 27.6380627952,
                        },
                    nodes: [
                        {
                            type: 'renderer',
                            id: 'main-renderer',
                            clear:
                                {
                                    color: true,
                                    depth: true,
                                    stencil: false,
                                },
                            clearColor:
                                {
                                    r: 0.4,
                                    b: 0.4,
                                    g: 0.4,
                                },
                            nodes: [
                                {
                                    type: 'matrix',
                                    id: 'light0-transform',
                                    elements: [-0.290864378214,0.955171227455,-0.055189050734,0.0,-0.771100878716,-0.199883162975,0.604524791241,0.0,0.566393375397,0.218391060829,0.794672250748,0.0,4.07624483109,1.00545394421,5.90386199951,1.0,],
                                    nodes: [
                                        {
                                            type: 'light',
                                            id: 'light0',
                                            color:
                                                {
                                                    r: 1.0,
                                                    b: 1.0,
                                                    g: 1.0,
                                                },
                                            pos:
                                                {
                                                    y: 0.0,
                                                    x: 0.0,
                                                    z: 0.0,
                                                },
                                            quadraticAttenuation: 0.000555556,
                                            linearAttenuation: 0.0,
                                            mode: 'point',
                                            constantAttenuation: 1.0,
                                        },
                                    ],
                                },
                                {
                                    type: 'matrix',
                                    id: 'light1-transform',
                                    elements: [1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,1.32968401909,-2.12760996819,2.74223303795,1.0,],
                                    nodes: [
                                        {
                                            type: 'light',
                                            id: 'light1',
                                            color:
                                                {
                                                    r: 1.0,
                                                    b: 1.0,
                                                    g: 1.0,
                                                },
                                            pos:
                                                {
                                                    y: 0.0,
                                                    x: 0.0,
                                                    z: 0.0,
                                                },
                                            quadraticAttenuation: 0.0008,
                                            linearAttenuation: 0.0,
                                            mode: 'point',
                                            constantAttenuation: 1.0,
                                        },
                                    ],
                                },
                                {
                                    type: 'matrix',
                                    id: 'cube-transform',
                                    elements: [1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0,],
                                    nodes: [
                                        {
                                            type: 'material',
                                            coreId: 'cube-mat',
                                            id: 'cube-mat',
                                            nodes: [
                                                {
                                                    type: 'geometry',
                                                    coreId: 'cube-mesh',
                                                },
                                            ],
                                        },
                                    ],
                                },
                            ],
                        },
                    ],
                },
            ],
        },
    ],
});
<|endoftext|>


<commit_before>Hero = function(game, characterName, isLocal, initX, initY, textureName) {
    //Phaser.Sprite.call(this, game, initX, initY, 'hero');
    Phaser.Sprite.call(this, game, initX, initY, textureName);

    this.game = game;
    this.characterName = characterName;
    this.isLocal = isLocal;

    // If local player, listen for keys.
    if (this.isLocal) {
        //game.input.keyboard.onDownCallback(this.handleKeyDown.bind(this));
        this.moveDirectionKeys = {
            up: this.game.input.keyboard.addKey(Phaser.Keyboard.W),
            left: this.game.input.keyboard.addKey(Phaser.Keyboard.A),
            down: this.game.input.keyboard.addKey(Phaser.Keyboard.S),
            right: this.game.input.keyboard.addKey(Phaser.Keyboard.D)
        };
        this.actionDirectionKeys = this.game.input.keyboard.createCursorKeys();
        this.actionSwitchKey = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
    }

    this.lastPos = {
        x: -1,
        y: -1
    }

    this.pos = {
        x: initX,
        y: initY
    }

    this.vel = {
        x: 0,
        y: 0
    }

    this.health = this.maxHealth;
    this.actionCooldown = 0;
    this.moveDelay = 0;
    this.currentAction = 1;

    this.animations.add('UP', [9, 10, 11]);
    this.animations.add('DOWN', [0,1,2]);
    this.animations.add('LEFT', [3,4,5]);
    this.animations.add('RIGHT', [6, 7, 8]);
    this.animations.play('DOWN', 5, true);

    // Init collision detection stuff
    game.add.existing(this);
    game.physics.enable(this, Phaser.Physics.ARCADE);

    this.body.collideWorldBounds = true;

    this.bringToTop();
};

Hero.prototype = Object.create(Phaser.Sprite.prototype);
Hero.prototype.constructor = Hero;

Hero.prototype.update = function() {
    if (this.moveDelay === 0) {
        var moveMessage = {
            type: 'FE_HERO_MOVE',
            content: {
                name: this.characterName,
                speed: 8,
                direction: ''
            }
        };

        if (this.moveDirectionKeys.left.isDown) {
            moveMessage.content.direction = 'LEFT'
            this.animations.play('LEFT', 5, true);
        } else if (this.moveDirectionKeys.right.isDown) {
            moveMessage.content.direction = 'RIGHT'
            this.animations.play('RIGHT', 5, true);
        } else if (this.moveDirectionKeys.up.isDown) {
            moveMessage.content.direction = 'UP'
            this.animations.play('UP', 5, true);
        } else if (this.moveDirectionKeys.down.isDown) {
            moveMessage.content.direction = 'DOWN'
            this.animations.play('DOWN', 5, true);
        }

        if (moveMessage.content.direction !== '') {
            //TODO this.messageCallback(moveMessage);
        }
    } else {
        this.moveDelay--;
    }

    if (this.actionSwitchKey.isDown) {
        if (this.currentAction === 1) {
            this.currentAction = 2;
        } else {
            this.currentAction = 1;
        }
    }

    if (this.actionCooldown === 0) {
        var actionMessage = {
            type: 'FE_HERO_ACTION',
            content: {
                name: this.characterName,
                action: this.currentAction,
                direction: ''
            }
        }

        if (this.actionDirectionKeys.left.isDown) {
            actionMessage.content.direction = 'LEFT'
        } else if (this.actionDirectionKeys.right.isDown) {
            actionMessage.content.direction = 'RIGHT'
        } else if (this.actionDirectionKeys.up.isDown) {
            actionMessage.content.direction = 'UP'
        } else if (this.actionDirectionKeys.down.isDown) {
            actionMessage.content.direction = 'DOWN'
        }

        if (actionMessage.content.direction !== '') {
            //TODO this.messageCallback(moveMessage);
        }
    }
}

Hero.prototype.update_smooth = function() {
    if (this.moveDelay === 0) {
        this.vel.x = 0;
        this.vel.y = 0;

        if (this.moveDirectionKeys.left.isDown) {
            this.vel.x = -8;
        } else if (this.moveDirectionKeys.right.isDown) {
            this.vel.x = 8;
        } else if (this.moveDirectionKeys.up.isDown) {
            this.vel.y = -8;
        } else if (this.moveDirectionKeys.down.isDown) {
            this.vel.y = 8;
        }

        this.pos.x += this.vel.x;
        this.pos.y += this.vel.y;

        this.x = this.pos.x;
        this.y = this.pos.y;

        // Check if there's been a change
        if (this.pos.x !== this.lastPos.x || this.pos.y !== this.lastPos.y) {
            /*this.messageCallback({
                type: 'FE_HERO_POSITION',
                content: {
                    name: this.characterName,
                    x: this.pos.x,
                    y: this.pos.y
                }
            })*/

            this.moveDelay = this.maxMoveDelay;

            this.lastPos.x = this.pos.x;
            this.lastPos.y = this.pos.y;
        }
    } else {
        this.moveDelay--;
    }
}<commit_msg>Removed use of pos, just uses Sprite position. Added receiveMessage method to handle server information.<commit_after>Hero = function(game, characterName, isLocal, initX, initY, textureName) {
    //Phaser.Sprite.call(this, game, initX, initY, 'hero');
    Phaser.Sprite.call(this, game, initX, initY, textureName);

    this.game = game;
    this.characterName = characterName;
    this.isLocal = isLocal;

    // If local player, listen for keys.
    if (this.isLocal) {
        //game.input.keyboard.onDownCallback(this.handleKeyDown.bind(this));
        this.moveDirectionKeys = {
            up: this.game.input.keyboard.addKey(Phaser.Keyboard.W),
            left: this.game.input.keyboard.addKey(Phaser.Keyboard.A),
            down: this.game.input.keyboard.addKey(Phaser.Keyboard.S),
            right: this.game.input.keyboard.addKey(Phaser.Keyboard.D)
        };
        this.actionDirectionKeys = this.game.input.keyboard.createCursorKeys();
        this.actionSwitchKey = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
    }

    this.lastPos = {
        x: -1,
        y: -1
    }

    this.vel = {
        x: 0,
        y: 0
    }

    this.health = this.maxHealth;
    this.actionCooldown = 0;
    this.moveDelay = 0;
    this.currentAction = 1;

    this.animations.add('UP', [9, 10, 11]);
    this.animations.add('DOWN', [0,1,2]);
    this.animations.add('LEFT', [3,4,5]);
    this.animations.add('RIGHT', [6, 7, 8]);
    this.animations.play('DOWN', 5, true);

    // Init collision detection stuff
    game.add.existing(this);
    game.physics.enable(this, Phaser.Physics.ARCADE);

    this.body.collideWorldBounds = true;

    this.bringToTop();
};

Hero.prototype = Object.create(Phaser.Sprite.prototype);
Hero.prototype.constructor = Hero;

Hero.prototype.update = function() {
    if (this.moveDelay === 0) {
        var moveMessage = {
            type: 'FE_HERO_MOVE',
            content: {
                name: this.characterName,
                speed: 8,
                direction: ''
            }
        };

        if (this.moveDirectionKeys.left.isDown) {
            moveMessage.content.direction = 'LEFT'
            this.animations.play('LEFT', 5, true);
        } else if (this.moveDirectionKeys.right.isDown) {
            moveMessage.content.direction = 'RIGHT'
            this.animations.play('RIGHT', 5, true);
        } else if (this.moveDirectionKeys.up.isDown) {
            moveMessage.content.direction = 'UP'
            this.animations.play('UP', 5, true);
        } else if (this.moveDirectionKeys.down.isDown) {
            moveMessage.content.direction = 'DOWN'
            this.animations.play('DOWN', 5, true);
        }

        if (moveMessage.content.direction !== '') {
            //TODO this.messageCallback(moveMessage);
        }
    } else {
        this.moveDelay--;
    }

    if (this.actionSwitchKey.isDown) {
        if (this.currentAction === 1) {
            this.currentAction = 2;
        } else {
            this.currentAction = 1;
        }
    }

    if (this.actionCooldown === 0) {
        var actionMessage = {
            type: 'FE_HERO_ACTION',
            content: {
                name: this.characterName,
                action: this.currentAction,
                direction: ''
            }
        }

        if (this.actionDirectionKeys.left.isDown) {
            actionMessage.content.direction = 'LEFT'
        } else if (this.actionDirectionKeys.right.isDown) {
            actionMessage.content.direction = 'RIGHT'
        } else if (this.actionDirectionKeys.up.isDown) {
            actionMessage.content.direction = 'UP'
        } else if (this.actionDirectionKeys.down.isDown) {
            actionMessage.content.direction = 'DOWN'
        }

        if (actionMessage.content.direction !== '') {
            //TODO this.messageCallback(moveMessage);
        }
    }
}

Hero.prototype.receiveMessage = function(message) {
    this.x = message.positionY;
    this.y = message.positionX;

    this.lastPos.x = this.x;
    this.lastPos.y = this.y;
}

Hero.prototype.update_smooth = function() {
    if (this.moveDelay === 0) {
        this.vel.x = 0;
        this.vel.y = 0;

        if (this.moveDirectionKeys.left.isDown) {
            this.vel.x = -8;
        } else if (this.moveDirectionKeys.right.isDown) {
            this.vel.x = 8;
        } else if (this.moveDirectionKeys.up.isDown) {
            this.vel.y = -8;
        } else if (this.moveDirectionKeys.down.isDown) {
            this.vel.y = 8;
        }

        this.x += this.vel.x;
        this.y += this.vel.y;

        // Check if there's been a change
        if (this.x !== this.lastPos.x || this.y !== this.lastPos.y) {
            /*this.messageCallback({
                type: 'FE_HERO_POSITION',
                content: {
                    name: this.characterName,
                    x: this.pos.x,
                    y: this.pos.y
                }
            })*/

            this.moveDelay = this.maxMoveDelay;

            this.lastPos.x = this.x;
            this.lastPos.y = this.y;
        }
    } else {
        this.moveDelay--;
    }
}<|endoftext|>


<commit_before>const MathUtil = require('../util/math-util');
const Cast = require('../util/cast');
const Clone = require('../util/clone');

class Scratch3SoundBlocks {
    constructor (runtime) {
        /**
         * The runtime instantiating this block package.
         * @type {Runtime}
         */
        this.runtime = runtime;

        // Clear sound effects on green flag and stop button events.
        this.stopAllSounds = this.stopAllSounds.bind(this);
        this._stopAllSoundsForTarget = this._stopAllSoundsForTarget.bind(this);
        this._clearEffectsForAllTargets = this._clearEffectsForAllTargets.bind(this);
        if (this.runtime) {
            this.runtime.on('PROJECT_STOP_ALL', this.stopAllSounds);
            this.runtime.on('PROJECT_STOP_ALL', this._clearEffectsForAllTargets);
            this.runtime.on('STOP_FOR_TARGET', this._stopAllSoundsForTarget);
            this.runtime.on('PROJECT_START', this._clearEffectsForAllTargets);
        }

        this._onTargetCreated = this._onTargetCreated.bind(this);
        if (this.runtime) {
            runtime.on('targetWasCreated', this._onTargetCreated);
        }
    }

    /**
     * The key to load & store a target's sound-related state.
     * @type {string}
     */
    static get STATE_KEY () {
        return 'Scratch.sound';
    }

    /**
     * The default sound-related state, to be used when a target has no existing sound state.
     * @type {SoundState}
     */
    static get DEFAULT_SOUND_STATE () {
        return {
            effects: {
                pitch: 0,
                pan: 0
            }
        };
    }

    /**
     * The minimum and maximum MIDI note numbers, for clamping the input to play note.
     * @type {{min: number, max: number}}
     */
    static get MIDI_NOTE_RANGE () {
        return {min: 36, max: 96}; // C2 to C7
    }

    /**
     * The minimum and maximum beat values, for clamping the duration of play note, play drum and rest.
     * 100 beats at the default tempo of 60bpm is 100 seconds.
     * @type {{min: number, max: number}}
     */
    static get BEAT_RANGE () {
        return {min: 0, max: 100};
    }

    /** The minimum and maximum tempo values, in bpm.
     * @type {{min: number, max: number}}
     */
    static get TEMPO_RANGE () {
        return {min: 20, max: 500};
    }

    /** The minimum and maximum values for each sound effect.
     * @type {{effect:{min: number, max: number}}}
     */
    static get EFFECT_RANGE () {
        return {
            pitch: {min: -360, max: 360}, // -3 to 3 octaves
            pan: {min: -100, max: 100} // 100% left to 100% right
        };
    }

    /**
     * @param {Target} target - collect sound state for this target.
     * @returns {SoundState} the mutable sound state associated with that target. This will be created if necessary.
     * @private
     */
    _getSoundState (target) {
        let soundState = target.getCustomState(Scratch3SoundBlocks.STATE_KEY);
        if (!soundState) {
            soundState = Clone.simple(Scratch3SoundBlocks.DEFAULT_SOUND_STATE);
            target.setCustomState(Scratch3SoundBlocks.STATE_KEY, soundState);
            target.soundEffects = soundState.effects;
        }
        return soundState;
    }

    /**
     * When a Target is cloned, clone the sound state.
     * @param {Target} newTarget - the newly created target.
     * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.
     * @listens Runtime#event:targetWasCreated
     * @private
     */
    _onTargetCreated (newTarget, sourceTarget) {
        if (sourceTarget) {
            const soundState = sourceTarget.getCustomState(Scratch3SoundBlocks.STATE_KEY);
            if (soundState && newTarget) {
                newTarget.setCustomState(Scratch3SoundBlocks.STATE_KEY, Clone.simple(soundState));
                this._syncEffectsForTarget(newTarget);
            }
        }
    }

    /**
     * Retrieve the block primitives implemented by this package.
     * @return {object.<string, Function>} Mapping of opcode to Function.
     */
    getPrimitives () {
        return {
            sound_play: this.playSound,
            sound_playuntildone: this.playSoundAndWait,
            sound_stopallsounds: this.stopAllSounds,
            sound_seteffectto: this.setEffect,
            sound_changeeffectby: this.changeEffect,
            sound_cleareffects: this.clearEffects,
            sound_sounds_menu: this.soundsMenu,
            sound_beats_menu: this.beatsMenu,
            sound_effects_menu: this.effectsMenu,
            sound_setvolumeto: this.setVolume,
            sound_changevolumeby: this.changeVolume,
            sound_volume: this.getVolume
        };
    }

    getMonitored () {
        return {
            sound_volume: {
                getId: () => 'volume'
            }
        };
    }

    playSound (args, util) {
        // Don't return the promise, it's the only difference for AndWait
        this.playSoundAndWait(args, util);
    }

    playSoundAndWait (args, util) {
        const index = this._getSoundIndex(args.SOUND_MENU, util);
        if (index >= 0) {
            const {target} = util;
            const {sprite} = target;
            const {soundId} = sprite.sounds[index];
            if (sprite.soundBank) {
                return sprite.soundBank.playSound(target, soundId);
            }
        }
    }

    _getSoundIndex (soundName, util) {
        // if the sprite has no sounds, return -1
        const len = util.target.sprite.sounds.length;
        if (len === 0) {
            return -1;
        }

        // look up by name first
        const index = this.getSoundIndexByName(soundName, util);
        if (index !== -1) {
            return index;
        }

        // then try using the sound name as a 1-indexed index
        const oneIndexedIndex = parseInt(soundName, 10);
        if (!isNaN(oneIndexedIndex)) {
            return MathUtil.wrapClamp(oneIndexedIndex - 1, 0, len - 1);
        }

        // could not be found as a name or converted to index, return -1
        return -1;
    }

    getSoundIndexByName (soundName, util) {
        const sounds = util.target.sprite.sounds;
        for (let i = 0; i < sounds.length; i++) {
            if (sounds[i].name === soundName) {
                return i;
            }
        }
        // if there is no sound by that name, return -1
        return -1;
    }

    stopAllSounds () {
        if (this.runtime.targets === null) return;
        const allTargets = this.runtime.targets;
        for (let i = 0; i < allTargets.length; i++) {
            this._stopAllSoundsForTarget(allTargets[i]);
        }
    }

    _stopAllSoundsForTarget (target) {
        if (target.sprite.soundBank) {
            target.sprite.soundBank.stopAllSounds(target);
        }
    }

    setEffect (args, util) {
        return this._updateEffect(args, util, false);
    }

    changeEffect (args, util) {
        return this._updateEffect(args, util, true);
    }

    _updateEffect (args, util, change) {
        const effect = Cast.toString(args.EFFECT).toLowerCase();
        const value = Cast.toNumber(args.VALUE);

        const soundState = this._getSoundState(util.target);
        if (!soundState.effects.hasOwnProperty(effect)) return;

        if (change) {
            soundState.effects[effect] += value;
        } else {
            soundState.effects[effect] = value;
        }

        const {min, max} = Scratch3SoundBlocks.EFFECT_RANGE[effect];
        soundState.effects[effect] = MathUtil.clamp(soundState.effects[effect], min, max);

        this._syncEffectsForTarget(util.target);
        // Yield until the next tick.
        return Promise.resolve();
    }

    _syncEffectsForTarget (target) {
        if (!target || !target.sprite.soundBank) return;
        target.soundEffects = this._getSoundState(target).effects;

        target.sprite.soundBank.setEffects(target);
    }

    clearEffects (args, util) {
        this._clearEffectsForTarget(util.target);
    }

    _clearEffectsForTarget (target) {
        const soundState = this._getSoundState(target);
        for (const effect in soundState.effects) {
            if (!soundState.effects.hasOwnProperty(effect)) continue;
            soundState.effects[effect] = 0;
        }
        this._syncEffectsForTarget(target);
    }

    _clearEffectsForAllTargets () {
        if (this.runtime.targets === null) return;
        const allTargets = this.runtime.targets;
        for (let i = 0; i < allTargets.length; i++) {
            this._clearEffectsForTarget(allTargets[i]);
        }
    }

    setVolume (args, util) {
        const volume = Cast.toNumber(args.VOLUME);
        return this._updateVolume(volume, util);
    }

    changeVolume (args, util) {
        const volume = Cast.toNumber(args.VOLUME) + util.target.volume;
        return this._updateVolume(volume, util);
    }

    _updateVolume (volume, util) {
        volume = MathUtil.clamp(volume, 0, 100);
        util.target.volume = volume;
        this._syncEffectsForTarget(util.target);

        // Yield until the next tick.
        return Promise.resolve();
    }

    getVolume (args, util) {
        return util.target.volume;
    }

    soundsMenu (args) {
        return args.SOUND_MENU;
    }

    beatsMenu (args) {
        return args.BEATS;
    }

    effectsMenu (args) {
        return args.EFFECT;
    }
}

module.exports = Scratch3SoundBlocks;
<commit_msg>stop only waiting sounds when STOP_FOR_TARGET is emitted<commit_after>const MathUtil = require('../util/math-util');
const Cast = require('../util/cast');
const Clone = require('../util/clone');

/**
 * Occluded boolean value to make its use more understandable.
 * @const {boolean}
 */
const STORE_WAITING = true;

class Scratch3SoundBlocks {
    constructor (runtime) {
        /**
         * The runtime instantiating this block package.
         * @type {Runtime}
         */
        this.runtime = runtime;

        this.waitingSounds = {};

        // Clear sound effects on green flag and stop button events.
        this.stopAllSounds = this.stopAllSounds.bind(this);
        this._stopWaitingSoundsForTarget = this._stopWaitingSoundsForTarget.bind(this);
        this._clearEffectsForAllTargets = this._clearEffectsForAllTargets.bind(this);
        if (this.runtime) {
            this.runtime.on('PROJECT_STOP_ALL', this.stopAllSounds);
            this.runtime.on('PROJECT_STOP_ALL', this._clearEffectsForAllTargets);
            this.runtime.on('STOP_FOR_TARGET', this._stopWaitingSoundsForTarget);
            this.runtime.on('PROJECT_START', this._clearEffectsForAllTargets);
        }

        this._onTargetCreated = this._onTargetCreated.bind(this);
        if (this.runtime) {
            runtime.on('targetWasCreated', this._onTargetCreated);
        }
    }

    /**
     * The key to load & store a target's sound-related state.
     * @type {string}
     */
    static get STATE_KEY () {
        return 'Scratch.sound';
    }

    /**
     * The default sound-related state, to be used when a target has no existing sound state.
     * @type {SoundState}
     */
    static get DEFAULT_SOUND_STATE () {
        return {
            effects: {
                pitch: 0,
                pan: 0
            }
        };
    }

    /**
     * The minimum and maximum MIDI note numbers, for clamping the input to play note.
     * @type {{min: number, max: number}}
     */
    static get MIDI_NOTE_RANGE () {
        return {min: 36, max: 96}; // C2 to C7
    }

    /**
     * The minimum and maximum beat values, for clamping the duration of play note, play drum and rest.
     * 100 beats at the default tempo of 60bpm is 100 seconds.
     * @type {{min: number, max: number}}
     */
    static get BEAT_RANGE () {
        return {min: 0, max: 100};
    }

    /** The minimum and maximum tempo values, in bpm.
     * @type {{min: number, max: number}}
     */
    static get TEMPO_RANGE () {
        return {min: 20, max: 500};
    }

    /** The minimum and maximum values for each sound effect.
     * @type {{effect:{min: number, max: number}}}
     */
    static get EFFECT_RANGE () {
        return {
            pitch: {min: -360, max: 360}, // -3 to 3 octaves
            pan: {min: -100, max: 100} // 100% left to 100% right
        };
    }

    /**
     * @param {Target} target - collect sound state for this target.
     * @returns {SoundState} the mutable sound state associated with that target. This will be created if necessary.
     * @private
     */
    _getSoundState (target) {
        let soundState = target.getCustomState(Scratch3SoundBlocks.STATE_KEY);
        if (!soundState) {
            soundState = Clone.simple(Scratch3SoundBlocks.DEFAULT_SOUND_STATE);
            target.setCustomState(Scratch3SoundBlocks.STATE_KEY, soundState);
            target.soundEffects = soundState.effects;
        }
        return soundState;
    }

    /**
     * When a Target is cloned, clone the sound state.
     * @param {Target} newTarget - the newly created target.
     * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.
     * @listens Runtime#event:targetWasCreated
     * @private
     */
    _onTargetCreated (newTarget, sourceTarget) {
        if (sourceTarget) {
            const soundState = sourceTarget.getCustomState(Scratch3SoundBlocks.STATE_KEY);
            if (soundState && newTarget) {
                newTarget.setCustomState(Scratch3SoundBlocks.STATE_KEY, Clone.simple(soundState));
                this._syncEffectsForTarget(newTarget);
            }
        }
    }

    /**
     * Retrieve the block primitives implemented by this package.
     * @return {object.<string, Function>} Mapping of opcode to Function.
     */
    getPrimitives () {
        return {
            sound_play: this.playSound,
            sound_playuntildone: this.playSoundAndWait,
            sound_stopallsounds: this.stopAllSounds,
            sound_seteffectto: this.setEffect,
            sound_changeeffectby: this.changeEffect,
            sound_cleareffects: this.clearEffects,
            sound_sounds_menu: this.soundsMenu,
            sound_beats_menu: this.beatsMenu,
            sound_effects_menu: this.effectsMenu,
            sound_setvolumeto: this.setVolume,
            sound_changevolumeby: this.changeVolume,
            sound_volume: this.getVolume
        };
    }

    getMonitored () {
        return {
            sound_volume: {
                getId: () => 'volume'
            }
        };
    }

    playSound (args, util) {
        // Don't return the promise, it's the only difference for AndWait
        this._playSound(args, util);
    }

    playSoundAndWait (args, util) {
        return this._playSound(args, util, STORE_WAITING);
    }

    _playSound (args, util, storeWaiting) {
        const index = this._getSoundIndex(args.SOUND_MENU, util);
        if (index >= 0) {
            const {target} = util;
            const {sprite} = target;
            const {soundId} = sprite.sounds[index];
            if (sprite.soundBank) {
                if (storeWaiting === STORE_WAITING) {
                    this._addWaitingSound(target.id, soundId);
                } else {
                    this._removeWaitingSound(target.id, soundId);
                }
                return sprite.soundBank.playSound(target, soundId);
            }
        }
    }

    _addWaitingSound (targetId, soundId) {
        if (!this.waitingSounds[targetId]) {
            this.waitingSounds[targetId] = new Set();
        }
        this.waitingSounds[targetId].add(soundId);
    }

    _removeWaitingSound (targetId, soundId) {
        if (!this.waitingSounds[targetId]) {
            return;
        }
        this.waitingSounds[targetId].delete(soundId);
    }

    _getSoundIndex (soundName, util) {
        // if the sprite has no sounds, return -1
        const len = util.target.sprite.sounds.length;
        if (len === 0) {
            return -1;
        }

        // look up by name first
        const index = this.getSoundIndexByName(soundName, util);
        if (index !== -1) {
            return index;
        }

        // then try using the sound name as a 1-indexed index
        const oneIndexedIndex = parseInt(soundName, 10);
        if (!isNaN(oneIndexedIndex)) {
            return MathUtil.wrapClamp(oneIndexedIndex - 1, 0, len - 1);
        }

        // could not be found as a name or converted to index, return -1
        return -1;
    }

    getSoundIndexByName (soundName, util) {
        const sounds = util.target.sprite.sounds;
        for (let i = 0; i < sounds.length; i++) {
            if (sounds[i].name === soundName) {
                return i;
            }
        }
        // if there is no sound by that name, return -1
        return -1;
    }

    stopAllSounds () {
        if (this.runtime.targets === null) return;
        const allTargets = this.runtime.targets;
        for (let i = 0; i < allTargets.length; i++) {
            this._stopAllSoundsForTarget(allTargets[i]);
        }
    }

    _stopAllSoundsForTarget (target) {
        if (target.sprite.soundBank) {
            target.sprite.soundBank.stopAllSounds(target);
            if (this.waitingSounds[target.id]) {
                this.waitingSounds[target.id].clear();
            }
        }
    }

    _stopWaitingSoundsForTarget (target) {
        if (target.sprite.soundBank) {
            if (this.waitingSounds[target.id]) {
                for (const soundId of this.waitingSounds[target.id].values()) {
                    target.sprite.soundBank.stop(target, soundId);
                }
                this.waitingSounds[target.id].clear();
            }
        }
    }

    setEffect (args, util) {
        return this._updateEffect(args, util, false);
    }

    changeEffect (args, util) {
        return this._updateEffect(args, util, true);
    }

    _updateEffect (args, util, change) {
        const effect = Cast.toString(args.EFFECT).toLowerCase();
        const value = Cast.toNumber(args.VALUE);

        const soundState = this._getSoundState(util.target);
        if (!soundState.effects.hasOwnProperty(effect)) return;

        if (change) {
            soundState.effects[effect] += value;
        } else {
            soundState.effects[effect] = value;
        }

        const {min, max} = Scratch3SoundBlocks.EFFECT_RANGE[effect];
        soundState.effects[effect] = MathUtil.clamp(soundState.effects[effect], min, max);

        this._syncEffectsForTarget(util.target);
        // Yield until the next tick.
        return Promise.resolve();
    }

    _syncEffectsForTarget (target) {
        if (!target || !target.sprite.soundBank) return;
        target.soundEffects = this._getSoundState(target).effects;

        target.sprite.soundBank.setEffects(target);
    }

    clearEffects (args, util) {
        this._clearEffectsForTarget(util.target);
    }

    _clearEffectsForTarget (target) {
        const soundState = this._getSoundState(target);
        for (const effect in soundState.effects) {
            if (!soundState.effects.hasOwnProperty(effect)) continue;
            soundState.effects[effect] = 0;
        }
        this._syncEffectsForTarget(target);
    }

    _clearEffectsForAllTargets () {
        if (this.runtime.targets === null) return;
        const allTargets = this.runtime.targets;
        for (let i = 0; i < allTargets.length; i++) {
            this._clearEffectsForTarget(allTargets[i]);
        }
    }

    setVolume (args, util) {
        const volume = Cast.toNumber(args.VOLUME);
        return this._updateVolume(volume, util);
    }

    changeVolume (args, util) {
        const volume = Cast.toNumber(args.VOLUME) + util.target.volume;
        return this._updateVolume(volume, util);
    }

    _updateVolume (volume, util) {
        volume = MathUtil.clamp(volume, 0, 100);
        util.target.volume = volume;
        this._syncEffectsForTarget(util.target);

        // Yield until the next tick.
        return Promise.resolve();
    }

    getVolume (args, util) {
        return util.target.volume;
    }

    soundsMenu (args) {
        return args.SOUND_MENU;
    }

    beatsMenu (args) {
        return args.BEATS;
    }

    effectsMenu (args) {
        return args.EFFECT;
    }
}

module.exports = Scratch3SoundBlocks;
<|endoftext|>


<commit_before>const MathUtil = require('../util/math-util');
const Cast = require('../util/cast');
const Clone = require('../util/clone');

/**
 * Occluded boolean value to make its use more understandable.
 * @const {boolean}
 */
const STORE_WAITING = true;

class Scratch3SoundBlocks {
    constructor (runtime) {
        /**
         * The runtime instantiating this block package.
         * @type {Runtime}
         */
        this.runtime = runtime;

        this.waitingSounds = {};

        // Clear sound effects on green flag and stop button events.
        this.stopAllSounds = this.stopAllSounds.bind(this);
        this._stopWaitingSoundsForTarget = this._stopWaitingSoundsForTarget.bind(this);
        this._clearEffectsForAllTargets = this._clearEffectsForAllTargets.bind(this);
        if (this.runtime) {
            this.runtime.on('PROJECT_STOP_ALL', this.stopAllSounds);
            this.runtime.on('PROJECT_STOP_ALL', this._clearEffectsForAllTargets);
            this.runtime.on('STOP_FOR_TARGET', this._stopWaitingSoundsForTarget);
            this.runtime.on('PROJECT_START', this._clearEffectsForAllTargets);
        }

        this._onTargetCreated = this._onTargetCreated.bind(this);
        if (this.runtime) {
            runtime.on('targetWasCreated', this._onTargetCreated);
        }
    }

    /**
     * The key to load & store a target's sound-related state.
     * @type {string}
     */
    static get STATE_KEY () {
        return 'Scratch.sound';
    }

    /**
     * The default sound-related state, to be used when a target has no existing sound state.
     * @type {SoundState}
     */
    static get DEFAULT_SOUND_STATE () {
        return {
            effects: {
                pitch: 0,
                pan: 0
            }
        };
    }

    /**
     * The minimum and maximum MIDI note numbers, for clamping the input to play note.
     * @type {{min: number, max: number}}
     */
    static get MIDI_NOTE_RANGE () {
        return {min: 36, max: 96}; // C2 to C7
    }

    /**
     * The minimum and maximum beat values, for clamping the duration of play note, play drum and rest.
     * 100 beats at the default tempo of 60bpm is 100 seconds.
     * @type {{min: number, max: number}}
     */
    static get BEAT_RANGE () {
        return {min: 0, max: 100};
    }

    /** The minimum and maximum tempo values, in bpm.
     * @type {{min: number, max: number}}
     */
    static get TEMPO_RANGE () {
        return {min: 20, max: 500};
    }

    /** The minimum and maximum values for each sound effect.
     * @type {{effect:{min: number, max: number}}}
     */
    static get EFFECT_RANGE () {
        return {
            pitch: {min: -360, max: 360}, // -3 to 3 octaves
            pan: {min: -100, max: 100} // 100% left to 100% right
        };
    }

    /**
     * @param {Target} target - collect sound state for this target.
     * @returns {SoundState} the mutable sound state associated with that target. This will be created if necessary.
     * @private
     */
    _getSoundState (target) {
        let soundState = target.getCustomState(Scratch3SoundBlocks.STATE_KEY);
        if (!soundState) {
            soundState = Clone.simple(Scratch3SoundBlocks.DEFAULT_SOUND_STATE);
            target.setCustomState(Scratch3SoundBlocks.STATE_KEY, soundState);
            target.soundEffects = soundState.effects;
        }
        return soundState;
    }

    /**
     * When a Target is cloned, clone the sound state.
     * @param {Target} newTarget - the newly created target.
     * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.
     * @listens Runtime#event:targetWasCreated
     * @private
     */
    _onTargetCreated (newTarget, sourceTarget) {
        if (sourceTarget) {
            const soundState = sourceTarget.getCustomState(Scratch3SoundBlocks.STATE_KEY);
            if (soundState && newTarget) {
                newTarget.setCustomState(Scratch3SoundBlocks.STATE_KEY, Clone.simple(soundState));
                this._syncEffectsForTarget(newTarget);
            }
        }
    }

    /**
     * Retrieve the block primitives implemented by this package.
     * @return {object.<string, Function>} Mapping of opcode to Function.
     */
    getPrimitives () {
        return {
            sound_play: this.playSound,
            sound_playuntildone: this.playSoundAndWait,
            sound_stopallsounds: this.stopAllSounds,
            sound_seteffectto: this.setEffect,
            sound_changeeffectby: this.changeEffect,
            sound_cleareffects: this.clearEffects,
            sound_sounds_menu: this.soundsMenu,
            sound_beats_menu: this.beatsMenu,
            sound_effects_menu: this.effectsMenu,
            sound_setvolumeto: this.setVolume,
            sound_changevolumeby: this.changeVolume,
            sound_volume: this.getVolume
        };
    }

    getMonitored () {
        return {
            sound_volume: {
                getId: () => 'volume'
            }
        };
    }

    playSound (args, util) {
        // Don't return the promise, it's the only difference for AndWait
        this._playSound(args, util);
    }

    playSoundAndWait (args, util) {
        return this._playSound(args, util, STORE_WAITING);
    }

    _playSound (args, util, storeWaiting) {
        const index = this._getSoundIndex(args.SOUND_MENU, util);
        if (index >= 0) {
            const {target} = util;
            const {sprite} = target;
            const {soundId} = sprite.sounds[index];
            if (sprite.soundBank) {
                if (storeWaiting === STORE_WAITING) {
                    this._addWaitingSound(target.id, soundId);
                } else {
                    this._removeWaitingSound(target.id, soundId);
                }
                return sprite.soundBank.playSound(target, soundId);
            }
        }
    }

    _addWaitingSound (targetId, soundId) {
        if (!this.waitingSounds[targetId]) {
            this.waitingSounds[targetId] = new Set();
        }
        this.waitingSounds[targetId].add(soundId);
    }

    _removeWaitingSound (targetId, soundId) {
        if (!this.waitingSounds[targetId]) {
            return;
        }
        this.waitingSounds[targetId].delete(soundId);
    }

    _getSoundIndex (soundName, util) {
        // if the sprite has no sounds, return -1
        const len = util.target.sprite.sounds.length;
        if (len === 0) {
            return -1;
        }

        // look up by name first
        const index = this.getSoundIndexByName(soundName, util);
        if (index !== -1) {
            return index;
        }

        // then try using the sound name as a 1-indexed index
        const oneIndexedIndex = parseInt(soundName, 10);
        if (!isNaN(oneIndexedIndex)) {
            return MathUtil.wrapClamp(oneIndexedIndex - 1, 0, len - 1);
        }

        // could not be found as a name or converted to index, return -1
        return -1;
    }

    getSoundIndexByName (soundName, util) {
        const sounds = util.target.sprite.sounds;
        for (let i = 0; i < sounds.length; i++) {
            if (sounds[i].name === soundName) {
                return i;
            }
        }
        // if there is no sound by that name, return -1
        return -1;
    }

    stopAllSounds () {
        if (this.runtime.targets === null) return;
        const allTargets = this.runtime.targets;
        for (let i = 0; i < allTargets.length; i++) {
            this._stopAllSoundsForTarget(allTargets[i]);
        }
    }

    _stopAllSoundsForTarget (target) {
        if (target.sprite.soundBank) {
            target.sprite.soundBank.stopAllSounds(target);
            if (this.waitingSounds[target.id]) {
                this.waitingSounds[target.id].clear();
            }
        }
    }

    _stopWaitingSoundsForTarget (target) {
        if (target.sprite.soundBank) {
            if (this.waitingSounds[target.id]) {
                for (const soundId of this.waitingSounds[target.id].values()) {
                    target.sprite.soundBank.stop(target, soundId);
                }
                this.waitingSounds[target.id].clear();
            }
        }
    }

    setEffect (args, util) {
        return this._updateEffect(args, util, false);
    }

    changeEffect (args, util) {
        return this._updateEffect(args, util, true);
    }

    _updateEffect (args, util, change) {
        const effect = Cast.toString(args.EFFECT).toLowerCase();
        const value = Cast.toNumber(args.VALUE);

        const soundState = this._getSoundState(util.target);
        if (!soundState.effects.hasOwnProperty(effect)) return;

        if (change) {
            soundState.effects[effect] += value;
        } else {
            soundState.effects[effect] = value;
        }

        const {min, max} = Scratch3SoundBlocks.EFFECT_RANGE[effect];
        soundState.effects[effect] = MathUtil.clamp(soundState.effects[effect], min, max);

        this._syncEffectsForTarget(util.target);
        // Yield until the next tick.
        return Promise.resolve();
    }

    _syncEffectsForTarget (target) {
        if (!target || !target.sprite.soundBank) return;
        target.soundEffects = this._getSoundState(target).effects;

        target.sprite.soundBank.setEffects(target);
    }

    clearEffects (args, util) {
        this._clearEffectsForTarget(util.target);
    }

    _clearEffectsForTarget (target) {
        const soundState = this._getSoundState(target);
        for (const effect in soundState.effects) {
            if (!soundState.effects.hasOwnProperty(effect)) continue;
            soundState.effects[effect] = 0;
        }
        this._syncEffectsForTarget(target);
    }

    _clearEffectsForAllTargets () {
        if (this.runtime.targets === null) return;
        const allTargets = this.runtime.targets;
        for (let i = 0; i < allTargets.length; i++) {
            this._clearEffectsForTarget(allTargets[i]);
        }
    }

    setVolume (args, util) {
        const volume = Cast.toNumber(args.VOLUME);
        return this._updateVolume(volume, util);
    }

    changeVolume (args, util) {
        const volume = Cast.toNumber(args.VOLUME) + util.target.volume;
        return this._updateVolume(volume, util);
    }

    _updateVolume (volume, util) {
        volume = MathUtil.clamp(volume, 0, 100);
        util.target.volume = volume;
        this._syncEffectsForTarget(util.target);

        // Yield until the next tick.
        return Promise.resolve();
    }

    getVolume (args, util) {
        return util.target.volume;
    }

    soundsMenu (args) {
        return args.SOUND_MENU;
    }

    beatsMenu (args) {
        return args.BEATS;
    }

    effectsMenu (args) {
        return args.EFFECT;
    }
}

module.exports = Scratch3SoundBlocks;
<commit_msg>Make volume a sprite-specific monitor (#1730)<commit_after>const MathUtil = require('../util/math-util');
const Cast = require('../util/cast');
const Clone = require('../util/clone');

/**
 * Occluded boolean value to make its use more understandable.
 * @const {boolean}
 */
const STORE_WAITING = true;

class Scratch3SoundBlocks {
    constructor (runtime) {
        /**
         * The runtime instantiating this block package.
         * @type {Runtime}
         */
        this.runtime = runtime;

        this.waitingSounds = {};

        // Clear sound effects on green flag and stop button events.
        this.stopAllSounds = this.stopAllSounds.bind(this);
        this._stopWaitingSoundsForTarget = this._stopWaitingSoundsForTarget.bind(this);
        this._clearEffectsForAllTargets = this._clearEffectsForAllTargets.bind(this);
        if (this.runtime) {
            this.runtime.on('PROJECT_STOP_ALL', this.stopAllSounds);
            this.runtime.on('PROJECT_STOP_ALL', this._clearEffectsForAllTargets);
            this.runtime.on('STOP_FOR_TARGET', this._stopWaitingSoundsForTarget);
            this.runtime.on('PROJECT_START', this._clearEffectsForAllTargets);
        }

        this._onTargetCreated = this._onTargetCreated.bind(this);
        if (this.runtime) {
            runtime.on('targetWasCreated', this._onTargetCreated);
        }
    }

    /**
     * The key to load & store a target's sound-related state.
     * @type {string}
     */
    static get STATE_KEY () {
        return 'Scratch.sound';
    }

    /**
     * The default sound-related state, to be used when a target has no existing sound state.
     * @type {SoundState}
     */
    static get DEFAULT_SOUND_STATE () {
        return {
            effects: {
                pitch: 0,
                pan: 0
            }
        };
    }

    /**
     * The minimum and maximum MIDI note numbers, for clamping the input to play note.
     * @type {{min: number, max: number}}
     */
    static get MIDI_NOTE_RANGE () {
        return {min: 36, max: 96}; // C2 to C7
    }

    /**
     * The minimum and maximum beat values, for clamping the duration of play note, play drum and rest.
     * 100 beats at the default tempo of 60bpm is 100 seconds.
     * @type {{min: number, max: number}}
     */
    static get BEAT_RANGE () {
        return {min: 0, max: 100};
    }

    /** The minimum and maximum tempo values, in bpm.
     * @type {{min: number, max: number}}
     */
    static get TEMPO_RANGE () {
        return {min: 20, max: 500};
    }

    /** The minimum and maximum values for each sound effect.
     * @type {{effect:{min: number, max: number}}}
     */
    static get EFFECT_RANGE () {
        return {
            pitch: {min: -360, max: 360}, // -3 to 3 octaves
            pan: {min: -100, max: 100} // 100% left to 100% right
        };
    }

    /**
     * @param {Target} target - collect sound state for this target.
     * @returns {SoundState} the mutable sound state associated with that target. This will be created if necessary.
     * @private
     */
    _getSoundState (target) {
        let soundState = target.getCustomState(Scratch3SoundBlocks.STATE_KEY);
        if (!soundState) {
            soundState = Clone.simple(Scratch3SoundBlocks.DEFAULT_SOUND_STATE);
            target.setCustomState(Scratch3SoundBlocks.STATE_KEY, soundState);
            target.soundEffects = soundState.effects;
        }
        return soundState;
    }

    /**
     * When a Target is cloned, clone the sound state.
     * @param {Target} newTarget - the newly created target.
     * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.
     * @listens Runtime#event:targetWasCreated
     * @private
     */
    _onTargetCreated (newTarget, sourceTarget) {
        if (sourceTarget) {
            const soundState = sourceTarget.getCustomState(Scratch3SoundBlocks.STATE_KEY);
            if (soundState && newTarget) {
                newTarget.setCustomState(Scratch3SoundBlocks.STATE_KEY, Clone.simple(soundState));
                this._syncEffectsForTarget(newTarget);
            }
        }
    }

    /**
     * Retrieve the block primitives implemented by this package.
     * @return {object.<string, Function>} Mapping of opcode to Function.
     */
    getPrimitives () {
        return {
            sound_play: this.playSound,
            sound_playuntildone: this.playSoundAndWait,
            sound_stopallsounds: this.stopAllSounds,
            sound_seteffectto: this.setEffect,
            sound_changeeffectby: this.changeEffect,
            sound_cleareffects: this.clearEffects,
            sound_sounds_menu: this.soundsMenu,
            sound_beats_menu: this.beatsMenu,
            sound_effects_menu: this.effectsMenu,
            sound_setvolumeto: this.setVolume,
            sound_changevolumeby: this.changeVolume,
            sound_volume: this.getVolume
        };
    }

    getMonitored () {
        return {
            sound_volume: {
                isSpriteSpecific: true,
                getId: targetId => `${targetId}_volume`
            }
        };
    }

    playSound (args, util) {
        // Don't return the promise, it's the only difference for AndWait
        this._playSound(args, util);
    }

    playSoundAndWait (args, util) {
        return this._playSound(args, util, STORE_WAITING);
    }

    _playSound (args, util, storeWaiting) {
        const index = this._getSoundIndex(args.SOUND_MENU, util);
        if (index >= 0) {
            const {target} = util;
            const {sprite} = target;
            const {soundId} = sprite.sounds[index];
            if (sprite.soundBank) {
                if (storeWaiting === STORE_WAITING) {
                    this._addWaitingSound(target.id, soundId);
                } else {
                    this._removeWaitingSound(target.id, soundId);
                }
                return sprite.soundBank.playSound(target, soundId);
            }
        }
    }

    _addWaitingSound (targetId, soundId) {
        if (!this.waitingSounds[targetId]) {
            this.waitingSounds[targetId] = new Set();
        }
        this.waitingSounds[targetId].add(soundId);
    }

    _removeWaitingSound (targetId, soundId) {
        if (!this.waitingSounds[targetId]) {
            return;
        }
        this.waitingSounds[targetId].delete(soundId);
    }

    _getSoundIndex (soundName, util) {
        // if the sprite has no sounds, return -1
        const len = util.target.sprite.sounds.length;
        if (len === 0) {
            return -1;
        }

        // look up by name first
        const index = this.getSoundIndexByName(soundName, util);
        if (index !== -1) {
            return index;
        }

        // then try using the sound name as a 1-indexed index
        const oneIndexedIndex = parseInt(soundName, 10);
        if (!isNaN(oneIndexedIndex)) {
            return MathUtil.wrapClamp(oneIndexedIndex - 1, 0, len - 1);
        }

        // could not be found as a name or converted to index, return -1
        return -1;
    }

    getSoundIndexByName (soundName, util) {
        const sounds = util.target.sprite.sounds;
        for (let i = 0; i < sounds.length; i++) {
            if (sounds[i].name === soundName) {
                return i;
            }
        }
        // if there is no sound by that name, return -1
        return -1;
    }

    stopAllSounds () {
        if (this.runtime.targets === null) return;
        const allTargets = this.runtime.targets;
        for (let i = 0; i < allTargets.length; i++) {
            this._stopAllSoundsForTarget(allTargets[i]);
        }
    }

    _stopAllSoundsForTarget (target) {
        if (target.sprite.soundBank) {
            target.sprite.soundBank.stopAllSounds(target);
            if (this.waitingSounds[target.id]) {
                this.waitingSounds[target.id].clear();
            }
        }
    }

    _stopWaitingSoundsForTarget (target) {
        if (target.sprite.soundBank) {
            if (this.waitingSounds[target.id]) {
                for (const soundId of this.waitingSounds[target.id].values()) {
                    target.sprite.soundBank.stop(target, soundId);
                }
                this.waitingSounds[target.id].clear();
            }
        }
    }

    setEffect (args, util) {
        return this._updateEffect(args, util, false);
    }

    changeEffect (args, util) {
        return this._updateEffect(args, util, true);
    }

    _updateEffect (args, util, change) {
        const effect = Cast.toString(args.EFFECT).toLowerCase();
        const value = Cast.toNumber(args.VALUE);

        const soundState = this._getSoundState(util.target);
        if (!soundState.effects.hasOwnProperty(effect)) return;

        if (change) {
            soundState.effects[effect] += value;
        } else {
            soundState.effects[effect] = value;
        }

        const {min, max} = Scratch3SoundBlocks.EFFECT_RANGE[effect];
        soundState.effects[effect] = MathUtil.clamp(soundState.effects[effect], min, max);

        this._syncEffectsForTarget(util.target);
        // Yield until the next tick.
        return Promise.resolve();
    }

    _syncEffectsForTarget (target) {
        if (!target || !target.sprite.soundBank) return;
        target.soundEffects = this._getSoundState(target).effects;

        target.sprite.soundBank.setEffects(target);
    }

    clearEffects (args, util) {
        this._clearEffectsForTarget(util.target);
    }

    _clearEffectsForTarget (target) {
        const soundState = this._getSoundState(target);
        for (const effect in soundState.effects) {
            if (!soundState.effects.hasOwnProperty(effect)) continue;
            soundState.effects[effect] = 0;
        }
        this._syncEffectsForTarget(target);
    }

    _clearEffectsForAllTargets () {
        if (this.runtime.targets === null) return;
        const allTargets = this.runtime.targets;
        for (let i = 0; i < allTargets.length; i++) {
            this._clearEffectsForTarget(allTargets[i]);
        }
    }

    setVolume (args, util) {
        const volume = Cast.toNumber(args.VOLUME);
        return this._updateVolume(volume, util);
    }

    changeVolume (args, util) {
        const volume = Cast.toNumber(args.VOLUME) + util.target.volume;
        return this._updateVolume(volume, util);
    }

    _updateVolume (volume, util) {
        volume = MathUtil.clamp(volume, 0, 100);
        util.target.volume = volume;
        this._syncEffectsForTarget(util.target);

        // Yield until the next tick.
        return Promise.resolve();
    }

    getVolume (args, util) {
        return util.target.volume;
    }

    soundsMenu (args) {
        return args.SOUND_MENU;
    }

    beatsMenu (args) {
        return args.BEATS;
    }

    effectsMenu (args) {
        return args.EFFECT;
    }
}

module.exports = Scratch3SoundBlocks;
<|endoftext|>


<commit_before>var ip = require('../../util/ip');
var path = require('path');
var sdkVersion = require('../../../package.json').version;
var deviceTypes = require('../../util/deviceTypes');

exports.getConfig = function(app, argv) {
  // NOTICE: we don't change the argv object
  var config = {};

  // keep a copy of the original argv unmodified
  config.argv = JSON.parse(JSON.stringify(argv));

  // app base directory
  config.appPath = app.paths.root;

  // supply some defaults
  config.debug = 'debug' in argv ? !!argv.debug : true;
  config.scheme = argv.scheme || (config.debug ? 'debug' : 'release');
  config.target = argv.target || 'browser-mobile';
  config.imports = [];
  config.schemePath = path.join('build/', config.scheme);
  config.outputPath = argv.output || path.resolve(config.appPath, path.join('build/', config.scheme, config.target));
  config.jsioPath = Array.isArray(argv.jsioPath) ? argv.jsioPath.slice(0) : [];
  config.clientPaths = {};
  config.sdkVersion = sdkVersion;
  config.isTestApp = argv.isTestApp;
  config.isSimulated = argv.simulated;
  config.simulator = {
    deviceId: argv.simulateDeviceId,
    deviceType: argv.simulateDeviceType,
    deviceInfo: deviceTypes[argv.simulateDeviceType] || {},
    port: argv.port,
    modules: []
  };

  config.spriteImages = 'sprite-images' in argv ? !!argv['sprite-images']
                                                : !config.isSimulated;

  var serverName;
  if (config.isSimulated) {
    // local for serving
    // use window.location + path to basil server proxy
    serverName = 'local';
  } else if (config.argv && argv.server) {
    serverName = config.argv && argv.server;
  } else if (/^browser-/.test(config.target)) {
    // we don't want to set the host:port here - just use window.location
    serverName = 'inherit';
  } else if (config.debug) {
    serverName = 'local';
  } else {
    serverName = 'production';
  }

  config.ip = argv.ip || ip.getLocalIP()[0];

  if (serverName == 'local') {
    config.localServerURL = argv.localServerURL || (config.ip + ':' + (argv.port || 9200));
  }

  config.serverName = serverName;

  // Note: may be overwritten later, e.g. for native builds
  config.outputResourcePath = config.outputPath;

  // studio domain name from manifest under studio.domain
  var studio = app.manifest.studio;
  config.studio = studio && studio.domain || 'gameclosure.com';

  // build version from command line or manifest
  config.version = argv.version || config.scheme == 'debug' && 'debug' || app.manifest.version || '';

  // build target
  config.target = argv.target;

  // enable JavaScript compression
  config.compress = !!argv.compress;

  // enable JavaScript compression
  if ('compress' in argv) {
    config.compress = !!argv.compress;
  } else {
    config.compress = config.scheme == 'release';
  }

  // useful for simulated builds with --single-port
  if (argv.baseURL) {
    config.baseURL = argv.baseURL;
  }

  return config;
};
<commit_msg>use a different flag to not conflict with devkit-core pre v4<commit_after>var ip = require('../../util/ip');
var path = require('path');
var sdkVersion = require('../../../package.json').version;
var deviceTypes = require('../../util/deviceTypes');

exports.getConfig = function(app, argv) {
  // NOTICE: we don't change the argv object
  var config = {};

  // keep a copy of the original argv unmodified
  config.argv = JSON.parse(JSON.stringify(argv));

  // app base directory
  config.appPath = app.paths.root;

  // supply some defaults
  config.debug = 'debug' in argv ? !!argv.debug : true;
  config.scheme = argv.scheme || (config.debug ? 'debug' : 'release');
  config.target = argv.target || 'browser-mobile';
  config.imports = [];
  config.schemePath = path.join('build/', config.scheme);
  config.outputPath = argv.output || path.resolve(config.appPath, path.join('build/', config.scheme, config.target));
  config.jsioPath = Array.isArray(argv.jsioPath) ? argv.jsioPath.slice(0) : [];
  config.clientPaths = {};
  config.sdkVersion = sdkVersion;
  config.isTestApp = argv.isTestApp;
  config.isSimulated = argv.simulated;
  config.simulator = {
    deviceId: argv.simulateDeviceId,
    deviceType: argv.simulateDeviceType,
    deviceInfo: deviceTypes[argv.simulateDeviceType] || {},
    port: argv.port,
    modules: []
  };

  // devkit-core v3 requires this to be true
  config.spriteImages = true;

  // devkit-core v4 enable/disable spriter
  config.createSpritesheets = 'sprite-images' in argv ? !!argv['sprite-images']
                                                : !config.isSimulated;

  var serverName;
  if (config.isSimulated) {
    // local for serving
    // use window.location + path to basil server proxy
    serverName = 'local';
  } else if (config.argv && argv.server) {
    serverName = config.argv && argv.server;
  } else if (/^browser-/.test(config.target)) {
    // we don't want to set the host:port here - just use window.location
    serverName = 'inherit';
  } else if (config.debug) {
    serverName = 'local';
  } else {
    serverName = 'production';
  }

  config.ip = argv.ip || ip.getLocalIP()[0];

  if (serverName == 'local') {
    config.localServerURL = argv.localServerURL || (config.ip + ':' + (argv.port || 9200));
  }

  config.serverName = serverName;

  // Note: may be overwritten later, e.g. for native builds
  config.outputResourcePath = config.outputPath;

  // studio domain name from manifest under studio.domain
  var studio = app.manifest.studio;
  config.studio = studio && studio.domain || 'gameclosure.com';

  // build version from command line or manifest
  config.version = argv.version || config.scheme == 'debug' && 'debug' || app.manifest.version || '';

  // build target
  config.target = argv.target;

  // enable JavaScript compression
  config.compress = !!argv.compress;

  // enable JavaScript compression
  if ('compress' in argv) {
    config.compress = !!argv.compress;
  } else {
    config.compress = config.scheme == 'release';
  }

  // useful for simulated builds with --single-port
  if (argv.baseURL) {
    config.baseURL = argv.baseURL;
  }

  return config;
};
<|endoftext|>


<commit_before>

$(document).ready(function() {

    $(".makeMeDraggable").draggable({

$(document).ready(function() {

    $(".makeMeDraggable").draggable({
      revert: "invalid",

    	snap: '.makeMeDroppable'
    });

    $(".makeMeDroppable").droppable({
      drop: handleDropEvent,
    });

  function handleDropEvent( event, ui ) {
    var draggable = ui.draggable;
  }
});
<commit_msg>fixed js file<commit_after>$(document).ready(function() {

    $(".makeMeDraggable").draggable({
      revert: "invalid",

    	snap: '.makeMeDroppable'
    });

    $(".makeMeDroppable").droppable({
      drop: handleDropEvent,
    });

  function handleDropEvent( event, ui ) {
    var draggable = ui.draggable;
  }
});
<|endoftext|>


<commit_before>$(document).on("ready turbolinks:load", () => {
  if ($(".quick-datetime-select").length > 0) {
    $(".quick-datetime-select").quickDatetimeSelector({
      locale: {
        Today: "오늘",
        Tomorrow: "내일",
        ThisWeek: "이번 주",
        NextWeek: "다음 주",
        day: {
          Sunday: "일요일",
          Monday: "월요일",
          Tuesday: "화요일",
          Wednesday: "수요일",
          Thursday: "목요일",
          Friday: "금요일",
          Saturday: "토요일",
        },
      },
    })
  }

  if ($("#new_event_form").length) {
    // Setup current timezone
    const $timezone = $("#event_form_timezone_offset")
    const browserOffset = (new Date()).getTimezoneOffset()
    const offset = $timezone.find(`option[value=${browserOffset}]`).val()
    const defaultOffset = 540
    $timezone.val(offset)

    const getDate = () => {
      const year = Number($("#event_start_time_1i").val())
      const month = Number($("#event_start_time_2i").val())
      const day = Number($("#event_start_time_3i").val())
      const hour = Number($("#event_start_time_4i").val())
      const min = Number($("#event_start_time_5i").val())

      return new Date(year, month, day, hour, min)
    }

    const setDate = (date) => {
      debugger
      let newMin = Math.floor(date.getMinutes() / 10) * 10
      if (newMin === 0) { newMin = "00" }
      let newHour = date.getHours()
      if (newHour < 10) { newHour = `0${newHour}` }

      $("#event_form_start_time_1i").val(date.getFullYear())
      $("#event_form_start_time_2i").val(date.getMonth())
      $("#event_form_start_time_3i").val(date.getDate())
      $("#event_form_start_time_4i").val(newHour)
      $("#event_form_start_time_5i").val(newMin)
    }

    const manipulateDate = (minOffset) => {
      const date = getDate()
      const min = date.getMinutes()

      date.setMinutes(min + minOffset)
      setDate(date)
    }

    // Init start_time with local time
    const now = new Date()
    now.setMinutes(0)
    now.setHours(now.getHours() + 1)
    setDate(now)

    // Transform UTC before submit
    $(".event-form").submit((event) => {
      manipulateDate(Number(minOffset) + defaultOffset)
      return true
    })
  }

  if ($(".summernote").length !== 0) {
    $(".summernote").summernote({ height: 300 })
  }
})
<commit_msg>Bug fix. month will use Date.prototype.getMonth + 1 (#198)<commit_after>$(document).on("ready turbolinks:load", () => {
  if ($(".quick-datetime-select").length > 0) {
    $(".quick-datetime-select").quickDatetimeSelector({
      locale: {
        Today: "오늘",
        Tomorrow: "내일",
        ThisWeek: "이번 주",
        NextWeek: "다음 주",
        day: {
          Sunday: "일요일",
          Monday: "월요일",
          Tuesday: "화요일",
          Wednesday: "수요일",
          Thursday: "목요일",
          Friday: "금요일",
          Saturday: "토요일",
        },
      },
    })
  }

  if ($("#new_event_form").length) {
    // Setup current timezone
    const $timezone = $("#event_form_timezone_offset")
    const browserOffset = (new Date()).getTimezoneOffset()
    const offset = $timezone.find(`option[value=${browserOffset}]`).val()
    const defaultOffset = 540
    $timezone.val(offset)

    const getDate = () => {
      const year = Number($("#event_start_time_1i").val())
      const month = Number($("#event_start_time_2i").val())
      const day = Number($("#event_start_time_3i").val())
      const hour = Number($("#event_start_time_4i").val())
      const min = Number($("#event_start_time_5i").val())

      return new Date(year, month, day, hour, min)
    }

    const setDate = (date) => {
      let newMin = Math.floor(date.getMinutes() / 10) * 10
      if (newMin === 0) { newMin = "00" }
      let newHour = date.getHours()
      if (newHour < 10) { newHour = `0${newHour}` }

      $("#event_form_start_time_1i").val(date.getFullYear())
      $("#event_form_start_time_2i").val(date.getMonth() + 1)
      $("#event_form_start_time_3i").val(date.getDate())
      $("#event_form_start_time_4i").val(newHour)
      $("#event_form_start_time_5i").val(newMin)
    }

    const manipulateDate = (minOffset) => {
      const date = getDate()
      const min = date.getMinutes()

      date.setMinutes(min + minOffset)
      setDate(date)
    }

    // Init start_time with local time
    const now = new Date()
    now.setMinutes(0)
    now.setHours(now.getHours() + 1)
    setDate(now)

    // Transform UTC before submit
    $(".event-form").submit((event) => {
      manipulateDate(defaultOffset)
      return true
    })
  }

  if ($(".summernote").length !== 0) {
    $(".summernote").summernote({ height: 300 })
  }
})
<|endoftext|>


<commit_before>/**
 * Application entry point.
 */
require([
  'jquery',
  'underscore',
  'Class',
  'backbone',
  'mps',
  'landing/views/SpinnerView',
  'landing/views/SlideView',
  'landing/views/StoriesView',
  'landing/views/HeaderView',
  'landing/views/FooterView',
  'views/DialogView',
  '_string'
], function($, _, Class, Backbone, mps, SpinnerView, SlideView, StoriesView, HeaderView, FooterView, DialogView) {
  'use strict';

  var LandingPage = Class.extend({

    $el: $('body'),

    init: function() {
      this._initViews();
    },

    /**
     * Initialize Application Views.
     */
    _initViews: function() {
      //shared
      //new HeaderView();
      new FooterView();
      new SpinnerView();

      //landing
      new SlideView();
      new StoriesView();
    }
  });

  new LandingPage();

});
<commit_msg>test deploy IX<commit_after>/**
 * Application entry point.
 */
require([
  'jquery',
  'underscore',
  'Class',
  'backbone',
  'mps',
  'landing/views/SpinnerView',
  'landing/views/SlideView',
  'landing/views/StoriesView',
  'landing/views/HeaderView',
  'landing/views/FooterView',
  'views/DialogView',
  '_string'
], function($, _, Class, Backbone, mps, SpinnerView, SlideView, StoriesView, HeaderView, FooterView, DialogView) {
  'use strict';

  var LandingPage = Class.extend({

    $el: $('body'),

    init: function() {
      this._initViews();
    },

    /**
     * Initialize Application Views.
     */
    _initViews: function() {
      //shared
      new HeaderView();
      new FooterView();
      //new SpinnerView();

      //landing
      new SlideView();
      new StoriesView();
    }
  });

  new LandingPage();

});
<|endoftext|>


<commit_before>function display_flash(message, notice) {
  if (typeof notice === 'undefined') { notice = true; }  //par défaut notice = true

  $('not_flash').hide();
  $('flash').firstChild.style.color = notice ? 'green' : 'red';
  $('flash').firstChild.update(message);
  $('flash').show();

  $('flash').addEventListener('mouseout', function(e) {
    e.preventDefault();
    $('flash').hide();
    $('not_flash').show();
  }, false);
};<commit_msg>Le message flash disparait automatiquement au bout de 5 secondes<commit_after>function hide_flash() {
  $('flash').hide();
  $('not_flash').show();
}

function display_flash(message, notice) {
  if (typeof notice === 'undefined') { notice = true; }  //par défaut notice = true

  $('not_flash').hide();
  $('flash').firstChild.style.color = notice ? 'green' : 'red';
  $('flash').firstChild.update(message);
  $('flash').show();

  $('flash').addEventListener('mouseover', function(e) {
    e.preventDefault();
    hide_flash();
  }, false);

  setTimeout(function() {
    hide_flash();
  }, 5000);  // on efface le flash automatiquement au bout de 5 secondes
};
<|endoftext|>


<commit_before>(function(root) {

  var ProfileChartData = function initializeProfileChartData (name, data) {
    this.name = name;
    this.data = data;
  };

  ProfileChartData.prototype.highChartDates = function profileHighChartDates() {
    return this.data.map(function(element) {
      return [
        Date.UTC(element[0],
                 element[1] - 1,   // JavaScript months start with 0, Ruby months start with 1
                 element[2]),
        element[3]
      ];
    });
  }

  ProfileChartData.prototype.toChart = function toChart() {
    return {
      name: this.name,
      data: this.data
    };
  };

  ProfileChartData.prototype.toDateChart = function toDateChart() {
    return {
      name: this.name,
      data: this.highChartDates()
    };
  }

  var ProfileController = function initializeProfileController () {
    this.options = $.extend({}, ChartSettings.base_options);
  };

  ProfileController.prototype.chartData = function profileChartDataData (data_type) {
    return $("#chart-data").data(data_type);
  };

  ProfileController.prototype.studentName = function storeStudentName () {
    return $("#student-name").text();
  };

  ProfileController.prototype.renderChartOrEmptyView = function profileRenderChartOrEmptyView (chart) {
    if (EmptyView.canRender(chart)) {
      new EmptyView(this.studentName, chart.title).render();
    } else {
      new Highcharts.Chart(chart.toHighChart());
    }
  }

  ProfileController.prototype.setChart = function profileSetChart (value) {
    var chart_data = $("#chart-data");
    if (value == "attendance") {
      AttendanceChart.fromChartData(chart_data).render(this);
      return;
    } else if (value == "behavior") {
      BehaviorChart.fromChartData(chart_data).render(this);
      return;
    } else if (value == "mcas-growth") {
      McasGrowthChart.fromChartData(chart_data).render(this);
      return;
    } else if (value == "mcas-scaled") {
      McasScaledChart.fromChartData(chart_data).render(this);
      return;
    } else if (value == "star") {
      StarChart.fromChartData(chart_data).render(this);
      return;
    }
  }

  ProfileController.prototype.onChartChange = function profileChartDataChange() {
    var selVal = $("#chart-type").val();
    this.setChart(selVal);
  }

  ProfileController.prototype.show = function renderProfileController() {
    var chart_start = $("#chart-start").data('start');
    var chart_options = ["attendance", "behavior", "mcas-growth", "mcas-scaled", "star"];
    if (chart_options.indexOf(chart_start) !== -1) {
      this.setChart(chart_start);
    } else {
      this.setChart("mcas-growth");
    }

    // Bind onChartChange to this instance of ProfileController
    var self = this;
    $("#chart-type").on('change', function() {
      self.onChartChange();
    });
  }

  root.ProfileController = ProfileController;
  root.ProfileChartData = ProfileChartData;

})(window)

$(function() {
  if ($('body').hasClass('students') && $('body').hasClass('show')) {
    new window.ProfileController().show();

    // Risk level tooltip
    var risk_level_tooltip = $('#risk-level-tooltip-template').html();
    var rendered = Mustache.render(risk_level_tooltip);

    $('#profile-risk-level').tooltipster({
      content: rendered,
      position: 'bottom-right',
      contentAsHTML: true
    });

    $(".datepicker").datepicker({
      showOn: "button",
      buttonImage: $("#calendar-icon-path").data('path'),
      buttonImageOnly: true,
      buttonText: "Select date"
    });

    // Hide Add Intervention form on page load
    $("#new_intervention").hide();

    $("#open-intervention-form").click(function() {
      $("#new_intervention").slideDown();
      $(this).slideUp();
    });

    $("#close-intervention-form").click(function() {
      $("#new_intervention").slideUp();
      $("#open-intervention-form").slideDown();
    });
  }
});
<commit_msg>Remove unused function<commit_after>(function(root) {

  var ProfileChartData = function initializeProfileChartData (name, data) {
    this.name = name;
    this.data = data;
  };

  ProfileChartData.prototype.highChartDates = function profileHighChartDates() {
    return this.data.map(function(element) {
      return [
        Date.UTC(element[0],
                 element[1] - 1,   // JavaScript months start with 0, Ruby months start with 1
                 element[2]),
        element[3]
      ];
    });
  }

  ProfileChartData.prototype.toChart = function toChart() {
    return {
      name: this.name,
      data: this.data
    };
  };

  ProfileChartData.prototype.toDateChart = function toDateChart() {
    return {
      name: this.name,
      data: this.highChartDates()
    };
  }

  var ProfileController = function initializeProfileController () {
    this.options = $.extend({}, ChartSettings.base_options);
  };

  ProfileController.prototype.studentName = function storeStudentName () {
    return $("#student-name").text();
  };

  ProfileController.prototype.renderChartOrEmptyView = function profileRenderChartOrEmptyView (chart) {
    if (EmptyView.canRender(chart)) {
      new EmptyView(this.studentName, chart.title).render();
    } else {
      new Highcharts.Chart(chart.toHighChart());
    }
  }

  ProfileController.prototype.setChart = function profileSetChart (value) {
    var chart_data = $("#chart-data");
    if (value == "attendance") {
      AttendanceChart.fromChartData(chart_data).render(this);
      return;
    } else if (value == "behavior") {
      BehaviorChart.fromChartData(chart_data).render(this);
      return;
    } else if (value == "mcas-growth") {
      McasGrowthChart.fromChartData(chart_data).render(this);
      return;
    } else if (value == "mcas-scaled") {
      McasScaledChart.fromChartData(chart_data).render(this);
      return;
    } else if (value == "star") {
      StarChart.fromChartData(chart_data).render(this);
      return;
    }
  }

  ProfileController.prototype.onChartChange = function profileChartDataChange() {
    var selVal = $("#chart-type").val();
    this.setChart(selVal);
  }

  ProfileController.prototype.show = function renderProfileController() {
    var chart_start = $("#chart-start").data('start');
    var chart_options = ["attendance", "behavior", "mcas-growth", "mcas-scaled", "star"];
    if (chart_options.indexOf(chart_start) !== -1) {
      this.setChart(chart_start);
    } else {
      this.setChart("mcas-growth");
    }

    // Bind onChartChange to this instance of ProfileController
    var self = this;
    $("#chart-type").on('change', function() {
      self.onChartChange();
    });
  }

  root.ProfileController = ProfileController;
  root.ProfileChartData = ProfileChartData;

})(window)

$(function() {
  if ($('body').hasClass('students') && $('body').hasClass('show')) {
    new window.ProfileController().show();

    // Risk level tooltip
    var risk_level_tooltip = $('#risk-level-tooltip-template').html();
    var rendered = Mustache.render(risk_level_tooltip);

    $('#profile-risk-level').tooltipster({
      content: rendered,
      position: 'bottom-right',
      contentAsHTML: true
    });

    $(".datepicker").datepicker({
      showOn: "button",
      buttonImage: $("#calendar-icon-path").data('path'),
      buttonImageOnly: true,
      buttonText: "Select date"
    });

    // Hide Add Intervention form on page load
    $("#new_intervention").hide();

    $("#open-intervention-form").click(function() {
      $("#new_intervention").slideDown();
      $(this).slideUp();
    });

    $("#close-intervention-form").click(function() {
      $("#new_intervention").slideUp();
      $("#open-intervention-form").slideDown();
    });
  }
});
<|endoftext|>


<commit_before>
$(document).bind("ajax:complete", function(event,xhr,status){
  if($("#submit_note").length > 0) {
    $('#note_title').val('');
    $('#note_address').val('');
    $('#note_body').val('');
    $('#note_url').val('');
    $('#note_tag_list').val(destination);
  }
});

<commit_msg>added sticky note function back in<commit_after>
$(document).ready(function(){
  $(function(){
    var stickyHeaderTop = $('.ramble-notes').offset().top;
    $(window).scroll(function(){
      if( $(window).scrollTop() > stickyHeaderTop ) {
        $('.ramble-notes').css({position: 'fixed', top: '0px'});
        $('.end').css('display', 'block');
      } else {
        $('.ramble-notes').css({position: 'static', top: '0px'});
        $('.end').css('display', 'none');
      }
    });
  });
});

$(document).bind("ajax:complete", function(event,xhr,status){
  if($("#submit_note").length > 0) {
    $('#note_title').val('');
    $('#note_address').val('');
    $('#note_body').val('');
    $('#note_url').val('');
    $('#note_tag_list').val(destination);
  }
});

<|endoftext|>