<commit_before>class SimpleSprite {
    constructor(animation, x, y, vx = 0, vy = 0, xScale = 1, yScale = 1) {
        this.animation = animation;
        this.x = x;
        this.y = y;
        this.vx = vx;
        this.vy = vy;
        this.rotation = 0;
        this.rotationPerFrame = 0;
        this.homing = false;
        this.target = {x: 0, y: 0};
        this.acceleration = 0;
        this.maxSpeed = 0;
        this.currentFrame = 0;
        this.framesToLive = 200;
        this.msBetweenFrames = 200;
        this.hasContrail = false;
        this.framesBetweenContrailParticles = 0; //game frames, not animation frames.
        this.contrailTimer = 0;
        this.contrailParticles = [];
        // I needed these because the graphic I wanted to use faced up and I need it to face down,
        // which I can get by using yScale = -1
        this.xScale = xScale;
        this.yScale = yScale;
        this.xScaleMax = xScale;
        this.xScaleMin = xScale;
        this.yScaleMax = yScale;
        this.yScaleMin = yScale;
        this.xScalePerFrame = 0;
        this.yScalePerFrame = 0;
        this.scaleOscillation = false;
        this.xScaleWaxing = false;
        this.yScaleWaxing = false;
    }
}

// This function is called every update and controls updating the local sprite, which for now just means:
// update position according to velocity.
// updating the current frame
// removing the object
function updateLocalSprite(localSprite) {
    if (localSprite.homing) { //homing behavior
        var homerToTargetX,  //these two vars are the difference, in x/y values, between the homer's position and its target's position,
        homerToTargetY,        //phrased so that they could be added to the homer's coordinates in order to overlap the target's.
        normalizedHomerToTargetXRatio,    //these two vars will turn that vector into a normalized ratio. Actually, sometimes one of the numbers is over 1 right now, but it still works, I *think.*
        normalizedHomerToTargetYRatio,
        homerX = localSprite.x,
        homerY = localSprite.y,
        targetX = localSprite.target.x,
        targetY = localSprite.target.y,
        dx,
        dy;
        //finding values for homerToTargetX and homerToTargetY.
        if ((homerX < targetX && homerX >= 0 && targetX > 0) || (homerX > targetX && homerX > 0 && targetX >= 0) || (homerX > targetX && homerX <= 0 && targetX < 0) || (homerX === targetX)) homerToTargetX = targetX - homerX;
        if (homerX < targetX && homerX <= 0 && targetX >= 0) homerToTargetX = Math.abs(homerX) + Math.abs(targetX);
        if (homerX < targetX && homerX < 0 && targetX <= 0) homerToTargetX = Math.abs(homerX) - Math.abs(targetX);
        if (homerX > targetX && homerX > 0 && targetX < 0) homerToTargetX = -(Math.abs(homerX) + Math.abs(targetX));
        if ((homerY < targetY && homerY >= 0 && targetY > 0) || (homerY > targetY && homerY > 0 && targetY >= 0) || (homerY > targetY && homerY <= 0 && targetY < 0) || (homerY === targetY)) homerToTargetY = targetY - homerY;
        if (homerY < targetY && homerY <= 0 && targetY >= 0) homerToTargetY = Math.abs(homerY) + Math.abs(targetY);
        if (homerY < targetY && homerY < 0 && targetY <= 0) homerToTargetY = Math.abs(homerY) - Math.abs(targetY);
        if (homerY > targetY && homerY > 0 && targetY < 0) homerToTargetY = -(Math.abs(homerY) + Math.abs(targetY));
        //making a normalized ratio out of homerToTargetX and homerToTargetY's relationship
        //NO DIVIDE BY ZERO PROTECTION HERE!!!
        if (Math.abs(homerToTargetX) > Math.abs(homerToTargetY)) {
            if (homerToTargetX < 0) {
                normalizedHomerToTargetXRatio = -(Math.abs(homerToTargetX) / Math.abs(homerToTargetX));
            } else {
                normalizedHomerToTargetXRatio = Math.abs(homerToTargetX) / Math.abs(homerToTargetX);
            }
            if (homerToTargetY < 0) {
                normalizedHomerToTargetYRatio = -(Math.abs(homerToTargetY) / Math.abs(homerToTargetX));
            } else {
                normalizedHomerToTargetYRatio = Math.abs(homerToTargetY) / Math.abs(homerToTargetX);
            }
        }
        if (Math.abs(homerToTargetX) < Math.abs(homerToTargetY)) {
            if (homerToTargetX < 0) {
                normalizedHomerToTargetXRatio = -(Math.abs(homerToTargetX) / Math.abs(homerToTargetY));
            } else {
                normalizedHomerToTargetXRatio = Math.abs(homerToTargetX) / Math.abs(homerToTargetY);
            }
            if (homerToTargetY < 0) {
                normalizedHomerToTargetYRatio = -(Math.abs(homerToTargetY) / Math.abs(homerToTargetY));
            } else {
                normalizedHomerToTargetYRatio = Math.abs(homerToTargetY) / Math.abs(homerToTargetY);
            }
        }
        dx = normalizedHomerToTargetXRatio * localSprite.acceleration;    //scale the normalized ratio for the desired acceleration
        dy = normalizedHomerToTargetYRatio * localSprite.acceleration;
        localSprite.vx += dx;   //add the scaled vector to the homer's velocity
        localSprite.vy += dy;
    }
    //rotation. Should there be an "isRotating" flag, ond only do this if it's true?
    localSprite.rotation += localSprite.rotationPerFrame;
    //scale oscillator
    if (localSprite.scaleOscillation === true) {
        if (localSprite.xScaleWaxing === true) {
            if (localSprite.xScale <= localSprite.xScaleMax) localSprite.xScale += localSprite.xScalePerFrame;
            if (localSprite.xScale > localSprite.xScaleMax) localSprite.xScaleWaxing = false;
        }
        if (localSprite.xScaleWaxing === false) {
            if (localSprite.xScale >= localSprite.xScaleMin) localSprite.xScale -= localSprite.xScalePerFrame;
            if (localSprite.xScale < localSprite.xScaleMin) localSprite.xScaleWaxing = true;
        }
        if (localSprite.yScaleWaxing === true) {
            if (localSprite.yScale <= localSprite.yScaleMax) localSprite.yScale += localSprite.yScalePerFrame;
            if (localSprite.yScale > localSprite.yScaleMax) localSprite.yScaleWaxing = false;
        }
        if (localSprite.yScaleWaxing === false) {
            if (localSprite.yScale >= localSprite.yScaleMin) localSprite.yScale -= localSprite.yScalePerFrame;
            if (localSprite.yScale < localSprite.yScaleMin) localSprite.yScaleWaxing = true;
        }
    }
    //contrail generation
    //TOO SPECIFIC TO FIREBALL RIGHT NOW. It seems like you could give addSprite functions a bunch of new parameters to fix this, but that seems messy, as a lot of sprites won't have contrails.
    if (localSprite.hasContrail === true) {
        if (localSprite.contrailTimer >= localSprite.framesBetweenContrailParticles) {
            addFireballContrailParticle(localSprite, 200, 32, 32);
            localSprite.contrailTimer = 0;
        } else {
            localSprite.contrailTimer++;
        }
    }
    localSprite.x += localSprite.vx;
    localSprite.y += localSprite.vy;
    //max speed limit:
    if (localSprite.vx < 0) {
        localSprite.vx = Math.max(-localSprite.maxSpeed, localSprite.vx);
    } else {
        localSprite.vx = Math.min(localSprite.maxSpeed, localSprite.vx);
    }
    if (localSprite.vy < 0) {
        localSprite.vy = Math.max(-localSprite.maxSpeed, localSprite.vy);
    } else {
        localSprite.vy = Math.min(localSprite.maxSpeed, localSprite.vy);
    }
    //animation stuff. msBetweenFrames sets sprite's animation speed.
    localSprite.currentFrame = Math.floor(now() / localSprite.msBetweenFrames) % localSprite.animation.frames.length;
    // remove the sprite from the array of local sprites after it has used up all of its frames.
    if (localSprite.framesToLive-- <= 0) {
        // This flag will be used in the update loop to remove this sprite from the list of localSprites.
        localSprite.shouldBeRemoved = true;
    }
}

function removeFinishedLocalSprites() {
    // This just gets rid of all the local sprites that have shouldBeRemoved set to true on them
    localSprites = localSprites.filter(localSprite => !localSprite.shouldBeRemoved);
}

var localSprites = [];
var twilightTilesImage = requireImage('/gfx/jetrel/twilight-tiles.png'),
fireballBImage = requireImage('/gfx/fireball/fireballB.png'),
fireballContrailAImage = requireImage('/gfx/fireball/fireballContrailA.png');

function addLocalFallingSpikesSprite() {
    var hitBox = rectangle(0, 0, 16, 16);
    // I'm using the different versions of the spikes pointing down tiles to make an "animation"
    var frames = [
        $.extend(rectangle(1 * 16, 14 * 16, 16, 16), {image: twilightTilesImage, hitBox}),
        $.extend(rectangle(2 * 16, 14 * 16, 16, 16), {image: twilightTilesImage, hitBox}),
        $.extend(rectangle(3 * 16, 14 * 16, 16, 16), {image: twilightTilesImage, hitBox}),
        $.extend(rectangle(2 * 16, 14 * 16, 16, 16), {image: twilightTilesImage, hitBox}),
    ];
    var fallingSpikesSprite = new SimpleSprite({frames}, mainCharacter.x, cameraY - 32, 0, 5, 2, -2); //what is this '5' in here?
    localSprites.push(fallingSpikesSprite);
}

function addHomingFireballSprite(xPosition, yPosition, target) {
    var hitBox = rectangle(0, 0, 32, 32);
    var frames = [
        $.extend(rectangle(0 * 32, 0 * 32, 32, 32), {image: fireballBImage, hitBox}),
        $.extend(rectangle(1 * 32, 0 * 32, 32, 32), {image: fireballBImage, hitBox}),
        $.extend(rectangle(2 * 32, 0 * 32, 32, 32), {image: fireballBImage, hitBox}),
        $.extend(rectangle(3 * 32, 0 * 32, 32, 32), {image: fireballBImage, hitBox}),
        $.extend(rectangle(4 * 32, 0 * 32, 32, 32), {image: fireballBImage, hitBox}),
    ];
    var homingFireballSprite = new SimpleSprite({frames}, xPosition, yPosition, 0, 0, 1.5, 1.5);
    homingFireballSprite.homing = true;
    homingFireballSprite.target = target;
    homingFireballSprite.maxSpeed = 3.5;
    homingFireballSprite.acceleration = 0.8;
    homingFireballSprite.framesToLive = 32767;
    homingFireballSprite.msBetweenFrames = 50;
    homingFireballSprite.rotationPerFrame = 5;
    homingFireballSprite.scaleOscillation = true;
    homingFireballSprite.xScaleMax = 1.75;
    homingFireballSprite.xScaleMin = 1.25;
    homingFireballSprite.yScaleMax = 1.75;
    homingFireballSprite.yScaleMin = 1.25;
    homingFireballSprite.xScalePerFrame = 0.01;
    homingFireballSprite.yScalePerFrame = 0.01;
    homingFireballSprite.hasContrail = true;
    homingFireballSprite.framesBetweenContrailParticles = 1;
    localSprites.push(homingFireballSprite);
}

function addFireballContrailParticle(parent, decayFrames, parentPreScalingXSize, parentPreScalingYSize) {
    var hitBox = rectangle(0, 0, 8, 8);
    var frames = [
        $.extend(rectangle(0 * 8, 0 * 8, 8, 8), {image: fireballContrailAImage, hitBox}),
        //$.extend(rectangle(1 * 8, 0 * 8, 8, 8), {image: fireballContrailAImage, hitBox}),
        //$.extend(rectangle(2 * 8, 0 * 8, 8, 8), {image: fireballContrailAImage, hitBox}),
        //$.extend(rectangle(3 * 8, 0 * 8, 8, 8), {image: fireballContrailAImage, hitBox}),
        //$.extend(rectangle(4 * 8, 0 * 8, 8, 8), {image: fireballContrailAImage, hitBox}),
    ];
    var randomX,    //distance particle spawns from parent's origin
    randomY;
    //random spawn location of particles in vicinity of parent sprite
    if (Math.random() > 0.5) {
        randomX = Math.round(parent.x + ((Math.random() * parent.xScale * parentPreScalingXSize) / 2));  //divide by 2 to keep the particles spawning completely within the bounds of the parent's hitbox
    } else {
        randomX = Math.round(parent.x - ((Math.random() * parent.xScale * parentPreScalingXSize) / 2));
    }
    if (Math.random() < 0.5) {
        randomY = Math.round(parent.y + ((Math.random() * parent.yScale * parentPreScalingYSize) / 2));
    } else {
        randomY = Math.round(parent.y - ((Math.random() * parent.yScale * parentPreScalingYSize) / 2));
    }
    var fireballContrailParticle = new SimpleSprite({frames}, randomX, randomY, 0, 0, 0.5, 1.5);
    fireballContrailParticle.framesToLive = decayFrames;
    fireballContrailParticle.scaleOscillation = true;
    fireballContrailParticle.xScalePerFrame = fireballContrailParticle.xScale / fireballContrailParticle.framesToLive;
    fireballContrailParticle.yScalePerFrame = fireballContrailParticle.yScale / fireballContrailParticle.framesToLive;
    fireballContrailParticle.xScaleMin = 0;
    fireballContrailParticle.yScaleMin = 0;
    fireballContrailParticle.rotationPerFrame = 50;
    //fireballContrailParticle.msBetweenFrames = Math.round((decayFrames * 50 /*or framerate*/) / frames.length) + 1; //'+1' hopefully keeps the animation from starting to loop just before the pariticle dies.  //would be better to also have a continuous alpha fade happen during this time. Could also scale down if that weren't build into the animation frames already.
    //parent.contrailParticles.push(fireballContrailParticle);
    localSprites.push(fireballContrailParticle); //BROKEN: Should push to parent.contrailParticles, but then render.js should render things in that array. I don't know the syntax for that yet, I don't think.
}

<commit_msg>Fewer, shorter-lived particles in fireball contrail.<commit_after>class SimpleSprite {
    constructor(animation, x, y, vx = 0, vy = 0, xScale = 1, yScale = 1) {
        this.animation = animation;
        this.x = x;
        this.y = y;
        this.vx = vx;
        this.vy = vy;
        this.rotation = 0;
        this.rotationPerFrame = 0;
        this.homing = false;
        this.target = {x: 0, y: 0};
        this.acceleration = 0;
        this.maxSpeed = 0;
        this.currentFrame = 0;
        this.framesToLive = 200;
        this.msBetweenFrames = 200;
        this.hasContrail = false;
        this.framesBetweenContrailParticles = 0; //game frames, not animation frames.
        this.contrailTimer = 0;
        this.contrailParticles = [];
        // I needed these because the graphic I wanted to use faced up and I need it to face down,
        // which I can get by using yScale = -1
        this.xScale = xScale;
        this.yScale = yScale;
        this.xScaleMax = xScale;
        this.xScaleMin = xScale;
        this.yScaleMax = yScale;
        this.yScaleMin = yScale;
        this.xScalePerFrame = 0;
        this.yScalePerFrame = 0;
        this.scaleOscillation = false;
        this.xScaleWaxing = false;
        this.yScaleWaxing = false;
    }
}

// This function is called every update and controls updating the local sprite, which for now just means:
// update position according to velocity.
// updating the current frame
// removing the object
function updateLocalSprite(localSprite) {
    if (localSprite.homing) { //homing behavior
        var homerToTargetX,  //these two vars are the difference, in x/y values, between the homer's position and its target's position,
        homerToTargetY,        //phrased so that they could be added to the homer's coordinates in order to overlap the target's.
        normalizedHomerToTargetXRatio,    //these two vars will turn that vector into a normalized ratio. Actually, sometimes one of the numbers is over 1 right now, but it still works, I *think.*
        normalizedHomerToTargetYRatio,
        homerX = localSprite.x,
        homerY = localSprite.y,
        targetX = localSprite.target.x,
        targetY = localSprite.target.y,
        dx,
        dy;
        //finding values for homerToTargetX and homerToTargetY.
        if ((homerX < targetX && homerX >= 0 && targetX > 0) || (homerX > targetX && homerX > 0 && targetX >= 0) || (homerX > targetX && homerX <= 0 && targetX < 0) || (homerX === targetX)) homerToTargetX = targetX - homerX;
        if (homerX < targetX && homerX <= 0 && targetX >= 0) homerToTargetX = Math.abs(homerX) + Math.abs(targetX);
        if (homerX < targetX && homerX < 0 && targetX <= 0) homerToTargetX = Math.abs(homerX) - Math.abs(targetX);
        if (homerX > targetX && homerX > 0 && targetX < 0) homerToTargetX = -(Math.abs(homerX) + Math.abs(targetX));
        if ((homerY < targetY && homerY >= 0 && targetY > 0) || (homerY > targetY && homerY > 0 && targetY >= 0) || (homerY > targetY && homerY <= 0 && targetY < 0) || (homerY === targetY)) homerToTargetY = targetY - homerY;
        if (homerY < targetY && homerY <= 0 && targetY >= 0) homerToTargetY = Math.abs(homerY) + Math.abs(targetY);
        if (homerY < targetY && homerY < 0 && targetY <= 0) homerToTargetY = Math.abs(homerY) - Math.abs(targetY);
        if (homerY > targetY && homerY > 0 && targetY < 0) homerToTargetY = -(Math.abs(homerY) + Math.abs(targetY));
        //making a normalized ratio out of homerToTargetX and homerToTargetY's relationship
        //NO DIVIDE BY ZERO PROTECTION HERE!!!
        if (Math.abs(homerToTargetX) > Math.abs(homerToTargetY)) {
            if (homerToTargetX < 0) {
                normalizedHomerToTargetXRatio = -(Math.abs(homerToTargetX) / Math.abs(homerToTargetX));
            } else {
                normalizedHomerToTargetXRatio = Math.abs(homerToTargetX) / Math.abs(homerToTargetX);
            }
            if (homerToTargetY < 0) {
                normalizedHomerToTargetYRatio = -(Math.abs(homerToTargetY) / Math.abs(homerToTargetX));
            } else {
                normalizedHomerToTargetYRatio = Math.abs(homerToTargetY) / Math.abs(homerToTargetX);
            }
        }
        if (Math.abs(homerToTargetX) < Math.abs(homerToTargetY)) {
            if (homerToTargetX < 0) {
                normalizedHomerToTargetXRatio = -(Math.abs(homerToTargetX) / Math.abs(homerToTargetY));
            } else {
                normalizedHomerToTargetXRatio = Math.abs(homerToTargetX) / Math.abs(homerToTargetY);
            }
            if (homerToTargetY < 0) {
                normalizedHomerToTargetYRatio = -(Math.abs(homerToTargetY) / Math.abs(homerToTargetY));
            } else {
                normalizedHomerToTargetYRatio = Math.abs(homerToTargetY) / Math.abs(homerToTargetY);
            }
        }
        dx = normalizedHomerToTargetXRatio * localSprite.acceleration;    //scale the normalized ratio for the desired acceleration
        dy = normalizedHomerToTargetYRatio * localSprite.acceleration;
        localSprite.vx += dx;   //add the scaled vector to the homer's velocity
        localSprite.vy += dy;
    }
    //rotation. Should there be an "isRotating" flag, ond only do this if it's true?
    localSprite.rotation += localSprite.rotationPerFrame;
    //scale oscillator
    if (localSprite.scaleOscillation === true) {
        if (localSprite.xScaleWaxing === true) {
            if (localSprite.xScale <= localSprite.xScaleMax) localSprite.xScale += localSprite.xScalePerFrame;
            if (localSprite.xScale > localSprite.xScaleMax) localSprite.xScaleWaxing = false;
        }
        if (localSprite.xScaleWaxing === false) {
            if (localSprite.xScale >= localSprite.xScaleMin) localSprite.xScale -= localSprite.xScalePerFrame;
            if (localSprite.xScale < localSprite.xScaleMin) localSprite.xScaleWaxing = true;
        }
        if (localSprite.yScaleWaxing === true) {
            if (localSprite.yScale <= localSprite.yScaleMax) localSprite.yScale += localSprite.yScalePerFrame;
            if (localSprite.yScale > localSprite.yScaleMax) localSprite.yScaleWaxing = false;
        }
        if (localSprite.yScaleWaxing === false) {
            if (localSprite.yScale >= localSprite.yScaleMin) localSprite.yScale -= localSprite.yScalePerFrame;
            if (localSprite.yScale < localSprite.yScaleMin) localSprite.yScaleWaxing = true;
        }
    }
    //contrail generation
    //TOO SPECIFIC TO FIREBALL RIGHT NOW. It seems like you could give addSprite functions a bunch of new parameters to fix this, but that seems messy, as a lot of sprites won't have contrails.
    if (localSprite.hasContrail === true) {
        if (localSprite.contrailTimer >= localSprite.framesBetweenContrailParticles) {
            addFireballContrailParticle(localSprite, 75, 32, 32);
            localSprite.contrailTimer = 0;
        } else {
            localSprite.contrailTimer++;
        }
    }
    localSprite.x += localSprite.vx;
    localSprite.y += localSprite.vy;
    //max speed limit:
    if (localSprite.vx < 0) {
        localSprite.vx = Math.max(-localSprite.maxSpeed, localSprite.vx);
    } else {
        localSprite.vx = Math.min(localSprite.maxSpeed, localSprite.vx);
    }
    if (localSprite.vy < 0) {
        localSprite.vy = Math.max(-localSprite.maxSpeed, localSprite.vy);
    } else {
        localSprite.vy = Math.min(localSprite.maxSpeed, localSprite.vy);
    }
    //animation stuff. msBetweenFrames sets sprite's animation speed.
    localSprite.currentFrame = Math.floor(now() / localSprite.msBetweenFrames) % localSprite.animation.frames.length;
    // remove the sprite from the array of local sprites after it has used up all of its frames.
    if (localSprite.framesToLive-- <= 0) {
        // This flag will be used in the update loop to remove this sprite from the list of localSprites.
        localSprite.shouldBeRemoved = true;
    }
}

function removeFinishedLocalSprites() {
    // This just gets rid of all the local sprites that have shouldBeRemoved set to true on them
    localSprites = localSprites.filter(localSprite => !localSprite.shouldBeRemoved);
}

var localSprites = [];
var twilightTilesImage = requireImage('/gfx/jetrel/twilight-tiles.png'),
fireballBImage = requireImage('/gfx/fireball/fireballB.png'),
fireballContrailAImage = requireImage('/gfx/fireball/fireballContrailA.png');

function addLocalFallingSpikesSprite() {
    var hitBox = rectangle(0, 0, 16, 16);
    // I'm using the different versions of the spikes pointing down tiles to make an "animation"
    var frames = [
        $.extend(rectangle(1 * 16, 14 * 16, 16, 16), {image: twilightTilesImage, hitBox}),
        $.extend(rectangle(2 * 16, 14 * 16, 16, 16), {image: twilightTilesImage, hitBox}),
        $.extend(rectangle(3 * 16, 14 * 16, 16, 16), {image: twilightTilesImage, hitBox}),
        $.extend(rectangle(2 * 16, 14 * 16, 16, 16), {image: twilightTilesImage, hitBox}),
    ];
    var fallingSpikesSprite = new SimpleSprite({frames}, mainCharacter.x, cameraY - 32, 0, 5, 2, -2); //what is this '5' in here?
    localSprites.push(fallingSpikesSprite);
}

function addHomingFireballSprite(xPosition, yPosition, target) {
    var hitBox = rectangle(0, 0, 32, 32);
    var frames = [
        $.extend(rectangle(0 * 32, 0 * 32, 32, 32), {image: fireballBImage, hitBox}),
        $.extend(rectangle(1 * 32, 0 * 32, 32, 32), {image: fireballBImage, hitBox}),
        $.extend(rectangle(2 * 32, 0 * 32, 32, 32), {image: fireballBImage, hitBox}),
        $.extend(rectangle(3 * 32, 0 * 32, 32, 32), {image: fireballBImage, hitBox}),
        $.extend(rectangle(4 * 32, 0 * 32, 32, 32), {image: fireballBImage, hitBox}),
    ];
    var homingFireballSprite = new SimpleSprite({frames}, xPosition, yPosition, 0, 0, 1.5, 1.5);
    homingFireballSprite.homing = true;
    homingFireballSprite.target = target;
    homingFireballSprite.maxSpeed = 3.5;
    homingFireballSprite.acceleration = 0.8;
    homingFireballSprite.framesToLive = 32767;
    homingFireballSprite.msBetweenFrames = 50;
    homingFireballSprite.rotationPerFrame = 5;
    homingFireballSprite.scaleOscillation = true;
    homingFireballSprite.xScaleMax = 1.75;
    homingFireballSprite.xScaleMin = 1.25;
    homingFireballSprite.yScaleMax = 1.75;
    homingFireballSprite.yScaleMin = 1.25;
    homingFireballSprite.xScalePerFrame = 0.01;
    homingFireballSprite.yScalePerFrame = 0.01;
    homingFireballSprite.hasContrail = true;
    homingFireballSprite.framesBetweenContrailParticles = 3;
    localSprites.push(homingFireballSprite);
}

function addFireballContrailParticle(parent, decayFrames, parentPreScalingXSize, parentPreScalingYSize) {
    var hitBox = rectangle(0, 0, 8, 8);
    var frames = [
        $.extend(rectangle(0 * 8, 0 * 8, 8, 8), {image: fireballContrailAImage, hitBox}),
        //$.extend(rectangle(1 * 8, 0 * 8, 8, 8), {image: fireballContrailAImage, hitBox}),
        //$.extend(rectangle(2 * 8, 0 * 8, 8, 8), {image: fireballContrailAImage, hitBox}),
        //$.extend(rectangle(3 * 8, 0 * 8, 8, 8), {image: fireballContrailAImage, hitBox}),
        //$.extend(rectangle(4 * 8, 0 * 8, 8, 8), {image: fireballContrailAImage, hitBox}),
    ];
    var randomX,    //distance particle spawns from parent's origin
    randomY;
    //random spawn location of particles in vicinity of parent sprite
    if (Math.random() > 0.5) {
        randomX = Math.round(parent.x + ((Math.random() * parent.xScale * parentPreScalingXSize) / 2));  //divide by 2 to keep the particles spawning completely within the bounds of the parent's hitbox
    } else {
        randomX = Math.round(parent.x - ((Math.random() * parent.xScale * parentPreScalingXSize) / 2));
    }
    if (Math.random() < 0.5) {
        randomY = Math.round(parent.y + ((Math.random() * parent.yScale * parentPreScalingYSize) / 2));
    } else {
        randomY = Math.round(parent.y - ((Math.random() * parent.yScale * parentPreScalingYSize) / 2));
    }
    var fireballContrailParticle = new SimpleSprite({frames}, randomX, randomY, 0, 0, 0.5, 1.5);
    fireballContrailParticle.framesToLive = decayFrames;
    fireballContrailParticle.scaleOscillation = true;
    fireballContrailParticle.xScalePerFrame = fireballContrailParticle.xScale / fireballContrailParticle.framesToLive;
    fireballContrailParticle.yScalePerFrame = fireballContrailParticle.yScale / fireballContrailParticle.framesToLive;
    fireballContrailParticle.xScaleMin = 0;
    fireballContrailParticle.yScaleMin = 0;
    fireballContrailParticle.rotationPerFrame = 50;
    //fireballContrailParticle.msBetweenFrames = Math.round((decayFrames * 50 /*or framerate*/) / frames.length) + 1; //'+1' hopefully keeps the animation from starting to loop just before the pariticle dies.  //would be better to also have a continuous alpha fade happen during this time. Could also scale down if that weren't build into the animation frames already.
    //parent.contrailParticles.push(fireballContrailParticle);
    localSprites.push(fireballContrailParticle); //BROKEN: Should push to parent.contrailParticles, but then render.js should render things in that array. I don't know the syntax for that yet, I don't think.
}

<|endoftext|>


<commit_before>var test = require("tape")

var h = require("../h.js")
var diff = require("../diff.js")
var patch = require("../patch.js")
var render = require("../create-element.js")

var patchCount = require("./lib/patch-count.js")

test("keys get reordered", function (assert) {
    var leftNode = h("div", [
        h("div", { key: 1 }),
        h("div", { key: 2 }),
        h("div", { key: 3 }),
        h("div", { key: 4 }),
        h("div", { key: "test" }),
        h("div", { key: 6 }),
        h("div", { key: "good" }),
        h("div", { key: "7" })
    ])

    var rightNode = h("div", [
        h("div", { key: "7" }),
        h("div", { key: 4 }),
        h("div", { key: 3 }),
        h("div", { key: 2 }),
        h("div", { key: 6 }),
        h("div", { key: "test" }),
        h("div", { key: "good" }),
        h("div", { key: 1 })
    ])

    var rootNode = render(leftNode)

    var childNodes = []
    for (var i = 0; i < rootNode.childNodes.length; i++) {
        childNodes.push(rootNode.childNodes[i])
    }

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, rootNode.childNodes.length)

    assert.equal(newRoot.childNodes[7], childNodes[0])
    assert.equal(newRoot.childNodes[3], childNodes[1])
    assert.equal(newRoot.childNodes[2], childNodes[2])
    assert.equal(newRoot.childNodes[1], childNodes[3])
    assert.equal(newRoot.childNodes[5], childNodes[4])
    assert.equal(newRoot.childNodes[4], childNodes[5])
    assert.equal(newRoot.childNodes[6], childNodes[6])
    assert.equal(newRoot.childNodes[0], childNodes[7])
    assert.end()
})

test("mix keys without keys", function (assert) {
    var leftNode = h("div", [
        h("div", { key: 1 }),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div")
    ])

    var rightNode = h("div", [
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div", { key: 1 })
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, rootNode.childNodes.length)

    assert.equal(newRoot.childNodes[0], childNodes[1])
    assert.equal(newRoot.childNodes[1], childNodes[2])
    assert.equal(newRoot.childNodes[2], childNodes[3])
    assert.equal(newRoot.childNodes[3], childNodes[4])
    assert.equal(newRoot.childNodes[4], childNodes[5])
    assert.equal(newRoot.childNodes[5], childNodes[6])
    assert.equal(newRoot.childNodes[6], childNodes[7])
    assert.equal(newRoot.childNodes[7], childNodes[0])
    assert.end()
})

test("missing key gets replaced", function (assert) {
    var leftNode = h("div", [
        h("div", { key: 1 }),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div")
    ])

    var rightNode = h("div", [
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div")
    ])

    var rootNode = render(leftNode)

    var childNodes = []
    for (var i = 0; i < rootNode.childNodes.length; i++) {
        childNodes.push(rootNode.childNodes[i])
    }

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, rootNode.childNodes.length)

    assert.notEqual(newRoot.childNodes[0], childNodes[0])
    assert.equal(newRoot.childNodes[1], childNodes[1])
    assert.equal(newRoot.childNodes[2], childNodes[2])
    assert.equal(newRoot.childNodes[3], childNodes[3])
    assert.equal(newRoot.childNodes[4], childNodes[4])
    assert.equal(newRoot.childNodes[5], childNodes[5])
    assert.equal(newRoot.childNodes[6], childNodes[6])
    assert.equal(newRoot.childNodes[7], childNodes[7])
    assert.end()
})

test("widgets can be keyed", function (assert) {
    function DivWidget(key, state) {
        this.key = key
        this.state = state
    }

    DivWidget.prototype.init = function () {
        return render(h("div", this.state))
    }

    DivWidget.prototype.update = function (rootNode, prev) {
        if (this.state !== prev.state) {
            return render(h("div", this.state))
        }
    }

    DivWidget.prototype.type = "Widget"

    var leftNode = h("div", [
        new DivWidget("1", "a"),
        new DivWidget("2", "b"),
        new DivWidget("3", "c")
    ])

    var rightNode = h("div", [
        new DivWidget("3", "c"),
        new DivWidget("2", "b"),
        new DivWidget("1", "a")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 4)    // 1 reorder and 3 update patches

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, rootNode.childNodes.length)

    assert.equal(newRoot.childNodes[0], childNodes[2])
    assert.equal(newRoot.childNodes[1], childNodes[1])
    assert.equal(newRoot.childNodes[2], childNodes[0])
    assert.end()
})

test("delete key at the start", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rightNode = h("div", [
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 2)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 2)

    assert.equal(newRoot.childNodes[0], childNodes[1])
    assert.equal(newRoot.childNodes[1], childNodes[2])
    assert.end()
})

test("add key to start", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rightNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 3)

    assert.equal(newRoot.childNodes[1], childNodes[0])
    assert.equal(newRoot.childNodes[2], childNodes[1])
    assert.end()
})

test("delete key at the end", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rightNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)
    debugger;
    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 2)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 2)

    assert.equal(newRoot.childNodes[0], childNodes[0])
    assert.equal(newRoot.childNodes[1], childNodes[1])
    assert.end()
})

test("add key to end", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b")
    ])

    var rightNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 3)

    assert.equal(newRoot.childNodes[0], childNodes[0])
    assert.equal(newRoot.childNodes[1], childNodes[1])
    assert.end()
})

test("add to end and delete from center & reverse", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c"),
        h("div", { key: "d" }, "d"),
    ])

    var rightNode = h("div", [
        h("div", { key: "e" }, "e"),
        h("div", { key: "d" }, "d"),
        h("div", { key: "c" }, "c"),
        h("div", { key: "a" }, "a")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 2)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 4)

    assert.equal(newRoot.childNodes[1], childNodes[3])
    assert.equal(newRoot.childNodes[2], childNodes[2])
    assert.equal(newRoot.childNodes[3], childNodes[0])
    assert.end()
})

test("adding multiple widgets", function (assert) {
    function FooWidget(foo) {
        this.foo = foo
        this.counter = 0
        this.key = foo
    }

    FooWidget.prototype.init = function () {
        return render(h("div", String(this.foo)))
    }

    FooWidget.prototype.update = function (prev, elem) {
        this.counter = prev.counter + 1
        elem.textContent = this.foo + this.counter
    }

    FooWidget.prototype.type = "Widget"

    var firstTree = h("div", [])
    var rootNode = render(firstTree)

    assert.equal(rootNode.tagName, "DIV")

    var secondTree = h("div", [
        new FooWidget("foo")
    ])
    rootNode = patch(rootNode, diff(firstTree, secondTree))

    assert.equal(rootNode.tagName, "DIV")
    assert.equal(rootNode.childNodes.length, 1)
    assert.equal(rootNode.childNodes[0].tagName, "DIV")
    assert.equal(rootNode.childNodes[0].childNodes[0].data, "foo")

    var thirdTree = h("div", [
        new FooWidget("foo"),
        new FooWidget("bar")
    ])
    rootNode = patch(rootNode, diff(secondTree, thirdTree))

    assert.equal(rootNode.tagName, "DIV")
    assert.equal(rootNode.childNodes.length, 2)

    assert.end()
})

function childNodesArray(node) {
    var childNodes = []
    for (var i = 0; i < node.childNodes.length; i++) {
        childNodes.push(node.childNodes[i])
    }
    return childNodes
}
<commit_msg>remove debugger statement -_-<commit_after>var test = require("tape")

var h = require("../h.js")
var diff = require("../diff.js")
var patch = require("../patch.js")
var render = require("../create-element.js")

var patchCount = require("./lib/patch-count.js")

test("keys get reordered", function (assert) {
    var leftNode = h("div", [
        h("div", { key: 1 }),
        h("div", { key: 2 }),
        h("div", { key: 3 }),
        h("div", { key: 4 }),
        h("div", { key: "test" }),
        h("div", { key: 6 }),
        h("div", { key: "good" }),
        h("div", { key: "7" })
    ])

    var rightNode = h("div", [
        h("div", { key: "7" }),
        h("div", { key: 4 }),
        h("div", { key: 3 }),
        h("div", { key: 2 }),
        h("div", { key: 6 }),
        h("div", { key: "test" }),
        h("div", { key: "good" }),
        h("div", { key: 1 })
    ])

    var rootNode = render(leftNode)

    var childNodes = []
    for (var i = 0; i < rootNode.childNodes.length; i++) {
        childNodes.push(rootNode.childNodes[i])
    }

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, rootNode.childNodes.length)

    assert.equal(newRoot.childNodes[7], childNodes[0])
    assert.equal(newRoot.childNodes[3], childNodes[1])
    assert.equal(newRoot.childNodes[2], childNodes[2])
    assert.equal(newRoot.childNodes[1], childNodes[3])
    assert.equal(newRoot.childNodes[5], childNodes[4])
    assert.equal(newRoot.childNodes[4], childNodes[5])
    assert.equal(newRoot.childNodes[6], childNodes[6])
    assert.equal(newRoot.childNodes[0], childNodes[7])
    assert.end()
})

test("mix keys without keys", function (assert) {
    var leftNode = h("div", [
        h("div", { key: 1 }),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div")
    ])

    var rightNode = h("div", [
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div", { key: 1 })
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, rootNode.childNodes.length)

    assert.equal(newRoot.childNodes[0], childNodes[1])
    assert.equal(newRoot.childNodes[1], childNodes[2])
    assert.equal(newRoot.childNodes[2], childNodes[3])
    assert.equal(newRoot.childNodes[3], childNodes[4])
    assert.equal(newRoot.childNodes[4], childNodes[5])
    assert.equal(newRoot.childNodes[5], childNodes[6])
    assert.equal(newRoot.childNodes[6], childNodes[7])
    assert.equal(newRoot.childNodes[7], childNodes[0])
    assert.end()
})

test("missing key gets replaced", function (assert) {
    var leftNode = h("div", [
        h("div", { key: 1 }),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div")
    ])

    var rightNode = h("div", [
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div"),
        h("div")
    ])

    var rootNode = render(leftNode)

    var childNodes = []
    for (var i = 0; i < rootNode.childNodes.length; i++) {
        childNodes.push(rootNode.childNodes[i])
    }

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, rootNode.childNodes.length)

    assert.notEqual(newRoot.childNodes[0], childNodes[0])
    assert.equal(newRoot.childNodes[1], childNodes[1])
    assert.equal(newRoot.childNodes[2], childNodes[2])
    assert.equal(newRoot.childNodes[3], childNodes[3])
    assert.equal(newRoot.childNodes[4], childNodes[4])
    assert.equal(newRoot.childNodes[5], childNodes[5])
    assert.equal(newRoot.childNodes[6], childNodes[6])
    assert.equal(newRoot.childNodes[7], childNodes[7])
    assert.end()
})

test("widgets can be keyed", function (assert) {
    function DivWidget(key, state) {
        this.key = key
        this.state = state
    }

    DivWidget.prototype.init = function () {
        return render(h("div", this.state))
    }

    DivWidget.prototype.update = function (rootNode, prev) {
        if (this.state !== prev.state) {
            return render(h("div", this.state))
        }
    }

    DivWidget.prototype.type = "Widget"

    var leftNode = h("div", [
        new DivWidget("1", "a"),
        new DivWidget("2", "b"),
        new DivWidget("3", "c")
    ])

    var rightNode = h("div", [
        new DivWidget("3", "c"),
        new DivWidget("2", "b"),
        new DivWidget("1", "a")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 4)    // 1 reorder and 3 update patches

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, rootNode.childNodes.length)

    assert.equal(newRoot.childNodes[0], childNodes[2])
    assert.equal(newRoot.childNodes[1], childNodes[1])
    assert.equal(newRoot.childNodes[2], childNodes[0])
    assert.end()
})

test("delete key at the start", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rightNode = h("div", [
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 2)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 2)

    assert.equal(newRoot.childNodes[0], childNodes[1])
    assert.equal(newRoot.childNodes[1], childNodes[2])
    assert.end()
})

test("add key to start", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rightNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 3)

    assert.equal(newRoot.childNodes[1], childNodes[0])
    assert.equal(newRoot.childNodes[2], childNodes[1])
    assert.end()
})

test("delete key at the end", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rightNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 2)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 2)

    assert.equal(newRoot.childNodes[0], childNodes[0])
    assert.equal(newRoot.childNodes[1], childNodes[1])
    assert.end()
})

test("add key to end", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b")
    ])

    var rightNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 1)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 3)

    assert.equal(newRoot.childNodes[0], childNodes[0])
    assert.equal(newRoot.childNodes[1], childNodes[1])
    assert.end()
})

test("add to end and delete from center & reverse", function (assert) {
    var leftNode = h("div", [
        h("div", { key: "a" }, "a"),
        h("div", { key: "b" }, "b"),
        h("div", { key: "c" }, "c"),
        h("div", { key: "d" }, "d"),
    ])

    var rightNode = h("div", [
        h("div", { key: "e" }, "e"),
        h("div", { key: "d" }, "d"),
        h("div", { key: "c" }, "c"),
        h("div", { key: "a" }, "a")
    ])

    var rootNode = render(leftNode)
    var childNodes = childNodesArray(rootNode)

    var patches = diff(leftNode, rightNode)
    assert.equal(patchCount(patches), 2)

    var newRoot = patch(rootNode, patches)
    assert.equal(newRoot, rootNode)

    assert.equal(newRoot.childNodes.length, 4)

    assert.equal(newRoot.childNodes[1], childNodes[3])
    assert.equal(newRoot.childNodes[2], childNodes[2])
    assert.equal(newRoot.childNodes[3], childNodes[0])
    assert.end()
})

test("adding multiple widgets", function (assert) {
    function FooWidget(foo) {
        this.foo = foo
        this.counter = 0
        this.key = foo
    }

    FooWidget.prototype.init = function () {
        return render(h("div", String(this.foo)))
    }

    FooWidget.prototype.update = function (prev, elem) {
        this.counter = prev.counter + 1
        elem.textContent = this.foo + this.counter
    }

    FooWidget.prototype.type = "Widget"

    var firstTree = h("div", [])
    var rootNode = render(firstTree)

    assert.equal(rootNode.tagName, "DIV")

    var secondTree = h("div", [
        new FooWidget("foo")
    ])
    rootNode = patch(rootNode, diff(firstTree, secondTree))

    assert.equal(rootNode.tagName, "DIV")
    assert.equal(rootNode.childNodes.length, 1)
    assert.equal(rootNode.childNodes[0].tagName, "DIV")
    assert.equal(rootNode.childNodes[0].childNodes[0].data, "foo")

    var thirdTree = h("div", [
        new FooWidget("foo"),
        new FooWidget("bar")
    ])
    rootNode = patch(rootNode, diff(secondTree, thirdTree))

    assert.equal(rootNode.tagName, "DIV")
    assert.equal(rootNode.childNodes.length, 2)

    assert.end()
})

function childNodesArray(node) {
    var childNodes = []
    for (var i = 0; i < node.childNodes.length; i++) {
        childNodes.push(node.childNodes[i])
    }
    return childNodes
}
<|endoftext|>


<commit_before>module.exports = {
    master: {
        product: require('./data-util/master/product-data-util'),
        holiday: require('./data-util/master/holiday-data-util'),
        uom: require('./data-util/master/uom-data-util'),
        vat: require('./data-util/master/vat-data-util'),
        currency: require('./data-util/master/currency-data-util'),
        buyer: require('./data-util/master/buyer-data-util'),
        budget: require('./data-util/master/budget-data-util'),
        supplier: require('./data-util/master/supplier-data-util'),
        unit: require('./data-util/master/unit-data-util'),
        machine: require('./data-util/master/machine-data-util'),
        //lotMachine: require('./data-util/master/lot-machine-data-util'),
        category: require('./data-util/master/category-data-util'),
        lampStandard: require('./data-util/master/lamp-standard-data-util'),
        instruction: require('./data-util/master/instruction-data-util'),
        orderType: require('./data-util/master/order-type-data-util'),
        processType: require('./data-util/master/process-type-data-util'),
        colorType: require('./data-util/master/color-type-data-util'),
        machineType: require('./data-util/master/machine-type-data-util'),
        machineEvent: require('./data-util/master/machine-event-data-util'),
        comodity: require('./data-util/master/comodity-data-util'),
        quality: require('./data-util/master/quality-data-util'),
        termOfPayment: require('./data-util/master/term-of-payment-data-util'),
        company: require('./data-util/master/company-data-util'),
        contact: require('./data-util/master/contact-data-util')
    },
    purchasing:{
        purchaseRequest: require('./data-util/purchasing/purchase-request-data-util'),
        purchaseOrder: require('./data-util/purchasing/purchase-order-data-util'),
        purchaseOrderExternal: require('./data-util/purchasing/purchase-order-external-data-util'),
    },
    transaction: {
        deliveryOrder: require('./data-util/transaction/delivery-order-data-util'),
        unitReceiptNote: require('./data-util/transaction/unit-receipt-note-data-util'),
        unitPaymentOrder: require('./data-util/transaction/unit-payment-order-data-util'),
        unitPaymentPriceCorrectionNote: require('./data-util/transaction/unit-payment-price-correction-note-data-util'),
        unitPaymentQuantityCorrectionNote: require('./data-util/transaction/unit-payment-quantity-correction-note-data-util')
    },
    production: {
        monitoringEvent: require('./data-util/production/finishing-printing/monitoring-event-data-util'),
        monitoringSpecificationMachine: require('./data-util/production/finishing-printing/monitoring-specification-machine-data-util'),
        kanban: require('./data-util/production/finishing-printing/kanban-data-util')
    },
    sales: {
        productionOrder: require('./data-util/sales/production-order-data-util'),
        dealTrackingBoard: require('./data-util/sales/deal-tracking-board-data-util'),
        dealTrackingStage: require('./data-util/sales/deal-tracking-stage-data-util'),
        dealTrackingDeal: require('./data-util/sales/deal-tracking-deal-data-util'),
        dealTrackingActivity: require('./data-util/sales/deal-tracking-activity-data-util')
    },

};<commit_msg>add index kursBudget<commit_after>module.exports = {
    master: {
        product: require('./data-util/master/product-data-util'),
        holiday: require('./data-util/master/holiday-data-util'),
        uom: require('./data-util/master/uom-data-util'),
        vat: require('./data-util/master/vat-data-util'),
        currency: require('./data-util/master/currency-data-util'),
        buyer: require('./data-util/master/buyer-data-util'),
        budget: require('./data-util/master/budget-data-util'),
        supplier: require('./data-util/master/supplier-data-util'),
        unit: require('./data-util/master/unit-data-util'),
        machine: require('./data-util/master/machine-data-util'),
        //lotMachine: require('./data-util/master/lot-machine-data-util'),
        category: require('./data-util/master/category-data-util'),
        lampStandard: require('./data-util/master/lamp-standard-data-util'),
        instruction: require('./data-util/master/instruction-data-util'),
        orderType: require('./data-util/master/order-type-data-util'),
        processType: require('./data-util/master/process-type-data-util'),
        colorType: require('./data-util/master/color-type-data-util'),
        machineType: require('./data-util/master/machine-type-data-util'),
        machineEvent: require('./data-util/master/machine-event-data-util'),
        comodity: require('./data-util/master/comodity-data-util'),
        quality: require('./data-util/master/quality-data-util'),
        termOfPayment: require('./data-util/master/term-of-payment-data-util'),
        company: require('./data-util/master/company-data-util'),
        contact: require('./data-util/master/contact-data-util'),
        kursBudget: require('./data-util/master/kurs-budget-data-util')
    },
    purchasing:{
        purchaseRequest: require('./data-util/purchasing/purchase-request-data-util'),
        purchaseOrder: require('./data-util/purchasing/purchase-order-data-util'),
        purchaseOrderExternal: require('./data-util/purchasing/purchase-order-external-data-util'),
    },
    transaction: {
        deliveryOrder: require('./data-util/transaction/delivery-order-data-util'),
        unitReceiptNote: require('./data-util/transaction/unit-receipt-note-data-util'),
        unitPaymentOrder: require('./data-util/transaction/unit-payment-order-data-util'),
        unitPaymentPriceCorrectionNote: require('./data-util/transaction/unit-payment-price-correction-note-data-util'),
        unitPaymentQuantityCorrectionNote: require('./data-util/transaction/unit-payment-quantity-correction-note-data-util')
    },
    production: {
        monitoringEvent: require('./data-util/production/finishing-printing/monitoring-event-data-util'),
        monitoringSpecificationMachine: require('./data-util/production/finishing-printing/monitoring-specification-machine-data-util'),
        kanban: require('./data-util/production/finishing-printing/kanban-data-util')
    },
    sales: {
        productionOrder: require('./data-util/sales/production-order-data-util'),
        dealTrackingBoard: require('./data-util/sales/deal-tracking-board-data-util'),
        dealTrackingStage: require('./data-util/sales/deal-tracking-stage-data-util'),
        dealTrackingDeal: require('./data-util/sales/deal-tracking-deal-data-util'),
        dealTrackingActivity: require('./data-util/sales/deal-tracking-activity-data-util')
    },

};<|endoftext|>


<commit_before>class TreeBuilder {
  static _appendChild(parent, child){
    parent.appendChild(child);
  }

  static _build(tag, attrs, ...children){
    const el = document.createElement(tag);
    const append = TreeBuilder._appendChild;

    for(let k in attrs){
      const v = attrs[k];
      if (/^on(click)$/.test(k)) {
        const eventName = k.substring(2);
        el.addEventListener(eventName, v, false);
      } else if (k === "style") {
        for (let sk in v) {
          const sv = v[sk];
          el.style[sk] = sv;
        }
      } else {
        el.setAttribute(k, v);
      }
    }

    children.forEach((child)=>{
      if (["string", "number", "boolean"].includes(typeof child)) {
        append(el, document.createTextNode(child));
      } else if (Array.isArray(child)) {
        child.forEach(_child =>{
          append(el, _child);
        });
      }else{
        append(el, child);
      }
    });

    return el;
  }

  static build(fn){
    return fn(TreeBuilder._build);
  }
}
<commit_msg>format  gt4j830y6j890h7<commit_after>class TreeBuilder {
  static _appendChild(parent, child){
    parent.appendChild(child);
  }

  static _build(tag, attrs, ...children){
    const el = document.createElement(tag);
    const append = TreeBuilder._appendChild;

    for (let k in attrs) {
      const v = attrs[k];
      if (/^on(click)$/.test(k)) {
        const eventName = k.substring(2);
        el.addEventListener(eventName, v, false);
      } else if (k === "style") {
        for (let sk in v) {
          const sv = v[sk];
          el.style[sk] = sv;
        }
      } else {
        el.setAttribute(k, v);
      }
    }

    children.forEach((child)=>{
      if (["string", "number", "boolean"].includes(typeof child)) {
        append(el, document.createTextNode(child));
      } else if (Array.isArray(child)) {
        child.forEach(_child => append(el, _child));
      } else {
        append(el, child);
      }
    });

    return el;
  }

  static build(fn){
    return fn(TreeBuilder._build);
  }
}
<|endoftext|>


<commit_before>var libPath = process.env['KALLY_RAZOR_COV'] ? '../lib-cov/' : '../lib/';
var chai = require('chai');
var KallyRazor = require(libPath + 'main');
var fs = require('fs');

var razor = KallyRazor({
    root: __dirname
});

var should = chai.should();

describe('KallyRazor', function() {
    describe('throws an error', function() {
        it('when not passing a view', function() {
            should.Throw(function() { razor.render(); });
        });

        it('when not passing a string to render', function() {
            should.Throw(function() { razor.render({ File: 'test' }); });
        });

        it('when a file does not exist', function() {
            should.Throw(function() { razor.render('file-does-not-exist.html'); });
        });

        it('when a null file name is passed', function() {
            should.Throw(function() { razor.render(); });
        });
    });

    describe('compiles', function() {
        describe('from a file', function() {
            it('using the exact file name', function() {
                var result = razor.render(__dirname + '/input/test-no-razor.html');
                result.should.equal(fs.readFileSync(__dirname + '/output/test-no-razor.html').toString());
            });

            it('using relative file names', function() {
                var result = razor.render('input/test-no-razor.html');
                result.should.equal(fs.readFileSync(__dirname + '/output/test-no-razor.html').toString());
            });
        });

        describe('using razor', function() {
            it('wth a basic model', function() {
                var result = razor.render('input/test-basic-model.html', { name: 'Testy Tester' });
                result.should.equal(fs.readFileSync(__dirname + '/output/test-basic-model.html').toString());
            });

            it('with multiple references', function() {
                var result = razor.render('input/test-multiple-references.html', { name: 'Testy Tester', title: 'my title' });
                result.should.equal(fs.readFileSync(__dirname + '/output/test-multiple-references.html').toString());
            })
        });
    });
});
<commit_msg>100% coverage<commit_after>var libPath = process.env['KALLY_RAZOR_COV'] ? '../lib-cov/' : '../lib/';
var chai = require('chai');
var KallyRazor = require(libPath + 'main');
var fs = require('fs');

var razor = KallyRazor({
    root: __dirname
});

var should = chai.should();

describe('KallyRazor', function() {
    describe('throws an error', function() {
        it('when not passing a view', function() {
            should.Throw(function() { razor.render(); });
        });

        it('when not passing a string to render', function() {
            should.Throw(function() { razor.render({ File: 'test' }); });
        });

        it('when a file does not exist', function() {
            should.Throw(function() { razor.render('file-does-not-exist.html'); });
        });

        it('when a null file name is passed', function() {
            should.Throw(function() { razor.renderFromFile(); });
        });
    });

    describe('compiles', function() {
        describe('from a file', function() {
            it('using the exact file name', function() {
                var result = razor.render(__dirname + '/input/test-no-razor.html');
                result.should.equal(fs.readFileSync(__dirname + '/output/test-no-razor.html').toString());
            });

            it('using relative file names', function() {
                var result = razor.render('input/test-no-razor.html');
                result.should.equal(fs.readFileSync(__dirname + '/output/test-no-razor.html').toString());
            });
        });

        describe('using razor', function() {
            it('wth a basic model', function() {
                var result = razor.render('input/test-basic-model.html', { name: 'Testy Tester' });
                result.should.equal(fs.readFileSync(__dirname + '/output/test-basic-model.html').toString());
            });

            it('with multiple references', function() {
                var result = razor.render('input/test-multiple-references.html', { name: 'Testy Tester', title: 'my title' });
                result.should.equal(fs.readFileSync(__dirname + '/output/test-multiple-references.html').toString());
            })
        });
    });
});
<|endoftext|>


<commit_before>//remove:
var main = require('../build/dom4.node.js');
//:remove

function create(name) {
  return document.createElement(name);
}

wru.test([
  {
    name: "prototype has methods",
    test: function () {
      var div = create('div');
      wru.assert('.prepend()', div.prepend);
      wru.assert('.append()', div.append);
      wru.assert('.before()', div.before);
      wru.assert('.after()', div.after);
      wru.assert('.replace()', div.replace);
      wru.assert('.remove()', div.remove);
    }
  },{
    name: 'prepend',
    test: function () {
      var div = create('div'),
          first = div.appendChild(create('div')),
          another = create('div');
      div.prepend(another);
      wru.assert(
        "Pre-insert node into the context object before the context object's first child.",
        div.firstChild === another &&
        div.lastChild === first
      );
      div.prepend(
        create('one'),
        'two',
        create('three')
      );
      wru.assert(
        'works with multiple nodes',
        div.childNodes.length === 5 &&
        div.firstChild.nodeName.toLowerCase() === 'one' &&
        div.childNodes[1].nodeType !== 1 &&
        div.childNodes[1].nodeValue === 'two' &&
        div.childNodes[2].nodeName.toLowerCase() === 'three' &&
        div.childNodes[3] === another &&
        div.childNodes[4] === first
      );
    }
  },{
    name: 'append',
    test: function () {
      var div = create('div'),
          first = div.appendChild(create('div')),
          another = create('div');
      div.append(another);
      wru.assert(
        "Append node to the context object.",
        div.firstChild === first &&
        div.lastChild === another
      );
      div.append(
        create('one'),
        'two',
        create('three')
      );
      wru.assert(
        'works with multiple nodes',
        div.childNodes[0] === first &&
        div.childNodes[1] === another &&
        div.childNodes[2].nodeName.toLowerCase() === 'one' &&
        div.childNodes[3].nodeType !== 1 &&
        div.childNodes[3].nodeValue === 'two' &&
        div.childNodes[4].nodeName.toLowerCase() === 'three'
      );
    }
  },{
    name: 'before',
    test: function () {
      var div = create('div'),
          first = div.appendChild(create('div')),
          node = create('div');
      wru.assert(
        'If the context object does not have a parent, terminate these steps',
        !div.before(node)
      );
      first.before(node);
      wru.assert(
        "Pre-insert node into the context object's parent before the context object.",
        div.firstChild === node
      );
      first.before(create('one'), 'two');
      wru.assert(
        'works with multiple nodes',
        div.childNodes[0] === node &&
        div.childNodes[1].nodeName.toLowerCase() === 'one' &&
        div.childNodes[2].nodeType !== 1 &&
        div.childNodes[2].nodeValue === 'two' &&
        div.childNodes[3] === first
      );
    }
  },{
    name: 'after',
    test: function () {
      var div = create('div'),
          first = div.appendChild(create('div')),
          node = create('div');
      wru.assert(
        'If the context object does not have a parent, terminate these steps',
        !div.after(node)
      );
      first.after(node);
      wru.assert(
        "Pre-insert node into the context object's parent before the context object's next sibling.",
        div.lastChild === node
      );
      first.after(create('one'), 'two');
      wru.assert(
        'works with multiple nodes',
        div.childNodes[0] === first &&
        div.childNodes[1].nodeName.toLowerCase() === 'one' &&
        div.childNodes[2].nodeType !== 1 &&
        div.childNodes[2].nodeValue === 'two' &&
        div.childNodes[3] === node
      );
    }
  },{
    name: 'replace',
    test: function () {
      var div = create('div'),
          first = div.appendChild(create('div')),
          node = create('div');
      wru.assert(
        'If the context object does not have a parent, terminate these steps',
        !div.replace(node)
      );
      first.replace(node);
      wru.assert(
        "Replace the context object with node within the context object's parent.",
        div.firstChild === node &&
        div.childNodes.length === 1
      );
      node.replace(create('one'), 'two');
      wru.assert(
        'works with multiple nodes',
        div.childNodes[0].nodeName.toLowerCase() === 'one' &&
        div.childNodes[1].nodeType !== 1 &&
        div.childNodes[1].nodeValue === 'two' &&
        div.childNodes.length === 2
      );
    }
  },{
    name: 'remove',
    test: function () {
      var div = create('div'),
          first = div.appendChild(create('div'));
      wru.assert(
        'If the context object does not have a parent, terminate these steps',
        !div.remove()
      );
      first.remove();
      wru.assert(
        "Remove the context object from the context object's parent.",
        div.childNodes.length === 0
      );
    }
  },{
    name: 'DOMTokenList',
    test: function () {
      wru.assert('it exists', create('div').classList);
    }
  },{
    name: 'DOMTokenList#length',
    test: function () {
      var div = create('div'),
          classList = div.classList;
      wru.assert('no tokens', classList.length === 0);
      div.className = 'a b c';
      classList = div.classList;
      wru.assert('3 tokens', classList.length === 3);
      classList.remove('a', 'b');
      wru.assert('1 token', classList.length === 1);
      classList.remove('c');
      wru.assert('no tokens', classList.length === 0);
    }
  },{
    name: 'DOMTokenList#item(i)',
    test: function () {
      var div = create('div'),
          classList = div.classList;
      wru.assert('returns null', classList.item(0) === null);
      // ASHA returns empty string in here o_O
      wru.assert('returns falsy', !classList[0]);
      classList.add('z');
      wru.assert('returns z', classList.item(0) === 'z');
      wru.assert('returns [] z', classList[0] == 'z');
      classList.add('Z');
      wru.assert('returns Z', classList.item(1) === 'Z');
      wru.assert('returns [] Z', classList[1] == 'Z');
      wru.assert('returns still z', classList.item(0) === 'z');
      wru.assert('returns still [] z', classList[0] == 'z');
    }
  },{
    name: 'DOMTokenList#contains(token)',
    test: function () {
      var div = create('div'),
          classList = div.classList;
      wru.assert('no bla', classList.contains('bla') === false);
      div.className = 'bla';
      classList = div.classList;
      wru.assert('yes bla', classList.contains('bla') === true);
      wru.assert('no Bla', classList.contains('Bla') === false);
      classList.add('Bla');
      wru.assert('yes Bla', classList.contains('Bla') === true);
      wru.assert('still bla', classList.contains('bla') === true);
      try {
        classList.contains('');
      } catch(e) {
        wru.assert('throws with empty strings', 1);
        try {
          classList.contains('a ');
        } catch(e) {
          wru.assert('throws with spaces', 1);
        }
      }
    }
  }, {
    name: 'DOMTokenList#add(tokens...)',
    test: function () {
      var div = create('div'),
          classList = div.classList;
      classList.add('a', 'b', 'c');
      wru.assert('added all the things', div.className === 'a\x20b\x20c');
      classList.add('z');
      wru.assert('added z too', div.className === 'a\x20b\x20c\x20z');
      classList.add('b');
      wru.assert('did not add b again', div.className === 'a\x20b\x20c\x20z');
    }
  },{
    name: 'DOMTokenList#remove(tokens...)',
    test: function () {
      var div = create('div'),
          classList;
      div.className = 'a\x20b\x20c';
      classList = div.classList;
      classList.remove('a', 'c');
      wru.assert('removed two tokens', div.className === 'b');
      classList.remove('b');
      wru.assert('removed last token', div.className === '');
      classList.remove('b');
      wru.assert('nothing happened', div.className === '');
    }
  }, {
    name: 'DOMTokenList#toggle(token, force)',
    test: function () {
      var div = create('div'),
          classList = div.classList;
      classList.add('a');
      wru.assert('If token is a case-sensitive match for one of tokens and force is not true (either false or omitted)',
        classList.toggle('a') === false && classList.length === 0);
      classList.add('b');
      wru.assert('If token is a case-sensitive match for one of tokens and force is true (neither false nor omitted)',
        classList.toggle('b', true) && classList.length === 1 && classList[0] === 'b');
      wru.assert('If force is false (neither true nor omitted)',
        classList.toggle('z', false) === false && !classList.contains('z'));
      wru.assert('If force is true',
        classList.toggle('z', true) === true && classList.contains('z'));
      wru.assert('If force is omitted same as true',
        classList.toggle('t', true) === true && classList.contains('t'));
    }
  }, {
    name: 'DOMTokenList#toString()',
    test: function () {
      var div = create('div');
      div.className = 'a\x20b\x20c\x20d';
      wru.assert('same as classname', div.className == div.classList);
    }
  }, {
    name: 'CustomEvent',
    test: function () {
      var
        detail = {},
        e = new CustomEvent('type')
      ;
      wru.assert('right type', e.type === 'type');
      wru.assert('detail not attached', e.detail === null);
      wru.assert('not cancelable', e.cancelable === false);
      wru.assert('not bubbling', e.bubbles === false);
      e.initCustomEvent('retype', true, true, detail);
      // TODO: WTF should happen here ?
      //  FF says type, other redefine ergardless
      //  wru.assert('right type', e.type === 'retype');
      wru.assert('detail attached', e.detail === detail);
      wru.assert('not cancelable', e.cancelable === true);
      wru.assert('not bubbling', e.bubbles === true);
      e = new CustomEvent('other-type', {
        cancelable: true,
        bubbles: true,
        detail: detail
      });
      wru.assert('right type', e.type === 'other-type');
      wru.assert('detail attached', e.detail === detail);
      wru.assert('not cancelable', e.cancelable === true);
      wru.assert('not bubbling', e.bubbles === true);
    }
  }, {
    name: 'CustomEvent#initCustomEvent',
    test: function () {
      try {
        new CustomEvent();
      } catch(e) {
        wru.assert('expected');
        e = new CustomEvent('type only');
        wru.assert('right type', e.type === 'type only');
        wru.assert('detail not attached', e.detail === null);
        wru.assert('not cancelable', e.cancelable === false);
        wru.assert('not bubbling', e.bubbles === false);
        e.initCustomEvent(e.type, true, true, 123);
        wru.assert('still right type', e.type === 'type only');
        wru.assert('detail attached', e.detail === 123);
        wru.assert('not cancelable', e.cancelable === true);
        wru.assert('not bubbling', e.bubbles === true);
      }
    }
  }, {
    name: 'CustomEvent is dispatchable',
    test: function () {
      var detail = {};
      document.addEventListener('what:ever', wru.async(function (e) {
        wru.assert('right detail', e.detail === detail);
      }));
      document.dispatchEvent(new CustomEvent('what:ever', {detail: detail}));
    }
  }
]);<commit_msg>iOS7 does not fail if no type is passed<commit_after>//remove:
var main = require('../build/dom4.node.js');
//:remove

function create(name) {
  return document.createElement(name);
}

wru.test([
  {
    name: "prototype has methods",
    test: function () {
      var div = create('div');
      wru.assert('.prepend()', div.prepend);
      wru.assert('.append()', div.append);
      wru.assert('.before()', div.before);
      wru.assert('.after()', div.after);
      wru.assert('.replace()', div.replace);
      wru.assert('.remove()', div.remove);
    }
  },{
    name: 'prepend',
    test: function () {
      var div = create('div'),
          first = div.appendChild(create('div')),
          another = create('div');
      div.prepend(another);
      wru.assert(
        "Pre-insert node into the context object before the context object's first child.",
        div.firstChild === another &&
        div.lastChild === first
      );
      div.prepend(
        create('one'),
        'two',
        create('three')
      );
      wru.assert(
        'works with multiple nodes',
        div.childNodes.length === 5 &&
        div.firstChild.nodeName.toLowerCase() === 'one' &&
        div.childNodes[1].nodeType !== 1 &&
        div.childNodes[1].nodeValue === 'two' &&
        div.childNodes[2].nodeName.toLowerCase() === 'three' &&
        div.childNodes[3] === another &&
        div.childNodes[4] === first
      );
    }
  },{
    name: 'append',
    test: function () {
      var div = create('div'),
          first = div.appendChild(create('div')),
          another = create('div');
      div.append(another);
      wru.assert(
        "Append node to the context object.",
        div.firstChild === first &&
        div.lastChild === another
      );
      div.append(
        create('one'),
        'two',
        create('three')
      );
      wru.assert(
        'works with multiple nodes',
        div.childNodes[0] === first &&
        div.childNodes[1] === another &&
        div.childNodes[2].nodeName.toLowerCase() === 'one' &&
        div.childNodes[3].nodeType !== 1 &&
        div.childNodes[3].nodeValue === 'two' &&
        div.childNodes[4].nodeName.toLowerCase() === 'three'
      );
    }
  },{
    name: 'before',
    test: function () {
      var div = create('div'),
          first = div.appendChild(create('div')),
          node = create('div');
      wru.assert(
        'If the context object does not have a parent, terminate these steps',
        !div.before(node)
      );
      first.before(node);
      wru.assert(
        "Pre-insert node into the context object's parent before the context object.",
        div.firstChild === node
      );
      first.before(create('one'), 'two');
      wru.assert(
        'works with multiple nodes',
        div.childNodes[0] === node &&
        div.childNodes[1].nodeName.toLowerCase() === 'one' &&
        div.childNodes[2].nodeType !== 1 &&
        div.childNodes[2].nodeValue === 'two' &&
        div.childNodes[3] === first
      );
    }
  },{
    name: 'after',
    test: function () {
      var div = create('div'),
          first = div.appendChild(create('div')),
          node = create('div');
      wru.assert(
        'If the context object does not have a parent, terminate these steps',
        !div.after(node)
      );
      first.after(node);
      wru.assert(
        "Pre-insert node into the context object's parent before the context object's next sibling.",
        div.lastChild === node
      );
      first.after(create('one'), 'two');
      wru.assert(
        'works with multiple nodes',
        div.childNodes[0] === first &&
        div.childNodes[1].nodeName.toLowerCase() === 'one' &&
        div.childNodes[2].nodeType !== 1 &&
        div.childNodes[2].nodeValue === 'two' &&
        div.childNodes[3] === node
      );
    }
  },{
    name: 'replace',
    test: function () {
      var div = create('div'),
          first = div.appendChild(create('div')),
          node = create('div');
      wru.assert(
        'If the context object does not have a parent, terminate these steps',
        !div.replace(node)
      );
      first.replace(node);
      wru.assert(
        "Replace the context object with node within the context object's parent.",
        div.firstChild === node &&
        div.childNodes.length === 1
      );
      node.replace(create('one'), 'two');
      wru.assert(
        'works with multiple nodes',
        div.childNodes[0].nodeName.toLowerCase() === 'one' &&
        div.childNodes[1].nodeType !== 1 &&
        div.childNodes[1].nodeValue === 'two' &&
        div.childNodes.length === 2
      );
    }
  },{
    name: 'remove',
    test: function () {
      var div = create('div'),
          first = div.appendChild(create('div'));
      wru.assert(
        'If the context object does not have a parent, terminate these steps',
        !div.remove()
      );
      first.remove();
      wru.assert(
        "Remove the context object from the context object's parent.",
        div.childNodes.length === 0
      );
    }
  },{
    name: 'DOMTokenList',
    test: function () {
      wru.assert('it exists', create('div').classList);
    }
  },{
    name: 'DOMTokenList#length',
    test: function () {
      var div = create('div'),
          classList = div.classList;
      wru.assert('no tokens', classList.length === 0);
      div.className = 'a b c';
      classList = div.classList;
      wru.assert('3 tokens', classList.length === 3);
      classList.remove('a', 'b');
      wru.assert('1 token', classList.length === 1);
      classList.remove('c');
      wru.assert('no tokens', classList.length === 0);
    }
  },{
    name: 'DOMTokenList#item(i)',
    test: function () {
      var div = create('div'),
          classList = div.classList;
      wru.assert('returns null', classList.item(0) === null);
      // ASHA returns empty string in here o_O
      wru.assert('returns falsy', !classList[0]);
      classList.add('z');
      wru.assert('returns z', classList.item(0) === 'z');
      wru.assert('returns [] z', classList[0] == 'z');
      classList.add('Z');
      wru.assert('returns Z', classList.item(1) === 'Z');
      wru.assert('returns [] Z', classList[1] == 'Z');
      wru.assert('returns still z', classList.item(0) === 'z');
      wru.assert('returns still [] z', classList[0] == 'z');
    }
  },{
    name: 'DOMTokenList#contains(token)',
    test: function () {
      var div = create('div'),
          classList = div.classList;
      wru.assert('no bla', classList.contains('bla') === false);
      div.className = 'bla';
      classList = div.classList;
      wru.assert('yes bla', classList.contains('bla') === true);
      wru.assert('no Bla', classList.contains('Bla') === false);
      classList.add('Bla');
      wru.assert('yes Bla', classList.contains('Bla') === true);
      wru.assert('still bla', classList.contains('bla') === true);
      try {
        classList.contains('');
      } catch(e) {
        wru.assert('throws with empty strings', 1);
        try {
          classList.contains('a ');
        } catch(e) {
          wru.assert('throws with spaces', 1);
        }
      }
    }
  }, {
    name: 'DOMTokenList#add(tokens...)',
    test: function () {
      var div = create('div'),
          classList = div.classList;
      classList.add('a', 'b', 'c');
      wru.assert('added all the things', div.className === 'a\x20b\x20c');
      classList.add('z');
      wru.assert('added z too', div.className === 'a\x20b\x20c\x20z');
      classList.add('b');
      wru.assert('did not add b again', div.className === 'a\x20b\x20c\x20z');
    }
  },{
    name: 'DOMTokenList#remove(tokens...)',
    test: function () {
      var div = create('div'),
          classList;
      div.className = 'a\x20b\x20c';
      classList = div.classList;
      classList.remove('a', 'c');
      wru.assert('removed two tokens', div.className === 'b');
      classList.remove('b');
      wru.assert('removed last token', div.className === '');
      classList.remove('b');
      wru.assert('nothing happened', div.className === '');
    }
  }, {
    name: 'DOMTokenList#toggle(token, force)',
    test: function () {
      var div = create('div'),
          classList = div.classList;
      classList.add('a');
      wru.assert('If token is a case-sensitive match for one of tokens and force is not true (either false or omitted)',
        classList.toggle('a') === false && classList.length === 0);
      classList.add('b');
      wru.assert('If token is a case-sensitive match for one of tokens and force is true (neither false nor omitted)',
        classList.toggle('b', true) && classList.length === 1 && classList[0] === 'b');
      wru.assert('If force is false (neither true nor omitted)',
        classList.toggle('z', false) === false && !classList.contains('z'));
      wru.assert('If force is true',
        classList.toggle('z', true) === true && classList.contains('z'));
      wru.assert('If force is omitted same as true',
        classList.toggle('t', true) === true && classList.contains('t'));
    }
  }, {
    name: 'DOMTokenList#toString()',
    test: function () {
      var div = create('div');
      div.className = 'a\x20b\x20c\x20d';
      wru.assert('same as classname', div.className == div.classList);
    }
  }, {
    name: 'CustomEvent',
    test: function () {
      var
        detail = {},
        e = new CustomEvent('type')
      ;
      wru.assert('right type', e.type === 'type');
      wru.assert('detail not attached', e.detail === null);
      wru.assert('not cancelable', e.cancelable === false);
      wru.assert('not bubbling', e.bubbles === false);
      e.initCustomEvent('retype', true, true, detail);
      // TODO: WTF should happen here ?
      //  FF says type, other redefine ergardless
      //  wru.assert('right type', e.type === 'retype');
      wru.assert('detail attached', e.detail === detail);
      wru.assert('not cancelable', e.cancelable === true);
      wru.assert('not bubbling', e.bubbles === true);
      e = new CustomEvent('other-type', {
        cancelable: true,
        bubbles: true,
        detail: detail
      });
      wru.assert('right type', e.type === 'other-type');
      wru.assert('detail attached', e.detail === detail);
      wru.assert('not cancelable', e.cancelable === true);
      wru.assert('not bubbling', e.bubbles === true);
    }
  }, {
    name: 'CustomEvent#initCustomEvent',
    test: function () {
      e = new CustomEvent('type only');
      wru.assert('right type', e.type === 'type only');
      wru.assert('detail not attached', e.detail === null);
      wru.assert('not cancelable', e.cancelable === false);
      wru.assert('not bubbling', e.bubbles === false);
      e.initCustomEvent(e.type, true, true, 123);
      wru.assert('still right type', e.type === 'type only');
      wru.assert('detail attached', e.detail === 123);
      wru.assert('not cancelable', e.cancelable === true);
      wru.assert('not bubbling', e.bubbles === true);
    }
  }, {
    name: 'CustomEvent is dispatchable',
    test: function () {
      var detail = {};
      document.addEventListener('what:ever', wru.async(function (e) {
        wru.assert('right detail', e.detail === detail);
      }));
      document.dispatchEvent(new CustomEvent('what:ever', {detail: detail}));
    }
  }
]);<|endoftext|>


<commit_before>var postLoc = "http://news.ycombinator.com/r"

$('a[href^="reply?"]').click(function(e) {
	e.preventDefault();
	$(this).hide();
	link = 'http://news.ycombinator.com/' + $(this).attr('href');
	$(this).after(
		'
 \
		<textarea rows="4" cols="60" style="height:60px;" /> \
		<input type ="submit" value="Reply" class="rbutton" \
		 style="position:absolute;bottom:0px;left:0px;" /> \

'
	);
	$(this).parent().find('.rbutton').attr('data', link);
});

$('.rbutton').live('click', function(e) {
	e.preventDefault();
	link = $(this).attr('data');
	text = $(this).prev().val();
	postCommentTo(link, text, $(this));
});

function postCommentTo(link, text, button) {
	disableButtonAndBox(button);
	$.ajax({
		accepts: "text/html",
		url : link
	}).success(function(html) {
		input = $(html).find('input');
		fnid = input.attr('value');
		sendComment(fnid, text);
	}).error(function(xhr, status, error) {
		enableButtonAndBox(button);
	});
}

function disableButtonAndBox(button) {
	button.attr('disabled','disabled');
	button.prev().attr('disabled', 'disabled');
}

function enableButtonAndBox(button) {
	button.removeAttr('disabled');
	button.prev().removeAttr('disabled');
}

function sendComment(fnidarg, textarg) {
	$.post(
		postLoc,
		{fnid : fnidarg, text: textarg }
	).complete(function(a) {
		window.location.reload(true);
	});
}
<commit_msg>Code style change, preparing for merge with HNES<commit_after>var InlineReply = {

	init: function() {
		$('a[href^="reply?"]').click(function(e) {
			e.preventDefault();
			$(this).hide();
			link = 'http://news.ycombinator.com/' + $(this).attr('href');
			$(this).after(
				'
 \
				<textarea rows="4" cols="60" style="height:60px;" /> \
				<input type ="submit" value="Reply" class="rbutton" \
				 style="position:absolute;bottom:0px;left:0px;" /> \

'
			);
			$(this).parent().find('.rbutton').attr('data', link);
		});

		$('.rbutton').live('click', function(e) {
			e.preventDefault();
			link = $(this).attr('data');
			text = $(this).prev().val();
			InlineReply.postCommentTo(link, text, $(this));
		});
	},

	postCommentTo: function(link, text, button) {
		InlineReply.disableButtonAndBox(button);
		$.ajax({
			accepts: "text/html",
			url : link
		}).success(function(html) {
			input = $(html).find('input');
			fnid = input.attr('value');
			InlineReply.sendComment(fnid, text);
		}).error(function(xhr, status, error) {
			InlineReply.enableButtonAndBox(button);
		});
	},

	sendComment: function(fnidarg, textarg) {
		$.post(
			"http://news.ycombinator.com/r",
			{fnid : fnidarg, text: textarg }
		).complete(function(a) {
			window.location.reload(true);
		});
	},

	disableButtonAndBox: function(button) {
		button.attr('disabled','disabled');
		button.prev().attr('disabled', 'disabled');
	},

	enableButtonAndBox: function(button) {
		button.removeAttr('disabled');
		button.prev().removeAttr('disabled');
	}
}

InlineReply.init();
<|endoftext|>


<commit_before>/* global describe, it, after, before, afterEach, beforeEach */

var path = require('path');
var rimraf = require('rimraf');
var gutil = require('gulp-util');

// just so this file is clean
var util = require('./_util');

// omit logging
gutil.log = function(){};

var git = require('../');

describe('gulp-git', function(){

  var testSuite = util.testSuite;

  testSuite.forEach(function(file){
    var suite = path.basename(file, path.extname(file));
    describe(suite, function(){
      // the actual suite code
      require('./'+file)(git, util);
    });
  });

  // wipe
  after(function(done){
    rimraf('test/repo', function(err){
      if(err) return done(err);
      done();
    });
  });
});
<commit_msg>update fn call<commit_after>/* global describe, it, after, before, afterEach, beforeEach */

var path = require('path');
var rimraf = require('rimraf');
var gutil = require('gulp-util');
var git = require('../');

// just so this file is clean
var util = require('./_util');

// omit logging
gutil.log = function(){};

describe('gulp-git', function(){

  var testSuite = util.testSuite();

  testSuite.forEach(function(file){
    var suite = path.basename(file, path.extname(file));
    describe(suite, function(){
      // the actual suite code
      require('./'+file)(git, util);
    });
  });

  // wipe
  after(function(done){
    rimraf('test/repo', function(err){
      if(err) return done(err);
      done();
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

/*global describe, beforeEach, it*/

var chai  = require('chai');
var spies = require('chai-spies');
chai.use(spies);
var should = chai.should();
var spy    = chai.spy;

var angular = require('angular');
require('angular-mocks/ngMock');

describe('angular-http-etag', function () {
  var $http, $httpBackend, $cacheFactory, testModule, serverData,
      ifEtagIs, ifEtagIsNot, onCache, onSuccess, onError;

  testModule = angular.module('test', ['http-etag']).config(['httpEtagProvider', function (httpEtagProvider) {
    httpEtagProvider.cache('test');
  }]);

  beforeEach(angular.mock.module(require('../')));
  beforeEach(angular.mock.module('test'));

  beforeEach(angular.mock.inject(function ($injector) {
    $http         = $injector.get('$http');
    $httpBackend  = $injector.get('$httpBackend');
    $cacheFactory = $injector.get('$cacheFactory');

    onCache   = spy('$http().cache',   angular.noop);
    onSuccess = spy('$http().success', angular.noop);
    onError   = spy('$http().error',   angular.noop);

    ifEtagIs = function (etag) {
      return function (headers) { return headers['If-None-Match'] === etag; };
    };

    ifEtagIsNot = function (etag) {
      return function (headers) { return headers['If-None-Match'] !== etag; };
    };

    serverData = { id:1, content:'Test data' };

    $httpBackend
      .whenGET('/1.json', ifEtagIsNot('1'))
      .respond(serverData, { 'etag': '1' });
    $httpBackend
      .whenGET('/1.json', ifEtagIs('1'))
      .respond(304);

    $httpBackend
      .whenGET('/1.json?param=test', ifEtagIsNot('1'))
      .respond(serverData, { 'etag': '1' });
    $httpBackend
      .whenGET('/1.json?param=test', ifEtagIs('1'))
      .respond(304);

  }));

  it('should cache ETags', function () {
    $http.get('/1.json', { etag: true })
      .cache(onCache)
      .success(onSuccess);
    $httpBackend.flush();

    $http.get('/1.json', { etag: true })
      .cache(onCache)
      .error(onError);
    $httpBackend.flush();

    onCache.should.have.been.called.once;
    onSuccess.should.have.been.called.once;
    onError.should.have.been.called.once;
  });

  it('should cache data', function () {
    var userData;

    $http({ method:'GET', url: '/1.json', etag: true })
      .cache(onCache);
    $httpBackend.flush();

    $http({ method:'GET', url: '/1.json', etag: true })
      .cache(function (data) {
        userData = data;
      });
    $httpBackend.flush();

    onCache.should.not.have.been.called();
    userData.should.deep.equal(serverData);
  });

  it('should cache data on specified cache ID', function () {
    var userData;

    $http.get('/1.json', { etag: 'test' });
    $httpBackend.flush();

    $http.get('/1.json', { etag: 'test' })
      .cache(function (data) {
        userData = data;
      })
      .error(onError);
    $httpBackend.flush();

    $http.get('/1.json', { etag: true })
      .success(onSuccess);
    $httpBackend.flush();

    userData.should.deep.equal(serverData);
    onSuccess.should.have.been.called.once;
    onError.should.have.been.called.once;
  });

  it('should function normally without ETag config property', function () {
    $http.get('/1.json').success(onSuccess);
    $httpBackend.flush();

    $http({ method: 'GET', url: '/1.json'}).error(onError);
    $httpBackend.flush();

    onSuccess.should.have.been.called.once;
    onError.should.not.have.been.called.once;
  });

  it('should cache ETags for requests with query params', function () {
    $http.get('/1.json', { etag: true, params: { param: 'test' } })
      .success(onSuccess);
    $httpBackend.flush();

    $http.get('/1.json', { etag: true, params: { param: 'test' } })
      .error(onError);
    $httpBackend.flush();

    onSuccess.should.have.been.once;
    onError.should.have.been.once;
  });

});
<commit_msg>More vigorously test query param stringification<commit_after>'use strict';

/*global describe, beforeEach, it*/

var chai  = require('chai');
var spies = require('chai-spies');
chai.use(spies);
var should = chai.should();
var spy    = chai.spy;

var angular = require('angular');
require('angular-mocks/ngMock');

describe('angular-http-etag', function () {
  var $http, $httpBackend, $cacheFactory, testModule, serverData,
      ifEtagIs, ifEtagIsNot, onCache, onSuccess, onError;

  testModule = angular.module('test', ['http-etag']).config(['httpEtagProvider', function (httpEtagProvider) {
    httpEtagProvider.cache('test');
  }]);

  beforeEach(angular.mock.module(require('../')));
  beforeEach(angular.mock.module('test'));

  beforeEach(angular.mock.inject(function ($injector) {
    $http         = $injector.get('$http');
    $httpBackend  = $injector.get('$httpBackend');
    $cacheFactory = $injector.get('$cacheFactory');

    onCache   = spy('$http().cache',   angular.noop);
    onSuccess = spy('$http().success', angular.noop);
    onError   = spy('$http().error',   angular.noop);

    ifEtagIs = function (etag) {
      return function (headers) { return headers['If-None-Match'] === etag; };
    };

    ifEtagIsNot = function (etag) {
      return function (headers) { return headers['If-None-Match'] !== etag; };
    };

    serverData = { id:1, content:'Test data' };

    $httpBackend
      .whenGET('/1.json', ifEtagIsNot('1'))
      .respond(serverData, { 'etag': '1' });
    $httpBackend
      .whenGET('/1.json', ifEtagIs('1'))
      .respond(304);

    $httpBackend
      .whenGET('/1.json?x=1&y=2&y=3', ifEtagIsNot('1'))
      .respond(serverData, { 'etag': '1' });
    $httpBackend
      .whenGET('/1.json?x=1&y=2&y=3', ifEtagIs('1'))
      .respond(304);

  }));

  it('should cache ETags', function () {
    $http.get('/1.json', { etag: true })
      .cache(onCache)
      .success(onSuccess);
    $httpBackend.flush();

    $http.get('/1.json', { etag: true })
      .cache(onCache)
      .error(onError);
    $httpBackend.flush();

    onCache.should.have.been.called.once;
    onSuccess.should.have.been.called.once;
    onError.should.have.been.called.once;
  });

  it('should cache data', function () {
    var userData;

    $http({ method:'GET', url: '/1.json', etag: true })
      .cache(onCache);
    $httpBackend.flush();

    $http({ method:'GET', url: '/1.json', etag: true })
      .cache(function (data) {
        userData = data;
      });
    $httpBackend.flush();

    onCache.should.not.have.been.called();
    userData.should.deep.equal(serverData);
  });

  it('should cache data on specified cache ID', function () {
    var userData;

    $http.get('/1.json', { etag: 'test' });
    $httpBackend.flush();

    $http.get('/1.json', { etag: 'test' })
      .cache(function (data) {
        userData = data;
      })
      .error(onError);
    $httpBackend.flush();

    $http.get('/1.json', { etag: true })
      .success(onSuccess);
    $httpBackend.flush();

    userData.should.deep.equal(serverData);
    onSuccess.should.have.been.called.once;
    onError.should.have.been.called.once;
  });

  it('should function normally without ETag config property', function () {
    $http.get('/1.json').success(onSuccess);
    $httpBackend.flush();

    $http({ method: 'GET', url: '/1.json'}).error(onError);
    $httpBackend.flush();

    onSuccess.should.have.been.called.once;
    onError.should.not.have.been.called.once;
  });

  it('should cache ETags for requests with query params', function () {
    $http.get('/1.json', { etag: true, params: { x:1, y:[2, 3] } })
      .success(onSuccess);
    $httpBackend.flush();

    $http.get('/1.json', { etag: true, params: { x:1, y:[2, 3] } })
      .error(onError);
    $httpBackend.flush();

    $http.get('/1.json', { etag: true })
      .success(onSuccess);
    $httpBackend.flush();

    onSuccess.should.have.been.called.twice;
    onError.should.have.been.called.once;
  });

});
<|endoftext|>