<commit_before>(function () {
    'use strict';

    function Play() {}
    Play.prototype = {
        create: function () {
            playerTurn = true;
            game.stage.backgroundColor = '#333333';
            this.score = 0;

            /* Transition */

            /* Audio */
            this.fx = game.add.audio('sfx');
            this.soundtrack = game.add.audio('soundtrack');
            //this.soundtrack.play('',0,1,true);

            // Show FPS
            this.game.time.advancedTiming = true;

            /* Groups */
            var margin = 0;
            if (gameWidth > gameHeight) {
                margin = gameHeight * 0.10;
            } else {
                margin = gameWidth * 0.10;
            }

            /* Grid */
            this.grid = new Grid(game, 10, 10, gameWidth, gameHeight, margin, true, true);
            var layers = this.genLayers();
            this.TilePowerUpsG = game.add.group();
            var powerup;

            for (var i = 0; i < powerUps; i++) {
                powerup = this.TilePowerUpsG.create((160 + (50 * i)), 0, 'tiles', 'TilePowerUp');
                powerup.scale.set(0.2);
            }

            for (var i = layers.length - 1; i >= 0; i--) {
                var name = 'layer' + i;
                for (var j = layers[i].length - 1; j >= 0; j--) {
                    var numberType = layers[i][j];
                    switch (numberType) {
                    case 1:
                        name = 'unmovable';
                        break;
                    case 2:
                        name = 'enemy';
                        break;
                    case 3:
                        name = 'final';
                        break;
                    case 4:
                        name = 'collectable';
                        break;
                    case 5:
                        name = 'movable';
                        break;
                    default:
                        break;
                    }
                }
                this.grid.addLayer(layers[i], name, 'tiles', [null, 'TileWall', 'TileEnemy', 'TileExit', 'TilePowerUp', 'TilePlayer']);
            }

            /* Text */
            this.fpsText = this.game.add.text(game.world.width - 50, 10, '0', {
                font: "20px Source Code Pro",
                fill: "#f0f0f0",
                align: "center"
            });

            this.levelText = this.game.add.text(50, 10, 'Level: ' + level, {
                font: "20px Source Code Pro",
                fill: "#f0f0f0",
                align: "center"
            });

            this.fpsText.anchor.set(1.0, 0);

            /* Keyboard */

            this.escKey = game.input.keyboard.addKey(Phaser.Keyboard.ESC);

            game.input.onDown.add(this.move, this);
            this.escKey.onDown.add(function () {
                this.quitGame('mainmenu', true);
            }, this);

            var bdmTransition = game.add.bitmapData(gameWidth, gameHeight);
            bdmTransition.context.fillStyle = 'rgba(50, 50, 50, 1.0)';
            bdmTransition.context.fillRect(0, 0, gameWidth, gameHeight);
            var transition = game.add.sprite(0, 0, bdmTransition);
            var e = game.add.tween(transition);
            e.onStart.add(function () {
                isMoving = true;
            });
            e.onComplete.add(function () {
                isMoving = false;
            })
            e.to({
                alpha: 0
            }, 500, Phaser.Easing.Linear.None, false, 0, 0, false);
            e.start();
        },

        update: function () {
            if (this.game.time.fps !== 0) {
                this.fpsText.setText(this.game.time.fps + ' FPS');
            }
            if (BUSTED) {
                this.quitGame('mainmenu', true);
                console.log('BUSTED !');
            }
        },
        render: function () {
            //game.debug.spriteInfo(this.grid.movableSprite, gameWidth - 500, 50);
            //game.debug.spriteCoords(this.grid.movableSprite, gameWidth - 500, 150);
            //game.debug.spriteCoords(this.grid.marker, gameWidth - 500, 250);
            //game.debug.spriteCoords(this.grid.debugW, gameWidth - 500, 150);
        },

        quitGame: function (_state, _gameOver) {
            playerTurn = false;
            if (!_gameOver) {
                level++;
                BUSTED = false;
                this.grid = {};
                this.fx.play('', 0, 1, false);
                localStorage.setItem('lastScore', this.score);
                game.time.events.remove(this.timer);

                var bdmTransition = game.add.bitmapData(gameWidth, gameHeight);
                bdmTransition.context.fillStyle = 'rgba(50, 50, 50, 1.0)';
                bdmTransition.context.fillRect(0, 0, gameWidth, gameHeight);
                var transition = game.add.sprite(0, 0, bdmTransition);
                transition.alpha = 0;
                var e = game.add.tween(transition);
                e.to({
                    alpha: 1
                }, 500, Phaser.Easing.Linear.None, false, 0, 0, false);
                e.start();
                e.onComplete.add(function () {
                    game.state.start(_state);
                });
            } else {
                powerUps = 0;
                level = 1;
                BUSTED = false;
                var gameOverText = this.game.add.text(game.world.centerX, game.world.centerY, 'Game Over', {
                    font: "50px Source Code Pro",
                    fill: "#fff",
                    align: "center"
                });
                gameOverText.anchor.set(0.5, 0.5);
                gameOverText.alpha = 0;
                var e = game.add.tween(gameOverText);
                e.to({
                    alpha: 1
                }, 1000, Phaser.Easing.Linear.None, false, 0, 0, false);
                e.onComplete.add(this.gameOVerText, this);
                e.start();
            }
        }
    };

    Play.prototype.gameOVerText = function () {
        var bdmTransition = game.add.bitmapData(gameWidth, gameHeight);
        bdmTransition.context.fillStyle = 'rgba(50, 50, 50, 1.0)';
        bdmTransition.context.fillRect(0, 0, gameWidth, gameHeight);
        var transition = game.add.sprite(0, 0, bdmTransition);
        transition.alpha = 0;
        var e = game.add.tween(transition);
        e.to({
            alpha: 1
        }, 500, Phaser.Easing.Linear.None, false, 0, 0, false);
        e.start();
        e.onComplete.add(function () {
            game.state.start('mainmenu');
        });
    };

    Play.prototype.genLayers = function () {
        var layers = [];
        var enemies = [2];
        var elements = [3, 4];
        var matrix1 = [];
        var matrix2 = [];
        var matrix3 = [];
        var matrix4 = [];
        var matrix5 = [];
        var indexes = [];

        for (var i = level - 1; i > 0; i--) {
            enemies.push(2);
        }

        for (var i = 100; i >= 0; i--) {
            matrix1.push(0);
            matrix2.push(0);
            matrix3.push(0);
            matrix4.push(0);
            matrix5.push(0);
        }

        for (var i = 5; i >= 0; i--) {
            matrix1[i + 22] = 1;
            matrix1[i + 72] = 1;

            matrix1[22 + i * 10] = 1;
            matrix1[27 + i * 10] = 1;
        }

        for (var i = matrix1.length - 1; i >= 0; i--) {
            if (matrix1[i] === 1) {
                indexes.push(i);
            }
        }

        Phaser.Utils.shuffle(elements);
        Phaser.Utils.shuffle(indexes);

        for (var i = elements.length - 1; i >= 0; i--) {
            switch (elements[i]) {
            case 2: //Enemies
                matrix2[indexes[i]] = elements[i];
                break;
            case 3: //Final
                matrix3[indexes[i]] = elements[i];
                break;
            case 4: //Collectable
                matrix4[indexes[i]] = elements[i];
                break;
            case 5: //Movable
                matrix5[indexes[i]] = elements[i];
                break;
            }
            matrix1[indexes[i]] = 0;
            if (i > -1) {
                indexes.splice(i, 1);
            }
        }

        for (var i = enemies.length - 1; i >= 0; i--) {
            var enemyLayer = [];
            for (var j = 100; j >= 0; j--) {
                enemyLayer[j] = 0;
            }
            var tmpIndex = indexes.pop();
            matrix1[tmpIndex] = 0;
            enemyLayer[tmpIndex] = 2;
            layers.push(enemyLayer);

        }

        var playerIndex = 0;

        matrix5[playerIndex] = 5;

        if (playerIndex >= 10) {
            if (playerIndex % 10 !== 9) {
                matrix5[playerIndex - 9] = 9;
            }
            if (playerIndex % 10 !== 0) {
                matrix5[playerIndex - 11] = 9;
            }
            matrix5[playerIndex - 10] = 9;
        }

        if (playerIndex % 10 !== 9) {
            matrix5[playerIndex + 1] = 9;
        }
        if (playerIndex % 10 !== 0) {
            matrix5[playerIndex - 1] = 9;
        }

        if (playerIndex < 90) {
            if (playerIndex % 10 !== 0) {
                matrix5[playerIndex + 9] = 9;
            }
            if (playerIndex % 10 !== 9) {
                matrix5[playerIndex + 11] = 9;
            }
            matrix5[playerIndex + 10] = 9;
        }

        layers.push(matrix1);
        layers.push(matrix2);
        layers.push(matrix3);
        layers.push(matrix4);
        layers.push(matrix5);

        return layers;
    };

    Play.prototype.move = function () {
        var shield = false;
        if (this.grid.canMove && playerTurn) {
            if (Math.round(this.grid.movableSprite.x) === Math.round(this.grid.marker.x)) {
                if (Math.round(this.grid.movableSprite.y) < Math.round(this.grid.marker.y)) {
                    this.grid.move(this.grid.movableLayers[0], 'down');
                } else {
                    this.grid.move(this.grid.movableLayers[0], 'up');
                }
            } else if (Math.round(this.grid.movableSprite.y) === Math.round(this.grid.marker.y)) {
                if (Math.round(this.grid.movableSprite.x) < Math.round(this.grid.marker.x)) {
                    this.grid.move(this.grid.movableLayers[0], 'right');
                } else {
                    this.grid.move(this.grid.movableLayers[0], 'left');
                }
            } else if (Math.round(this.grid.movableSprite.y) < Math.round(this.grid.marker.y) && Math.round(this.grid.movableSprite.x) > Math.round(this.grid.marker.x)) {
                this.grid.move(this.grid.movableLayers[0], 'bottomleft');
            } else if (Math.round(this.grid.movableSprite.y) > Math.round(this.grid.marker.y) && Math.round(this.grid.movableSprite.x) > Math.round(this.grid.marker.x)) {
                this.grid.move(this.grid.movableLayers[0], 'topleft');
            } else if (Math.round(this.grid.movableSprite.y) > Math.round(this.grid.marker.y) && Math.round(this.grid.movableSprite.x) < Math.round(this.grid.marker.x)) {
                this.grid.move(this.grid.movableLayers[0], 'topright');
            } else if (Math.round(this.grid.movableSprite.y) < Math.round(this.grid.marker.y) && Math.round(this.grid.movableSprite.x) < Math.round(this.grid.marker.x)) {
                this.grid.move(this.grid.movableLayers[0], 'bottomright');
            }

            if (this.grid.checkLayer(this.grid.marker.x, this.grid.marker.y, 3, this.grid.finalLayers)) {
                this.quitGame('play', false);
            }

            if (this.grid.checkLayer(this.grid.marker.x, this.grid.marker.y, 4, this.grid.collectableLayers)) {
                var spriteArray = this.grid.collect(this.grid.marker.x, this.grid.marker.y, this.grid.collectableLayers);
                for (var i = spriteArray.length - 1; i >= 0; i--) {
                    var tmp = spriteArray[i];
                    var s = game.add.tween(tmp.scale);
                    s.to({
                        x: 0.2,
                        y: 0.2
                    }, 250, Phaser.Easing.Linear.None);
                    s.start();
                    var e = game.add.tween(tmp);
                    e.onStart.add(function () {
                        isMoving = true;
                    });
                    e.onComplete.add(function () {
                        isMoving = false;
                    })
                    e.to({
                        x: (160 + (50 * powerUps)),
                        y: 0
                    }, 250, Phaser.Easing.Linear.None, false, 0, 0, false);
                    e.start();
                    powerUps++;
                    shield = true;
                }
            }
            for (var i = this.grid.enemiesLayers.length - 1; i >= 0; i--) {
                this.grid.enemiesLayers[i].callAll('turn');
            }
        }
    }
    PlayS = Play;
}());<commit_msg>When game over you go to leader boards screen<commit_after>(function () {
    'use strict';

    function Play() {}
    Play.prototype = {
        create: function () {
            playerTurn = true;
            game.stage.backgroundColor = '#333333';
            this.score = 0;

            /* Transition */

            /* Audio */
            this.fx = game.add.audio('sfx');
            this.soundtrack = game.add.audio('soundtrack');
            //this.soundtrack.play('',0,1,true);

            // Show FPS
            this.game.time.advancedTiming = true;

            /* Groups */
            var margin = 0;
            if (gameWidth > gameHeight) {
                margin = gameHeight * 0.10;
            } else {
                margin = gameWidth * 0.10;
            }

            /* Grid */
            this.grid = new Grid(game, 10, 10, gameWidth, gameHeight, margin, true, true);
            var layers = this.genLayers();
            this.TilePowerUpsG = game.add.group();
            var powerup;

            for (var i = 0; i < powerUps; i++) {
                powerup = this.TilePowerUpsG.create((160 + (50 * i)), 0, 'tiles', 'TilePowerUp');
                powerup.scale.set(0.2);
            }

            for (var i = layers.length - 1; i >= 0; i--) {
                var name = 'layer' + i;
                for (var j = layers[i].length - 1; j >= 0; j--) {
                    var numberType = layers[i][j];
                    switch (numberType) {
                        case 1:
                        name = 'unmovable';
                        break;
                        case 2:
                        name = 'enemy';
                        break;
                        case 3:
                        name = 'final';
                        break;
                        case 4:
                        name = 'collectable';
                        break;
                        case 5:
                        name = 'movable';
                        break;
                        default:
                        break;
                    }
                }
                this.grid.addLayer(layers[i], name, 'tiles', [null, 'TileWall', 'TileEnemy', 'TileExit', 'TilePowerUp', 'TilePlayer']);
            }

            /* Text */
            this.fpsText = this.game.add.text(game.world.width - 50, 10, '0', {
                font: "20px Source Code Pro",
                fill: "#f0f0f0",
                align: "center"
            });

            this.levelText = this.game.add.text(50, 10, 'Level: ' + level, {
                font: "20px Source Code Pro",
                fill: "#f0f0f0",
                align: "center"
            });

            this.fpsText.anchor.set(1.0, 0);

            /* Keyboard */

            this.escKey = game.input.keyboard.addKey(Phaser.Keyboard.ESC);

            game.input.onDown.add(this.move, this);
            this.escKey.onDown.add(function () {
                this.quitGame('mainmenu', true);
            }, this);

            var bdmTransition = game.add.bitmapData(gameWidth, gameHeight);
            bdmTransition.context.fillStyle = 'rgba(50, 50, 50, 1.0)';
            bdmTransition.context.fillRect(0, 0, gameWidth, gameHeight);
            var transition = game.add.sprite(0, 0, bdmTransition);
            var e = game.add.tween(transition);
            e.onStart.add(function () {
                isMoving = true;
            });
            e.onComplete.add(function () {
                isMoving = false;
            })
            e.to({
                alpha: 0
            }, 500, Phaser.Easing.Linear.None, false, 0, 0, false);
            e.start();
        },

        update: function () {
            if (this.game.time.fps !== 0) {
                this.fpsText.setText(this.game.time.fps + ' FPS');
            }
            if (BUSTED) {
                this.quitGame('leaderboards', true);
                console.log('BUSTED !');
            }
        },
        render: function () {
            //game.debug.spriteInfo(this.grid.movableSprite, gameWidth - 500, 50);
            //game.debug.spriteCoords(this.grid.movableSprite, gameWidth - 500, 150);
            //game.debug.spriteCoords(this.grid.marker, gameWidth - 500, 250);
            //game.debug.spriteCoords(this.grid.debugW, gameWidth - 500, 150);
        },

        quitGame: function (_state, _gameOver) {
            playerTurn = false;
            var bdmTransition = game.add.bitmapData(gameWidth, gameHeight);
            bdmTransition.context.fillStyle = 'rgba(50, 50, 50, 1.0)';
            bdmTransition.context.fillRect(0, 0, gameWidth, gameHeight);
            var transition = game.add.sprite(0, 0, bdmTransition);
            transition.alpha = 0;
            if (!_gameOver) {
                level++;

            } else {
                powerUps = 0;
                level = 1;
                var gameOverText = this.game.add.text(game.world.centerX, game.world.centerY, 'Game Over', {
                    font: "50px Source Code Pro",
                    fill: "#fff",
                    align: "center"
                });
                gameOverText.anchor.set(0.5, 0.5);
                gameOverText.alpha = 0;
            }
            var e = game.add.tween(transition);
            e.to({
                alpha: 1
            }, 500, Phaser.Easing.Linear.None, false, 0, 0, false);
            e.start();
            e.onComplete.add(function () {
                game.state.start(_state);
            });

            BUSTED = false;
            this.grid = {};
            this.fx.play('', 0, 1, false);
            game.time.events.remove(this.timer);
        }
    };

    Play.prototype.gameOVerText = function () {
        var bdmTransition = game.add.bitmapData(gameWidth, gameHeight);
        bdmTransition.context.fillStyle = 'rgba(50, 50, 50, 1.0)';
        bdmTransition.context.fillRect(0, 0, gameWidth, gameHeight);
        var transition = game.add.sprite(0, 0, bdmTransition);
        transition.alpha = 0;
        var e = game.add.tween(transition);
        e.to({
            alpha: 1
        }, 500, Phaser.Easing.Linear.None, false, 0, 0, false);
        e.start();
        e.onComplete.add(function () {
            game.state.start('mainmenu');
        });
    };

    Play.prototype.genLayers = function () {
        var layers = [];
        var enemies = [2];
        var elements = [3, 4];
        var matrix1 = [];
        var matrix2 = [];
        var matrix3 = [];
        var matrix4 = [];
        var matrix5 = [];
        var indexes = [];

        for (var i = level - 1; i > 0; i--) {
            enemies.push(2);
        }

        for (var i = 100; i >= 0; i--) {
            matrix1.push(0);
            matrix2.push(0);
            matrix3.push(0);
            matrix4.push(0);
            matrix5.push(0);
        }

        for (var i = 5; i >= 0; i--) {
            matrix1[i + 22] = 1;
            matrix1[i + 72] = 1;

            matrix1[22 + i * 10] = 1;
            matrix1[27 + i * 10] = 1;
        }

        for (var i = matrix1.length - 1; i >= 0; i--) {
            if (matrix1[i] === 1) {
                indexes.push(i);
            }
        }

        Phaser.Utils.shuffle(elements);
        Phaser.Utils.shuffle(indexes);

        for (var i = elements.length - 1; i >= 0; i--) {
            switch (elements[i]) {
            case 2: //Enemies
            matrix2[indexes[i]] = elements[i];
            break;
            case 3: //Final
            matrix3[indexes[i]] = elements[i];
            break;
            case 4: //Collectable
            matrix4[indexes[i]] = elements[i];
            break;
            case 5: //Movable
            matrix5[indexes[i]] = elements[i];
            break;
        }
        matrix1[indexes[i]] = 0;
        if (i > -1) {
            indexes.splice(i, 1);
        }
    }

    for (var i = enemies.length - 1; i >= 0; i--) {
        var enemyLayer = [];
        for (var j = 100; j >= 0; j--) {
            enemyLayer[j] = 0;
        }
        var tmpIndex = indexes.pop();
        matrix1[tmpIndex] = 0;
        enemyLayer[tmpIndex] = 2;
        layers.push(enemyLayer);

    }

    var playerIndex = 0;

    matrix5[playerIndex] = 5;

    if (playerIndex >= 10) {
        if (playerIndex % 10 !== 9) {
            matrix5[playerIndex - 9] = 9;
        }
        if (playerIndex % 10 !== 0) {
            matrix5[playerIndex - 11] = 9;
        }
        matrix5[playerIndex - 10] = 9;
    }

    if (playerIndex % 10 !== 9) {
        matrix5[playerIndex + 1] = 9;
    }
    if (playerIndex % 10 !== 0) {
        matrix5[playerIndex - 1] = 9;
    }

    if (playerIndex < 90) {
        if (playerIndex % 10 !== 0) {
            matrix5[playerIndex + 9] = 9;
        }
        if (playerIndex % 10 !== 9) {
            matrix5[playerIndex + 11] = 9;
        }
        matrix5[playerIndex + 10] = 9;
    }

    layers.push(matrix1);
    layers.push(matrix2);
    layers.push(matrix3);
    layers.push(matrix4);
    layers.push(matrix5);

    return layers;
};

Play.prototype.move = function () {
    var shield = false;
    if (this.grid.canMove && playerTurn) {
        if (Math.round(this.grid.movableSprite.x) === Math.round(this.grid.marker.x)) {
            if (Math.round(this.grid.movableSprite.y) < Math.round(this.grid.marker.y)) {
                this.grid.move(this.grid.movableLayers[0], 'down');
            } else {
                this.grid.move(this.grid.movableLayers[0], 'up');
            }
        } else if (Math.round(this.grid.movableSprite.y) === Math.round(this.grid.marker.y)) {
            if (Math.round(this.grid.movableSprite.x) < Math.round(this.grid.marker.x)) {
                this.grid.move(this.grid.movableLayers[0], 'right');
            } else {
                this.grid.move(this.grid.movableLayers[0], 'left');
            }
        } else if (Math.round(this.grid.movableSprite.y) < Math.round(this.grid.marker.y) && Math.round(this.grid.movableSprite.x) > Math.round(this.grid.marker.x)) {
            this.grid.move(this.grid.movableLayers[0], 'bottomleft');
        } else if (Math.round(this.grid.movableSprite.y) > Math.round(this.grid.marker.y) && Math.round(this.grid.movableSprite.x) > Math.round(this.grid.marker.x)) {
            this.grid.move(this.grid.movableLayers[0], 'topleft');
        } else if (Math.round(this.grid.movableSprite.y) > Math.round(this.grid.marker.y) && Math.round(this.grid.movableSprite.x) < Math.round(this.grid.marker.x)) {
            this.grid.move(this.grid.movableLayers[0], 'topright');
        } else if (Math.round(this.grid.movableSprite.y) < Math.round(this.grid.marker.y) && Math.round(this.grid.movableSprite.x) < Math.round(this.grid.marker.x)) {
            this.grid.move(this.grid.movableLayers[0], 'bottomright');
        }

        if (this.grid.checkLayer(this.grid.marker.x, this.grid.marker.y, 3, this.grid.finalLayers)) {
            this.quitGame('play', false);
        }

        if (this.grid.checkLayer(this.grid.marker.x, this.grid.marker.y, 4, this.grid.collectableLayers)) {
            var spriteArray = this.grid.collect(this.grid.marker.x, this.grid.marker.y, this.grid.collectableLayers);
            for (var i = spriteArray.length - 1; i >= 0; i--) {
                var tmp = spriteArray[i];
                var s = game.add.tween(tmp.scale);
                s.to({
                    x: 0.2,
                    y: 0.2
                }, 250, Phaser.Easing.Linear.None);
                s.start();
                var e = game.add.tween(tmp);
                e.onStart.add(function () {
                    isMoving = true;
                });
                e.onComplete.add(function () {
                    isMoving = false;
                })
                e.to({
                    x: (160 + (50 * powerUps)),
                    y: 0
                }, 250, Phaser.Easing.Linear.None, false, 0, 0, false);
                e.start();
                powerUps++;
                shield = true;
            }
        }
        for (var i = this.grid.enemiesLayers.length - 1; i >= 0; i--) {
            this.grid.enemiesLayers[i].callAll('turn');
        }
    }
}
PlayS = Play;
}());<|endoftext|>


<commit_before>(function(){
  "use strict";

  const d = document;

  function createDiv(className) {
    const div = d.createElement('div');
    div.className = className;
    return div;
  }

  function appendDiv(node, className) {
    return node.appendChild(createDiv(className));
  }

  function createNode(text) {
    const node = createDiv('Node collapsed');
    appendDiv(node, 'Expander');
    const content = appendDiv(node, 'Content');
    content.contentEditable = 'true';
    if(text) content.textContent = text;
    appendDiv(node, 'Children');
    return node;
  }

  function expanderElement(node) { return node.firstChild; }
  function contentElement(node) { return node.children[1]; }
  function childrenElement(node) { return node.lastChild; }
  function parentNode(node) {
    node = node.parentElement;
    if(node && node.classList.contains('Children')) return node.parentElement;
    return null;
  }

  function isExpanded(node) { return node.classList.contains('expanded'); }
  function swapClass(node, oldClass, newClass) {
    node.classList.remove(oldClass);
    node.classList.add(newClass);
  }
  function expand(node) { swapClass(node, 'collapsed', 'expanded'); }
  function collapse(node) { swapClass(node, 'expanded', 'collapsed'); }
  function toggle(node) { (isExpanded(node)?collapse:expand)(node); }

  function focusContent(node) {
    if(node) contentElement(node).focus();
  }

  function lastVisibleLeaf(node) {
    while(node && isExpanded(node)) {
      var lastChild = childrenElement(node).lastChild;
      if(lastChild) node = lastChild;
    }
    return node;
  }

  function focusPrevContent(node) {
    var prev = node.previousSibling;
    if(prev) prev = lastVisibleLeaf(prev);
    else prev = parentNode(node);
    focusContent(prev);
  }

  function focusNextContent(node) {
    var next = isExpanded(node) ? childrenElement(node).firstChild : node.nextSibling;
    if(!next) {
      const parent = parentNode(node);
      if(parent) next = parent.nextSibling;
    }
    focusContent(next);
  }

  function appendSibling(node, sibling) {
    return node.insertAdjacentElement('afterEnd', sibling);
  }

  function prependSibling(node, sibling) {
    return node.insertAdjacentElement('beforeBegin', sibling);
  }

  function appendChild(node, child) {
    return childrenElement(node).appendChild(child);
  }

  function appendNewSibling(node) {
    focusContent(appendSibling(node, createNode()));
  }

  function indent(node) {
    const prev = node.previousSibling;
    if(prev) {
      expand(prev);
      appendChild(prev, node);
      focusContent(node);
    }
  }

  function outdent(node) {
    const parent = parentNode(node);
    if(parent) {
      appendSibling(parent, node);
      focusContent(node);
    }
  }

  function moveUp(node) {
    const prev = node.previousSibling || parentNode(node);
    if(prev) {
      prependSibling(prev, node);
      focusContent(node);
    }
  }

  function moveDown(node) {
    const next = node.nextSibling || parentNode(node);
    if(next) {
      appendSibling(next, node);
      focusContent(node);
    }
  }

  const outlinerElement = d.getElementById('outliner');
  focusContent(outlinerElement.appendChild(createNode('Test')));

  outlinerElement.addEventListener('click', e => {
    if(e.target.classList.contains('Expander')) {
      toggle(e.target.parentElement);
    }
  });

  const MOD_NONE = 0, MOD_CTRL = 1, MOD_ALT = 2, MOD_SHIFT = 4, MOD_META = 8;
  function getModKeys(e) {
    var mod = MOD_NONE;
    if(e.ctrlKey) mod += MOD_CTRL;
    if(e.altKey) mod += MOD_ALT;
    if(e.shiftKey) mod += MOD_SHIFT;
    if(e.metaKey) mod += MOD_META;
    return mod;
  }

  outlinerElement.addEventListener('keydown', e => {
    const node = e.target.parentElement;
    switch(e.key) {
      case 'Enter':
      if(getModKeys(e) == MOD_NONE) { appendNewSibling(node); e.preventDefault(); }
      break;

      case 'Tab':
      e.preventDefault();
      switch(getModKeys(e)) {
        case MOD_NONE: indent(node); break;
        case MOD_SHIFT: outdent(node); break;
      }
      break;

      case 'ArrowUp':
      switch(getModKeys(e)) {
        case MOD_ALT: moveUp(node); e.preventDefault(); break;
        case MOD_CTRL: focusPrevContent(node); e.preventDefault();break;
      }
      break;

      case 'ArrowDown':
      switch(getModKeys(e)) {
        case MOD_ALT: moveDown(node); e.preventDefault(); break;
        case MOD_CTRL: focusNextContent(node); e.preventDefault();break;
      }
      break;

      case 'ArrowLeft':
      if(getModKeys(e) == MOD_ALT) { outdent(node); e.preventDefault(); }
      break;

      case 'ArrowRight':
      if(getModKeys(e) == MOD_ALT) { indent(node); e.preventDefault(); }
      break;
    }
  });
}())<commit_msg>Bug fix<commit_after>(function(){
  "use strict";

  const d = document;

  function createDiv(className) {
    const div = d.createElement('div');
    div.className = className;
    return div;
  }

  function appendDiv(node, className) {
    return node.appendChild(createDiv(className));
  }

  function createNode(text) {
    const node = createDiv('Node collapsed');
    appendDiv(node, 'Expander');
    const content = appendDiv(node, 'Content');
    content.contentEditable = 'true';
    if(text) content.textContent = text;
    appendDiv(node, 'Children');
    return node;
  }

  function expanderElement(node) { return node.firstChild; }
  function contentElement(node) { return node.children[1]; }
  function childrenElement(node) { return node.lastChild; }
  function parentNode(node) {
    node = node.parentElement;
    if(node && node.classList.contains('Children')) return node.parentElement;
    return null;
  }

  function isExpanded(node) { return node.classList.contains('expanded'); }
  function swapClass(node, oldClass, newClass) {
    node.classList.remove(oldClass);
    node.classList.add(newClass);
  }
  function expand(node) { swapClass(node, 'collapsed', 'expanded'); }
  function collapse(node) { swapClass(node, 'expanded', 'collapsed'); }
  function toggle(node) { (isExpanded(node)?collapse:expand)(node); }

  function focusContent(node) {
    if(node) contentElement(node).focus();
  }

  function lastVisibleLeaf(node) {
    while(node && isExpanded(node)) {
      var lastChild = childrenElement(node).lastChild;
      if(lastChild) node = lastChild;
    }
    return node;
  }

  function focusPrevContent(node) {
    var prev = node.previousSibling;
    if(prev) prev = lastVisibleLeaf(prev);
    else prev = parentNode(node);
    focusContent(prev);
  }

  function focusNextContent(node) {
    var next = isExpanded(node) ? childrenElement(node).firstChild : node.nextSibling;
    if(!next) {
      const parent = parentNode(node);
      if(parent) next = parent.nextSibling;
    }
    focusContent(next);
  }

  function appendSibling(node, sibling) {
    return node.insertAdjacentElement('afterEnd', sibling);
  }

  function prependSibling(node, sibling) {
    return node.insertAdjacentElement('beforeBegin', sibling);
  }

  function appendChild(node, child) {
    return childrenElement(node).appendChild(child);
  }

  function appendNewSiblingOrFirstChild(node) {
    if(isExpanded(node)) {
      const firstChild = childrenElement(node).firstChild;
      if(firstChild) {
        focusContent(prependSibling(firstChild, createNode()));
        return;
      }
    }
    focusContent(appendSibling(node, createNode()));
  }

  function indent(node) {
    const prev = node.previousSibling;
    if(prev) {
      expand(prev);
      appendChild(prev, node);
      focusContent(node);
    }
  }

  function outdent(node) {
    const parent = parentNode(node);
    if(parent) {
      appendSibling(parent, node);
      focusContent(node);
    }
  }

  function moveUp(node) {
    const prev = node.previousSibling || parentNode(node);
    if(prev) {
      prependSibling(prev, node);
      focusContent(node);
    }
  }

  function moveDown(node) {
    const next = node.nextSibling || parentNode(node);
    if(next) {
      appendSibling(next, node);
      focusContent(node);
    }
  }

  const outlinerElement = d.getElementById('outliner');
  focusContent(outlinerElement.appendChild(createNode('Test')));

  outlinerElement.addEventListener('click', e => {
    if(e.target.classList.contains('Expander')) {
      toggle(e.target.parentElement);
    }
  });

  const MOD_NONE = 0, MOD_CTRL = 1, MOD_ALT = 2, MOD_SHIFT = 4, MOD_META = 8;
  function getModKeys(e) {
    var mod = MOD_NONE;
    if(e.ctrlKey) mod += MOD_CTRL;
    if(e.altKey) mod += MOD_ALT;
    if(e.shiftKey) mod += MOD_SHIFT;
    if(e.metaKey) mod += MOD_META;
    return mod;
  }

  outlinerElement.addEventListener('keydown', e => {
    const node = e.target.parentElement;
    switch(e.key) {
      case 'Enter':
      if(getModKeys(e) == MOD_NONE) { appendNewSiblingOrFirstChild(node); e.preventDefault(); }
      break;

      case 'Tab':
      e.preventDefault();
      switch(getModKeys(e)) {
        case MOD_NONE: indent(node); break;
        case MOD_SHIFT: outdent(node); break;
      }
      break;

      case 'ArrowUp':
      switch(getModKeys(e)) {
        case MOD_ALT: moveUp(node); e.preventDefault(); break;
        case MOD_CTRL: focusPrevContent(node); e.preventDefault();break;
      }
      break;

      case 'ArrowDown':
      switch(getModKeys(e)) {
        case MOD_ALT: moveDown(node); e.preventDefault(); break;
        case MOD_CTRL: focusNextContent(node); e.preventDefault();break;
      }
      break;

      case 'ArrowLeft':
      if(getModKeys(e) == MOD_ALT) { outdent(node); e.preventDefault(); }
      break;

      case 'ArrowRight':
      if(getModKeys(e) == MOD_ALT) { indent(node); e.preventDefault(); }
      break;
    }
  });
}())<|endoftext|>


<commit_before>// vim:ts=2:sw=2:expandtab:autoindent:
/*
Copyright (c) 2008 Aristid Breitkreuz, Ash Berlin, Ruediger Sonderfeld

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

(function () {

// Test for equality any JavaScript type.
// Discussions and reference: http://philrathe.com/articles/equiv
// Test suites: http://philrathe.com/tests/equiv
// Author: Philippe Rathé <prathe@gmail.com>

// About passing arguments:
//      when < 2   : return true
//      when 2     : return true if 1st equals 2nd
//      when > 2   : return true 1st equals 2nd, and 2nd equals 3rd,
//                      and 3rd equals 4th ... ans so on (by transition)
//

// Stack of constructors that enables us to skip or abort on functions value.
var objectCallerConstructorStack = [];
function equiv() {
    // we will need to use the array splice method,
    // so we need to convert arguments into a true array.
    var args = Array.prototype.slice.apply(arguments);
    var a, b; // compares a and b (1st and 2nd argument)
    var i; // for iterating over objects convenience
    var len; // for iterating array's length memoization
    // Memoized objects' properties.
    // Allow us to test for equivalence only in one way,
    // then to compare properties to make sure no one is missing.
    var aProperties, bProperties;

    if (args.length < 2) {
        return true; // nothing to compare together
    }

    a = args[0];
    b = args[1];

    return (function (a, b) {

        // Try to optimize the algo speed if ever both a and b are references
        // pointing to the same object (e..g. only for functions, arrays and objects)
        if (a === b) {
            return true;
        }

        // Must test for the null value before testing for the type of an object,
        // because null is also an object. Trapping null or undefined values here
        // is a good to avoid to much validations further in the code.
        if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined") {
            return false; // Anyway (a === b) would have already caught it.
        }

        // Don't lose time and prevent further wrong type manipulation or unexpected results.
        if (typeof a !== typeof b) {
            return false;
        }

        // Must test if it's an array before testing for the type of an object,
        // because an array is also an object.
        if (a instanceof Array) {

            // Make sure b is also an array.
            // At this point b can be an object.
            // Prevent further arrays operation on object.
            if ( ! (b instanceof Array)) {
                return false;
            }

            len = a.length;
            if (len !== b.length) { // safe and faster
                return false;
            }
            for (i = 0; i < len; i++) {
                if( ! equiv(a[i], b[i])) {
                    return false;
                }
            }
            return true;
        }

        // NOTE:
        //      Must test if it's a date before testing if it is an object,
        //      because a date is also an object.
        if (a instanceof Date) {
            return b instanceof Date && a.valueOf() === b.valueOf();
        }

        // NOTE:
        //      Using toSource() to compare regexp only works in FF. It was working as well as the above.
        //      Must verify the constructor of a regexp before verifying it is an object.
        //      Notice those confusing true statements:
        //          var a = /./;
        //          a instanceof Object; // => true
        //          a instanceof RegExp; // => true
        //          typeof a === "function"; // => false in IE and Opera, true in FF and Safari
        //
        // There is 3 possible modifier for regular expressions (g, m and i)
        // The source property only returns the regular expression string without the modifier.
        if (a instanceof RegExp) {
            return b instanceof RegExp &&
                a.source === b.source &&
                a.global === b.global &&
                a.ignoreCase === b.ignoreCase &&
                a.multiline === b.multiline;
        }

        // typeof on some types returns sometimes unexpected type that we deals with.
        // We could have used instanceof instead of typeof but it comes with
        // other problems as mentioned above when testing for the RegExp constructor.
        // We explicitely don't use hasOwnProperty when iterating on the properties of
        // an object to allow a deeper equivalence (e.g. for instances particularly)
        if (typeof a === "object") {
            // Different constructors means a and b can't be equivalent instances.
            // We don't have any choice of comparing them in both ways because of inheritance
            // otherwise {} could be mistaken with []
            // NOTE this:
            //      var a = [];
            //      a instanceof Array;  // true
            //      a instanceof Object; // true
            if ( a.constructor !== b.constructor) {
                return false;
            }

            // Stack constructors before iterating over its properties
            objectCallerConstructorStack.push(a.constructor);

            aProperties = [];
            bProperties = [];

            // NOTE:
            //      We only need to compare propertie's equivalence in one way
            //      and ensures that all properties of a and b are the same.

            // Verify a's properties with b's properties equivalence.
            // Stack a's properties

            for (i in a) {
                aProperties.push(i);
                if (!equiv(a[i], b[i])) {
                    // Unstack current constructor when finished with the object
                    objectCallerConstructorStack.pop();
                    return false;
                }
            }

            // Stack also b's properties
            for (i in b) {
                bProperties.push(i);
            }

            // Unstack current constructor when finished with the object
            objectCallerConstructorStack.pop();

            // Ensures properties's name in both ways.
            // Must sort them because they may not be always in the same order!
            return equiv(aProperties.sort(), bProperties.sort());
        }

        // NOTE:
        //      Using the typeof operator will also catch a RegExp instance.
        //      Take no risk.
        if (a instanceof Function) {
            // BEHAVIOR when comparing functions.
            //      Being here means that a and b were were not the same references.
            //      At this level, functions won't be compared at all.
            //      - skip when they are properties of instances (not instantiates from the Object construtor)
            //      - abort otherwise
            //
            //      To Determine if a function is anonymous we use a regexp on the function sources.
            var currentObjectCallerConstructor =
                    objectCallerConstructorStack[objectCallerConstructorStack.length - 1];
            return currentObjectCallerConstructor !== Object &&
                    typeof currentObjectCallerConstructor !== "undefined";
        }

        // NaN is a number in JavaScript.
        // Because this statement is false: 0/0 === 0/0,
        // we must use the isNaN function which is the only way to know a number is NaN.
        // Note that it isn't the case with the Infinity number, and that 1/0 === 2/0.
        if (typeof a === "number" && isNaN(a)) {
            return isNaN(b);
        }

        // Compares Number, String or Boolean
        // Because sooner we have already tried:
        //          a === b (then return if it is the case)
        //            and
        //  typeof a !== typeof b (then return false if it is the case)
        // at this time a and b should be of the same type, but hold different values.
        // We can safely return false instead of returning a === b
        return false;

    }(a, b)) && equiv.apply(this, args.splice(1, args.length -1)); // apply transition with (1..n) arguments
}

this.TestHarness = function TestHarness() {
  this.tests = [];
  this.asserts_failed = 0;
  this.total_asserts = 0;
  this.tests_failed = 0;
  this.total_tests = 0;
  this.outputStream = IO.stdout;
}

this.TestHarness.prototype = {
  constructor: TestHarness,

  colourize: true,

  same: function() {
    var args = Array.prototype.slice.call(arguments);

    var msg;
    if (args.length > 2)
      msg = args.pop();

    this.do_assert( {
      type: 'same',
      when: new Date(),
      ok: !!equiv.apply(equiv, args),
      message: msg,
      default_msg: "arguments are the same"
    });
  },

  instance_of: function(obj, type, msg) {
    var test = obj instanceof type;
    this.do_assert( {
      type: 'instance_of',
      ok: !!test,
      when: new Date(),
      message: msg,
      default_msg: "object is instance of type"
    });
  },

  ok: function ok(test, msg) {
    if (arguments.length == 0) {
      test = true; // If nothing is passed, then assume its successful
    }
    this.do_assert( {
      type: 'ok',
      ok: !!test,
      when: new Date(),
      message: msg,
    } );
  },

  // Handle/print the assert
  do_assert: function do_assert(a) {
    this.current_test.asserts.push(a);
    a.num = this.current_test.asserts.length;

    // Padding to make numbers line up.
    var padd = "  ";
    var plan = this.current_test.plan;

    if (a.num === 1) {
      // First assert this test
      if (isNaN(plan)) {
        // Just print the newline
        this.outputStream.print();
      }
    }

    if (plan === undefined || isNaN(plan))
      plan = a.num;

    let len = plan.toString().length - a.num.toString().length;
    if (len > 0)
      padd += Array(len+1).join(' ');

    let data = [padd + a.num];
    if (this.colourize)
      data.push(a.ok ? '\x1b[32mok\x1b[0m' : '\x1b[31mnot ok\x1b[0m');
    else
      data.push(a.ok ? 'ok' : 'not ok');

    if (a.message !== undefined)
      data.push('-', a.message)
    else if ('default_msg' in a)
      data.push('-', a.default_msg)

    if (a.ok)
      this.current_test.asserts_passed++;
    else
      this.current_test.asserts_failed++;

    this.outputStream.print(data);
  },

  expect: function expect(count) {
    if (this.current_test.asserts.length)
      throw new Error('expect called after an assertion');
    this.current_test.plan = new Number(count).valueOf();
    if (isNaN(this.current_test.plan))
      throw new Error('invalid plan - not a number');
    this.outputStream.print(" 1.." + this.current_test.plan);
  },

  run: function run(name) {
    this.current_test = {
      name: name,
      asserts: [],
      asserts_passed: 0,
      asserts_failed: 0,
    };

    if (this[name] instanceof Function === false) {
      throw new Error(name + " is not a function");
    }

    this.tests.push(this.current_test);

    this.outputStream.write(name + ":");

    // If there's a 'plan' property of the function object, use it as the plan
    if ('expect' in this[name]) {
      this.expect(this[name].expect)
    }

    try {
        this[name].apply(this);
    }
    catch (e) {
      this.current_test.error = e;
    }
    this.finalize_test();
    delete this.current_test;
  },

  go: function go() {
    for (var n in this) {
      if (this.hasOwnProperty(n) == false)
        continue;
      n = n.toString();

      if (n.substr(0,5) === "test_")
        this.run(n);
    }
    this.finalize();
  },

  finalize_test: function finalize_test() {
    var test = this.current_test;
    var msg = [];
    this.asserts_failed += test.asserts_failed;

    if (test.plan !== null && !isNaN(test.plan)) {
      // Check the plan.
      let plural = test.plan == 1 ? 'assert' : 'asserts';

      if (test.plan > test.asserts.length) {
        // too many
        test.passed = false;
        msg = [test.name, 'expected', test.plan, plural, 'only got',
               test.asserts.length];
        this.total_asserts += test.plan;
        this.asserts_failed += test.plan - test.asserts.length;
      } else if (test.plan < test.asserts.length) {
        test.passed = false;
        msg = [test.name, 'expected', test.plan, plural, 'but got',
               test.asserts.length];
        this.total_asserts += test.asserts.length;
        this.asserts_failed += test.asserts.length - test.plan;
      }
      else {
        test.passed = test.asserts_failed == 0;
        this.total_asserts += test.plan;
      }
    }

    if (test.error) {
      // The test died.

      // Possibly before it could plan, so add a \n
      if (isNaN(test.plan) && test.asserts.length == 0)
        this.outputStream.print();

      let e = this.current_test.error;
      this.outputStream.print(test.name, 'died:');
      var e_msg = e.toString().replace(/\n/g, "\n  ");
      let a = ['  ' + e_msg];
      if (e.fileName) {
        a.push('at', e.fileName + ':' + e.lineNumber);
      }
      msg = a;
      test.passed = false;
    }
    else {
      // No plan, no error.
      test.passed = test.asserts_failed == 0;
    }
    if (msg.length)
      this.outputStream.print(msg);

    this.total_tests++;
    if (!test.passed)
      this.tests_failed++;
  },

  // Print summary
  finalize: function finalize() {
    if (this.tests_failed == 0) {
      this.outputStream.print("All tests successful")
      return;
    }

    this.outputStream.print( this.tests_failed + "/" + this.total_tests, "tests",
           "(" + this.asserts_failed + "/" + this.total_asserts,
           "asserts)", "failed");
  }
}

})()

/*
t = new TestHarness;
t.test_expect_prop = function() {
  with(this) {
    ok(true, "foo")
  }
}
t.test_expect_prop.expect = 6;

t.test_die = function() {
  throw new Error("foo\nbar");
}

t.test_noplan = function() {
  this.ok("no plan");
  this.same(1,new Number("1"), "numbers are the same");
}

t.test_gt_ten = function () {
  this.expect(12);
  [this.ok(1, i) for (i in Range(0,12) )];
}

t.test_gt = function() {
  this.expect(1);
  this.ok();
  this.ok();
  this.ok();
}

t.go();
*/
<commit_msg>JS/TestHarnes: colour final message too<commit_after>// vim:ts=2:sw=2:expandtab:autoindent:
/*
Copyright (c) 2008 Aristid Breitkreuz, Ash Berlin, Ruediger Sonderfeld

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

(function () {

// Test for equality any JavaScript type.
// Discussions and reference: http://philrathe.com/articles/equiv
// Test suites: http://philrathe.com/tests/equiv
// Author: Philippe Rathé <prathe@gmail.com>

// About passing arguments:
//      when < 2   : return true
//      when 2     : return true if 1st equals 2nd
//      when > 2   : return true 1st equals 2nd, and 2nd equals 3rd,
//                      and 3rd equals 4th ... ans so on (by transition)
//

// Stack of constructors that enables us to skip or abort on functions value.
var objectCallerConstructorStack = [];
function equiv() {
    // we will need to use the array splice method,
    // so we need to convert arguments into a true array.
    var args = Array.prototype.slice.apply(arguments);
    var a, b; // compares a and b (1st and 2nd argument)
    var i; // for iterating over objects convenience
    var len; // for iterating array's length memoization
    // Memoized objects' properties.
    // Allow us to test for equivalence only in one way,
    // then to compare properties to make sure no one is missing.
    var aProperties, bProperties;

    if (args.length < 2) {
        return true; // nothing to compare together
    }

    a = args[0];
    b = args[1];

    return (function (a, b) {

        // Try to optimize the algo speed if ever both a and b are references
        // pointing to the same object (e..g. only for functions, arrays and objects)
        if (a === b) {
            return true;
        }

        // Must test for the null value before testing for the type of an object,
        // because null is also an object. Trapping null or undefined values here
        // is a good to avoid to much validations further in the code.
        if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined") {
            return false; // Anyway (a === b) would have already caught it.
        }

        // Don't lose time and prevent further wrong type manipulation or unexpected results.
        if (typeof a !== typeof b) {
            return false;
        }

        // Must test if it's an array before testing for the type of an object,
        // because an array is also an object.
        if (a instanceof Array) {

            // Make sure b is also an array.
            // At this point b can be an object.
            // Prevent further arrays operation on object.
            if ( ! (b instanceof Array)) {
                return false;
            }

            len = a.length;
            if (len !== b.length) { // safe and faster
                return false;
            }
            for (i = 0; i < len; i++) {
                if( ! equiv(a[i], b[i])) {
                    return false;
                }
            }
            return true;
        }

        // NOTE:
        //      Must test if it's a date before testing if it is an object,
        //      because a date is also an object.
        if (a instanceof Date) {
            return b instanceof Date && a.valueOf() === b.valueOf();
        }

        // NOTE:
        //      Using toSource() to compare regexp only works in FF. It was working as well as the above.
        //      Must verify the constructor of a regexp before verifying it is an object.
        //      Notice those confusing true statements:
        //          var a = /./;
        //          a instanceof Object; // => true
        //          a instanceof RegExp; // => true
        //          typeof a === "function"; // => false in IE and Opera, true in FF and Safari
        //
        // There is 3 possible modifier for regular expressions (g, m and i)
        // The source property only returns the regular expression string without the modifier.
        if (a instanceof RegExp) {
            return b instanceof RegExp &&
                a.source === b.source &&
                a.global === b.global &&
                a.ignoreCase === b.ignoreCase &&
                a.multiline === b.multiline;
        }

        // typeof on some types returns sometimes unexpected type that we deals with.
        // We could have used instanceof instead of typeof but it comes with
        // other problems as mentioned above when testing for the RegExp constructor.
        // We explicitely don't use hasOwnProperty when iterating on the properties of
        // an object to allow a deeper equivalence (e.g. for instances particularly)
        if (typeof a === "object") {
            // Different constructors means a and b can't be equivalent instances.
            // We don't have any choice of comparing them in both ways because of inheritance
            // otherwise {} could be mistaken with []
            // NOTE this:
            //      var a = [];
            //      a instanceof Array;  // true
            //      a instanceof Object; // true
            if ( a.constructor !== b.constructor) {
                return false;
            }

            // Stack constructors before iterating over its properties
            objectCallerConstructorStack.push(a.constructor);

            aProperties = [];
            bProperties = [];

            // NOTE:
            //      We only need to compare propertie's equivalence in one way
            //      and ensures that all properties of a and b are the same.

            // Verify a's properties with b's properties equivalence.
            // Stack a's properties

            for (i in a) {
                aProperties.push(i);
                if (!equiv(a[i], b[i])) {
                    // Unstack current constructor when finished with the object
                    objectCallerConstructorStack.pop();
                    return false;
                }
            }

            // Stack also b's properties
            for (i in b) {
                bProperties.push(i);
            }

            // Unstack current constructor when finished with the object
            objectCallerConstructorStack.pop();

            // Ensures properties's name in both ways.
            // Must sort them because they may not be always in the same order!
            return equiv(aProperties.sort(), bProperties.sort());
        }

        // NOTE:
        //      Using the typeof operator will also catch a RegExp instance.
        //      Take no risk.
        if (a instanceof Function) {
            // BEHAVIOR when comparing functions.
            //      Being here means that a and b were were not the same references.
            //      At this level, functions won't be compared at all.
            //      - skip when they are properties of instances (not instantiates from the Object construtor)
            //      - abort otherwise
            //
            //      To Determine if a function is anonymous we use a regexp on the function sources.
            var currentObjectCallerConstructor =
                    objectCallerConstructorStack[objectCallerConstructorStack.length - 1];
            return currentObjectCallerConstructor !== Object &&
                    typeof currentObjectCallerConstructor !== "undefined";
        }

        // NaN is a number in JavaScript.
        // Because this statement is false: 0/0 === 0/0,
        // we must use the isNaN function which is the only way to know a number is NaN.
        // Note that it isn't the case with the Infinity number, and that 1/0 === 2/0.
        if (typeof a === "number" && isNaN(a)) {
            return isNaN(b);
        }

        // Compares Number, String or Boolean
        // Because sooner we have already tried:
        //          a === b (then return if it is the case)
        //            and
        //  typeof a !== typeof b (then return false if it is the case)
        // at this time a and b should be of the same type, but hold different values.
        // We can safely return false instead of returning a === b
        return false;

    }(a, b)) && equiv.apply(this, args.splice(1, args.length -1)); // apply transition with (1..n) arguments
}

this.TestHarness = function TestHarness() {
  this.tests = [];
  this.asserts_failed = 0;
  this.total_asserts = 0;
  this.tests_failed = 0;
  this.total_tests = 0;
  this.outputStream = IO.stdout;
}

this.TestHarness.prototype = {
  constructor: TestHarness,

  colourize: true,

  same: function() {
    var args = Array.prototype.slice.call(arguments);

    var msg;
    if (args.length > 2)
      msg = args.pop();

    this.do_assert( {
      type: 'same',
      when: new Date(),
      ok: !!equiv.apply(equiv, args),
      message: msg,
      default_msg: "arguments are the same"
    });
  },

  instance_of: function(obj, type, msg) {
    var test = obj instanceof type;
    this.do_assert( {
      type: 'instance_of',
      ok: !!test,
      when: new Date(),
      message: msg,
      default_msg: "object is instance of type"
    });
  },

  ok: function ok(test, msg) {
    if (arguments.length == 0) {
      test = true; // If nothing is passed, then assume its successful
    }
    this.do_assert( {
      type: 'ok',
      ok: !!test,
      when: new Date(),
      message: msg,
    } );
  },

  // Handle/print the assert
  do_assert: function do_assert(a) {
    this.current_test.asserts.push(a);
    a.num = this.current_test.asserts.length;

    // Padding to make numbers line up.
    var padd = "  ";
    var plan = this.current_test.plan;

    if (a.num === 1) {
      // First assert this test
      if (isNaN(plan)) {
        // Just print the newline
        this.outputStream.print();
      }
    }

    if (plan === undefined || isNaN(plan))
      plan = a.num;

    let len = plan.toString().length - a.num.toString().length;
    if (len > 0)
      padd += Array(len+1).join(' ');

    let data = [padd + a.num];
    if (this.colourize)
      data.push(a.ok ? '\x1b[32mok\x1b[0m' : '\x1b[31mnot ok\x1b[0m');
    else
      data.push(a.ok ? 'ok' : 'not ok');

    if (a.message !== undefined)
      data.push('-', a.message)
    else if ('default_msg' in a)
      data.push('-', a.default_msg)

    if (a.ok)
      this.current_test.asserts_passed++;
    else
      this.current_test.asserts_failed++;

    this.outputStream.print(data);
  },

  expect: function expect(count) {
    if (this.current_test.asserts.length)
      throw new Error('expect called after an assertion');
    this.current_test.plan = new Number(count).valueOf();
    if (isNaN(this.current_test.plan))
      throw new Error('invalid plan - not a number');
    this.outputStream.print(" 1.." + this.current_test.plan);
  },

  run: function run(name) {
    this.current_test = {
      name: name,
      asserts: [],
      asserts_passed: 0,
      asserts_failed: 0,
    };

    if (this[name] instanceof Function === false) {
      throw new Error(name + " is not a function");
    }

    this.tests.push(this.current_test);

    this.outputStream.write(name + ":");

    // If there's a 'plan' property of the function object, use it as the plan
    if ('expect' in this[name]) {
      this.expect(this[name].expect)
    }

    try {
        this[name].apply(this);
    }
    catch (e) {
      this.current_test.error = e;
    }
    this.finalize_test();
    delete this.current_test;
  },

  go: function go() {
    for (var n in this) {
      if (this.hasOwnProperty(n) == false)
        continue;
      n = n.toString();

      if (n.substr(0,5) === "test_")
        this.run(n);
    }
    this.finalize();
  },

  finalize_test: function finalize_test() {
    var test = this.current_test;
    var msg = [];
    this.asserts_failed += test.asserts_failed;

    if (test.plan !== null && !isNaN(test.plan)) {
      // Check the plan.
      let plural = test.plan == 1 ? 'assert' : 'asserts';

      if (test.plan > test.asserts.length) {
        // too many
        test.passed = false;
        msg = [test.name, 'expected', test.plan, plural, 'only got',
               test.asserts.length];
        this.total_asserts += test.plan;
        this.asserts_failed += test.plan - test.asserts.length;
      } else if (test.plan < test.asserts.length) {
        test.passed = false;
        msg = [test.name, 'expected', test.plan, plural, 'but got',
               test.asserts.length];
        this.total_asserts += test.asserts.length;
        this.asserts_failed += test.asserts.length - test.plan;
      }
      else {
        test.passed = test.asserts_failed == 0;
        this.total_asserts += test.plan;
      }
    }

    if (test.error) {
      // The test died.

      // Possibly before it could plan, so add a \n
      if (isNaN(test.plan) && test.asserts.length == 0)
        this.outputStream.print();

      let e = this.current_test.error;
      this.outputStream.print(test.name, 'died:');
      var e_msg = e.toString().replace(/\n/g, "\n  ");
      let a = ['  ' + e_msg];
      if (e.fileName) {
        a.push('at', e.fileName + ':' + e.lineNumber);
      }
      msg = a;
      test.passed = false;
    }
    else {
      // No plan, no error.
      test.passed = test.asserts_failed == 0;
    }
    if (msg.length)
      this.outputStream.print(msg);

    this.total_tests++;
    if (!test.passed)
      this.tests_failed++;
  },

  // Print summary
  finalize: function finalize() {
    if (this.tests_failed == 0) {
      var msg = 'All tests successful';
      if (this.colourize)
        msg = '\x1b[32m' + msg + '\x1b[0m';
      this.outputStream.print(msg)
      return;
    }

    var msg = [this.tests_failed + "/" + this.total_tests, "tests",
              "(" + this.asserts_failed + "/" + this.total_asserts,
              "asserts)", "failed"];
    if (this.colourize) {
      msg[0] = '\x1b[31m' + msg[0];
      msg[msg.length-1] = msg[msg.length-1] + '\x1b[0m';
    }

    this.outputStream.print(msg)
  }
}

})()

/*
t = new TestHarness;
t.test_expect_prop = function() {
  with(this) {
    ok(true, "foo")
  }
}
t.test_expect_prop.expect = 6;

t.test_die = function() {
  throw new Error("foo\nbar");
}

t.test_noplan = function() {
  this.ok("no plan");
  this.same(1,new Number("1"), "numbers are the same");
}

t.test_gt_ten = function () {
  this.expect(12);
  [this.ok(1, i) for (i in Range(0,12) )];
}

t.test_gt = function() {
  this.expect(1);
  this.ok();
  this.ok();
  this.ok();
}

t.go();
*/
<|endoftext|>


<commit_before>Date.prototype.getMonthWeek = function(){
    var firstDay = new Date(this.getUTCFullYear(), this.getUTCMonth(), 1).getUTCDay();
    return Math.ceil((this.getUTCDate() + firstDay)/7);
}

$( "#time-button" ).click(function() {
  var inputDate = new Date($('#date').val() + ' ' + $('#time').val() );
  loadData(inputDate);
});

$(document).ready(function () {
  $('#time').timepicker({
      defaultTime: 'current',
      minuteStep: 1,
      disableFocus: true,
      template: 'dropdown'
  });

  $('.datepicker').datepicker();
});

/*
 * Returns true if it's sweeping time
 * Returns false if it's not sweeping time
 * arugments strings of time eg "11:00:23" or "13:37:01"
 * milliseconds not acceptable in firefox
 */
 function isItSweepingTime(time, start, end) {
  var temp = "October 13, 1975";
  var searchTime = new Date(temp + " " + time);
  var startTime = new Date(temp + " "+ start);
  var endTime = new Date(temp + " " + end);
  return (startTime <= searchTime) && (searchTime <= endTime);
 }

function isItSweepingWeek (weekOfMonth, weeks) {
  return (weeks[weekOfMonth-1] == "True");
}

function isItSweepingDay (dayOfWeek, days) {
  return (days[dayOfWeek] == "True");
}

function removeMilliseconds(time) {
  return time.split('.')[0];
}
function loadData (filterTime) {
  // All dates from the server are stored as UTC, use UTC for comparison
  var utcTime = filterTime.getUTCHours().toString() + ":" + filterTime.getUTCMinutes().toString() + ":" + filterTime.getUTCSeconds().toString();

  d3.csv("data/boston-street-sweeping-schedules.csv", function(data) {
    data = data.filter(function(row) {
      //filter down csv to only listings that are currently happening.
      var weeks = [row['week1'], row['week2'], row['week3'], row['week4'], row['week5']];
      var days = [row['Sunday'],row['Monday'],row['Tuesday'],row['Wednesday'],row['Thursday'],row['Friday'],row['Saturday']];
      var sweepingWeek = isItSweepingWeek(filterTime.getMonthWeek(), weeks);
      var sweepingDay = isItSweepingDay(filterTime.getUTCDay(), days);
      var sweepingTime = isItSweepingTime(utcTime, removeMilliseconds(row['StartTime']), removeMilliseconds(row['EndTime']));
      return  sweepingTime && sweepingWeek && sweepingDay;
  })
  // the columns you'd like to display
  var columns = ['Street','from','to'];

  d3.select("#results").html(null);
  var table = d3.select("#results").append("table").attr("class", "tblResults"),
      thead = table.append("thead"),
      tbody = table.append("tbody");

  // append the header row
  thead.append("tr")
      .selectAll("th")
      .data(columns)
      .enter()
      .append("th")
          .text(function(column) { return column; });

  // create a row for each object in the data
  var rows = tbody.selectAll("tr")
      .data(data)
      .enter()
      .append("tr");

  // create a cell in each row for each column
  var cells = rows.selectAll("td")
      .data(function(row) {
          return columns.map(function(column) {
              return {column: column, value: row[column]};
          });
      })
      .enter()
      .append("td")
          .text(function(d) { return d.value; });
  });
}
<commit_msg>Clean up<commit_after>Date.prototype.getUTCMonthWeek = function(){
    var firstDay = new Date(this.getUTCFullYear(), this.getUTCMonth(), 1).getUTCDay();
    return Math.ceil((this.getUTCDate() + firstDay)/7);
}

$( "#time-button" ).click(function() {
  var inputDate = new Date($('#date').val() + ' ' + $('#time').val() );
  loadData(inputDate);
});

$(document).ready(function () {
  $('#time').timepicker({
      defaultTime: 'current',
      minuteStep: 1,
      disableFocus: true,
      template: 'dropdown'
  });

  $('.datepicker').datepicker();
});

/*
 * Returns true if it's sweeping time
 * Returns false if it's not sweeping time
 * arugments strings of time eg "11:00:23" or "13:37:01"
 * milliseconds not acceptable in firefox
 */
 function isItSweepingTime(time, start, end) {
  var temp = "October 13, 1975";
  var searchTime = new Date(temp + " " + time);
  var startTime = new Date(temp + " "+ start);
  var endTime = new Date(temp + " " + end);
  return (startTime <= searchTime) && (searchTime <= endTime);
 }

function isItSweepingWeek (weekOfMonth, weeks) {
  return (weeks[weekOfMonth-1] == "True");
}

function isItSweepingDay (dayOfWeek, days) {
  return (days[dayOfWeek] == "True");
}

function removeMilliseconds(time) {
  return time.split('.')[0];
}
function loadData (filterTime) {
  // All dates from the server are stored as UTC, use UTC for comparison
  var utcTime = filterTime.getUTCHours().toString() + ":" + filterTime.getUTCMinutes().toString() + ":" + filterTime.getUTCSeconds().toString();

  d3.csv("data/boston-street-sweeping-schedules.csv", function(data) {
    data = data.filter(function(row) {
      //filter down csv to only listings that are currently happening.
      var weeks = [row['week1'], row['week2'], row['week3'], row['week4'], row['week5']];
      var days = [row['Sunday'],row['Monday'],row['Tuesday'],row['Wednesday'],row['Thursday'],row['Friday'],row['Saturday']];
      var sweepingWeek = isItSweepingWeek(filterTime.getUTCMonthWeek(), weeks);
      var sweepingDay = isItSweepingDay(filterTime.getUTCDay(), days);
      var sweepingTime = isItSweepingTime(utcTime, removeMilliseconds(row['StartTime']), removeMilliseconds(row['EndTime']));
      return  sweepingTime && sweepingWeek && sweepingDay;
  })
  // the columns you'd like to display
  var columns = ['Street','from','to'];

  var results = d3.select("#results").html(null),
      table = results.append("table").attr("class", "tblResults"),
      thead = table.append("thead"),
      tbody = table.append("tbody");

  // append the header row
  thead.append("tr")
      .selectAll("th")
      .data(columns)
      .enter()
      .append("th")
          .text(function(column) { return column; });

  // create a row for each object in the data
  var rows = tbody.selectAll("tr")
      .data(data)
      .enter()
      .append("tr");

  // create a cell in each row for each column
  var cells = rows.selectAll("td")
      .data(function(row) {
          return columns.map(function(column) {
              return {column: column, value: row[column]};
          });
      })
      .enter()
      .append("td")
          .text(function(d) { return d.value; });
  });
}
<|endoftext|>


<commit_before>const templates = require('./templates');

class Slideshow {
  constructor() {
    this.start = this.start.bind(this);
    this.keyboardListener = this.keyboardListener.bind(this);
    this.setFocus = this.setFocus.bind(this);
  }

  start(e) {
    let items = [];

    this.iterations = 0;

    e.target
      .closest('article.slideshow')
      .querySelectorAll('.lb-item img')
      .forEach((img) => {
        let matches = [];

        img.getAttribute('srcset').replace(/(\S+)\s\d+w/g, (s, match) => {
          matches.push(match);
        });

        let [baseImage, thumbnail, viewImage] = matches;

        items.push({
          item: {
            meta: {media: {renditions: {
              baseImage: {href: baseImage},
              thumbnail: {href: thumbnail},
              viewImage: {href: viewImage}
            }}},
            active: thumbnail === e.target.getAttribute('src')
          }
        });
      });

    let slideshow = templates.slideshow({
      refs: items
    });

    window.parent.postMessage('fullscreen', window.document.referrer);

    document.querySelector('div.lb-timeline')
      .insertAdjacentHTML('afterend', slideshow);

    window.addEventListener('keydown', this.keyboardListener);

    this.setFocus();
  }

  setFocus() {
    const container = document.querySelector('#slideshow .container');

    container.querySelectorAll('img').forEach((img, i) => {
      if (img.classList.contains('active')) {
        this.iterations = i;
      }
    });

    if (this.iterations > 0) {
      container.style.marginTop = `-${container.offsetHeight * this.iterations}px`;
    }
  }

  keyboardListener(e) {
    const container = document.querySelector('#slideshow .container');
    const picturesCount = container.querySelectorAll('img').length;
    let offset = container.offsetHeight * this.iterations;

    switch (e.keyCode) {
    case 39: // right
      if (offset + container.offsetHeight < picturesCount * container.offsetHeight) {
        container.style.marginTop = `-${offset + container.offsetHeight}px`;
        this.iterations++;
      }

      break;
    case 37: // left
      if (offset - container.offsetHeight >= 0) {
        container.style.marginTop = `-${offset - container.offsetHeight}px`;
        this.iterations--;
      }

      break;
    case 27: // esc
      window.parent.postMessage('quitfullscreen', window.document.referrer);
      document.querySelector('#slideshow').remove();
    }
  }
}

module.exports = Slideshow;
<commit_msg>true full screen<commit_after>const templates = require('./templates');

class Slideshow {
  constructor() {
    this.start = this.start.bind(this);
    this.keyboardListener = this.keyboardListener.bind(this);
    this.setFocus = this.setFocus.bind(this);
  }

  start(e) {
    let items = [];

    this.iterations = 0;

    e.target
      .closest('article.slideshow')
      .querySelectorAll('.lb-item img')
      .forEach((img) => {
        let matches = [];

        img.getAttribute('srcset').replace(/(\S+)\s\d+w/g, (s, match) => {
          matches.push(match);
        });

        let [baseImage, thumbnail, viewImage] = matches;

        items.push({
          item: {
            meta: {media: {renditions: {
              baseImage: {href: baseImage},
              thumbnail: {href: thumbnail},
              viewImage: {href: viewImage}
            }}},
            active: thumbnail === e.target.getAttribute('src')
          }
        });
      });

    let slideshow = templates.slideshow({
      refs: items
    });

    //window.parent.postMessage('fullscreen', window.document.referrer);

    document.querySelector('div.lb-timeline')
      .insertAdjacentHTML('afterend', slideshow);

    this.launchIntoFullscreen(document.getElementById('slideshow'));
    window.addEventListener('keydown', this.keyboardListener);

    this.setFocus();
  }

  launchIntoFullscreen(element) {
    if(element.requestFullscreen) {
      element.requestFullscreen();
    } else if(element.mozRequestFullScreen) {
      element.mozRequestFullScreen();
    } else if(element.webkitRequestFullscreen) {
      element.webkitRequestFullscreen();
    } else if(element.msRequestFullscreen) {
      element.msRequestFullscreen();
    }
  }

  exitFullscreen() {
    if(document.exitFullscreen) {
      document.exitFullscreen();
    } else if(document.mozCancelFullScreen) {
      document.mozCancelFullScreen();
    } else if(document.webkitExitFullscreen) {
      document.webkitExitFullscreen();
    }
  }

  setFocus() {
    const container = document.querySelector('#slideshow .container');

    container.querySelectorAll('img').forEach((img, i) => {
      if (img.classList.contains('active')) {
        this.iterations = i;
      }
    });

    if (this.iterations > 0) {
      container.style.marginTop = `-${container.offsetHeight * this.iterations}px`;
    }
  }

  keyboardListener(e) {
    const container = document.querySelector('#slideshow .container');
    const picturesCount = container.querySelectorAll('img').length;
    let offset = container.offsetHeight * this.iterations;

    switch (e.keyCode) {
    case 39: // right
      if (offset + container.offsetHeight < picturesCount * container.offsetHeight) {
        container.style.marginTop = `-${offset + container.offsetHeight}px`;
        this.iterations++;
      }

      break;
    case 37: // left
      if (offset - container.offsetHeight >= 0) {
        container.style.marginTop = `-${offset - container.offsetHeight}px`;
        this.iterations--;
      }

      break;
    case 27: // esc
      this.exitFullscreen();
      document.querySelector('#slideshow').remove();
    }
  }
}

module.exports = Slideshow;
<|endoftext|>


<commit_before>if(typeof translations == 'undefined'){
    translations = {};
}

translations.fr_FR = {
// header
    'Search': 'Rechercher',
    // Nav menu
    'Add': 'Ajouter',
    'By URIs': 'Par URIs',
    'By Torrents': 'Par Torrents',
    'By Metalinks': 'Par Metaliens',
    'Manage': 'Gérer',
    'Pause All': 'Tout suspendre',
    'Resume Paused': 'Reprendre',
    'Purge Completed': 'Nettoyer les fichiers complétés',
    'Settings': 'Paramètres',
    'Connection Settings': 'Paramètres de Connexion',
    'Global Settings': 'Paramètres Globaux',
    'Server info': 'Informations Serveur',
    'About and contribute': 'À propos et contribuer',
    'Toggle navigation': 'Basculer la navigation',
// body
// nav side bar
    'Miscellaneous': 'Autres',
    'Global Statistics': 'Statistiques Globales',
    'About': 'À propos',
    'Displaying': 'Affichage de',
    'of': 'parmi',
    'downloads': 'téléchargements',
    'Language': 'Langage',
// download filters
    'Download Filters': 'Filtres de Téléchargement',
    'Running': 'En cours',
    'Active': 'Actifs',
    'Waiting': 'En Attente',
    'Complete': 'Complétés',
    'Error': 'Erreurs',
    'Paused': 'En Pause',
    'Removed': 'Supprimés',
    'Hide linked meta-data': 'Cacher les méta-données liées',
    'Toggle': 'Basculer',
    'Reset filters': 'Réinitialiser les filtres',
// starred properties
    'Quick Access Settings': 'Paramètres d\'Accès Rapide',
    'Save settings': 'Sauvegarder les paramètres',
    'Currently no download in line to display, use the': 'Aucun téléchargement dans la file d\'attente, utilisez le button de téléchargement',
    'download button to start downloading files!': 'pour commencer à télécharger des fichiers!',
    'Peers': 'Pairs',
    'More Info': 'Plus d\'infos',
    'Remove': 'Supprimer',
    '# of': '# parmi',
    'Length': 'Longueur',
// modals
    'Add Downloads By URIs': 'Ajouter des téléchargements depuis des URIs',
    '- You can add multiple downloads (files) at the same time by putting URIs for each file on a separate line.':
        'Vous pouvez ajouter plusieurs téléchargements (fichiers) en même temps, en mettant une URI pour chaque fichier sur une nouveau ligne',
    '- You can also add multiple URIs (mirrors) for the *same* file. To do this, separate the URIs by a space.':
        'Vous pouvez aussi ajouter plusieurs URIs (mirroirs) pour le *même* fichier. Pour ce faire, séparez les URIs par un espace.',
    '- A URI can be HTTP(S)/FTP/BitTorrent-Magnet.': 'Une URI peut être HTTP(S)/FTP/BitTorrent-Magnet.',
    'Download settings': 'Paramètres de téléchargement',
    'Advanced settings': 'Paramètres avancés',
    'Cancel': 'Annuler',
    'Start': 'Démarrer',
    'Choose': 'Choisir',
    'Quick Access (shown on the main page)': 'Accès Rapide (affiché sur la page principale',
// add torrent modal
    'Add Downloads By Torrents': 'Ajouter des téléchargements à partir de fichiers Torrent',
    '- Select the torrent from the local filesystem to start the download.': '- Sélectionnez le torrent depuis votre système de fichier local pour commencer le téléchargement.',
    '- You can select multiple torrents to start multiple downloads.': 'Vous pouvez sélectionner plusieurs torrents pour commencer plusieurs téléchargements.',
    '- To add a BitTorrent-Magnet URL, use the Add By URI option and add it there.': 'Pour ajouter une URL BitTorrent-Magnet, utilisez l\'option Ajoutez Par URIs et ajoutez-la à ce niveau.',
    'Select Torrents': 'Sélectionnez des Torrents',
    'Select a Torrent': 'Sélectionnez un Torrent',
// add metalink modal
    'Add Downloads By Metalinks': 'Ajoutez des téléchargements Par Metaliens',
    'Select Metalinks': 'Sélectionnez des Métaliens',
    '- Select the Metalink from the local filesystem to start the download.': 'Sélectionnez le Métalien depuis votre système de fichier local pour commencer le téléchargement.',
    '- You can select multiple Metalinks to start multiple downloads.': 'Vous pouvez sélectionner plusieurs Metaliens pour commencer plusieurs téléchargements.',
    'Select a Metalink': 'Sélectionnez un Métalien',
// select file modal
    'Choose files to start download for': 'Sélectionnez les fichiers pour lesquels commencer le téléchargement.',
    'Select to download': 'Sélectionner pour télécharger',
// settings modal
    'Aria2 RPC host and port': 'Hôte et ports Aria2 RPC',
    'Enter the host': 'Entrez l\'hôte',
    'Enter the IP or DNS name of the server on which the RPC for Aria2 is running (default: localhost)':
        'Entrez l\'IP ou le nom DNS du serveur sur lequel est lancé le RPC pour Aria2 (défaut : localhost)',
    'Enter the port': 'Entrez le port',
    'Enter the port of the server on which the RPC for Aria2 is running (default: 6800)':
        'Entrez le port du serveur sur lequel tourne le RPC pour Aria2 (défaut : 6800)',
    'Enter the RPC path': 'Entrez le chemin vers le RPC',
    'Enter the path for the Aria2 RPC endpoint (default: /jsonrpc)': 'Entrez le chemin final pour le RPC Aria2 (défaut : /jsonrpc)',
    'SSL/TLS encryption': 'Chiffrage SSL/TLS',
    'Enable SSL/TLS encryption': 'Activer le chiffrage SSL/TLS',
    'Enter the secret token (optional)': 'Entrez le token secret (optionnel)',
    'Enter the Aria2 RPC secret token (leave empty if authentication is not enabled)':
        'Entrez le token secret pour le RPC Aria2 (laissez vide si l\'authentification n\'est pas activée)',
    'Enter the username (optional)': 'Entrez le nom d\'utilisateur (optionnel)',
    'Enter the Aria2 RPC username (empty if authentication not enabled)':
        'Entrez le nom d\'utilisateur RPC Aria2 (laissez vide si l\authentification n\'est pas activée)',
    'Enter the password (optional)': 'Entrez le mot de passe (optionnel)',
    'Enter the Aria2 RPC password (empty if authentication not enabled)': 'Entrezle mot de passe RPC Aria2 (laissez vide si l\'authentification n\'est pas activée)',
    'Enter base URL (optional)': 'Entrez l\'URL de base',
    'Direct Download': 'Téléchargement Direct',
    'If supplied, links will be created to enable direct download from the Aria2 server.':
        'S\'ils sont fournis, les liens seront créés pour activer le téléchargement direct depuis le serveur Aria2',
    '(Requires appropriate webserver to be configured.)': '(Requière un serveur web approprié pour être configuré)',
    'Save Connection configuration': 'Sauvegarder la configuration de Connexion',
// server info modal
    'Aria2 server info': 'Infos serveur Aria2',
    'Aria2 Version': 'Version Aria2',
    'Features Enabled': 'Fonctionnalités Activées',
// about modal
    'To download the latest version of the project, add issues or to contribute back, head on to':
        'Pour télécharger la dernière version du projet, signaler des problèmes ou pour contribuer, dirigez vous vers',
    'Or you can open the latest version in the browser through': 'Ou vous pouvez ouvrir la dernière version dans le navigateur depuis',
    'Close': 'Fermer',
// lables
    'Download status':'Statut de téléchargement',
    'Download Speed':'Vitesse de téléchargement',
    'Upload Speed':'Vitesse d\'envoi',
    'Estimated time':'Temps estimé',
    'Download Size':'Taille du téléchargement',
    'Downloaded':'Téléchargé',
    'Progress':'Avancement',
    'Download Path':'Chemin de téléchargement',
    'Uploaded':'Envoyé',
    'Download GID':'GID du téléchargement',
    'Number of Pieces':'Nombre de pièces',
    'Piece Length':'Taille de la pièce'
};
<commit_msg>Update fr_FR.js<commit_after>if(typeof translations == 'undefined'){
    translations = {};
}

translations.fr_FR = {
// header
    'Search': 'Rechercher',
    // Nav menu
    'Add': 'Ajouter',
    'By URIs': 'Par URIs',
    'By Torrents': 'Par Torrents',
    'By Metalinks': 'Par Metaliens',
    'Manage': 'Gérer',
    'Pause All': 'Tout suspendre',
    'Resume Paused': 'Reprendre',
    'Purge Completed': 'Nettoyer les fichiers complétés',
    'Settings': 'Paramètres',
    'Connection Settings': 'Paramètres de connexion',
    'Global Settings': 'Paramètres globaux',
    'Server info': 'Informations serveur',
    'About and contribute': 'À propos et contribuer',
    'Toggle navigation': 'Basculer la navigation',
// body
// nav side bar
    'Miscellaneous': 'Autres',
    'Global Statistics': 'Statistiques globales',
    'About': 'À propos',
    'Displaying': 'Affichage de',
    'of': 'parmi',
    'downloads': 'téléchargements',
    'Language': 'Langue',
// download filters
    'Download Filters': 'Filtres de téléchargement',
    'Running': 'En cours',
    'Active': 'Actifs',
    'Waiting': 'En attente',
    'Complete': 'Complétés',
    'Error': 'Erreurs',
    'Paused': 'En pause',
    'Removed': 'Supprimés',
    'Hide linked meta-data': 'Cacher les métadonnées liées',
    'Toggle': 'Basculer',
    'Reset filters': 'Réinitialiser les filtres',
// starred properties
    'Quick Access Settings': 'Paramètres d\'accès rapide',
    'Save settings': 'Sauvegarder les paramètres',
    'Currently no download in line to display, use the': 'Aucun téléchargement dans la file d\'attente, utilisez le bouton de téléchargement',
    'download button to start downloading files!': 'pour commencer à télécharger des fichiers!',
    'Peers': 'Pairs',
    'More Info': 'Plus d\'infos',
    'Remove': 'Supprimer',
    '# of': '# parmi',
    'Length': 'Longueur',
// modals
    'Add Downloads By URIs': 'Ajouter des téléchargements depuis des URIs',
    '- You can add multiple downloads (files) at the same time by putting URIs for each file on a separate line.':
        'Vous pouvez ajouter plusieurs téléchargements (fichiers) en même temps, en mettant une URI pour chaque fichier sur une nouvelle ligne',
    '- You can also add multiple URIs (mirrors) for the *same* file. To do this, separate the URIs by a space.':
        'Vous pouvez aussi ajouter plusieurs URIs (mirroirs) pour le *même* fichier. Pour ce faire, séparez les URIs par un espace.',
    '- A URI can be HTTP(S)/FTP/BitTorrent-Magnet.': 'Une URI peut être HTTP(S)/FTP/BitTorrent-Magnet.',
    'Download settings': 'Paramètres de téléchargement',
    'Advanced settings': 'Paramètres avancés',
    'Cancel': 'Annuler',
    'Start': 'Démarrer',
    'Choose': 'Choisir',
    'Quick Access (shown on the main page)': 'Accès rapide (affiché sur la page principale',
// add torrent modal
    'Add Downloads By Torrents': 'Ajouter des téléchargements à partir de fichiers Torrent',
    '- Select the torrent from the local filesystem to start the download.': '- Sélectionnez le torrent depuis votre système de fichier local pour commencer le téléchargement.',
    '- You can select multiple torrents to start multiple downloads.': 'Vous pouvez sélectionner plusieurs torrents pour commencer plusieurs téléchargements.',
    '- To add a BitTorrent-Magnet URL, use the Add By URI option and add it there.': 'Pour ajouter une URL BitTorrent-Magnet, utilisez l\'option Ajouter par URIs et ajoutez-la à ce niveau.',
    'Select Torrents': 'Sélectionner des Torrents',
    'Select a Torrent': 'Sélectionner un Torrent',
// add metalink modal
    'Add Downloads By Metalinks': 'Ajouter des téléchargements par Metaliens',
    'Select Metalinks': 'Sélectionner des Métaliens',
    '- Select the Metalink from the local filesystem to start the download.': 'Sélectionner le Métalien depuis votre système de fichier local pour commencer le téléchargement.',
    '- You can select multiple Metalinks to start multiple downloads.': 'Vous pouvez sélectionner plusieurs Métaliens pour commencer plusieurs téléchargements.',
    'Select a Metalink': 'Sélectionner un Métalien',
// select file modal
    'Choose files to start download for': 'Sélectionner les fichiers pour lesquels commencer le téléchargement.',
    'Select to download': 'Sélectionner pour télécharger',
// settings modal
    'Aria2 RPC host and port': 'Hôte et ports Aria2 RPC',
    'Enter the host': 'Entrer l\'hôte',
    'Enter the IP or DNS name of the server on which the RPC for Aria2 is running (default: localhost)':
        'Entrer l\'IP ou le nom DNS du serveur sur lequel est lancé le RPC pour Aria2 (défaut : localhost)',
    'Enter the port': 'Entrer le port',
    'Enter the port of the server on which the RPC for Aria2 is running (default: 6800)':
        'Entrer le port du serveur sur lequel tourne le RPC pour Aria2 (défaut : 6800)',
    'Enter the RPC path': 'Entrer le chemin vers le RPC',
    'Enter the path for the Aria2 RPC endpoint (default: /jsonrpc)': 'Entrer le chemin final pour le RPC Aria2 (défaut : /jsonrpc)',
    'SSL/TLS encryption': 'Chiffrage SSL/TLS',
    'Enable SSL/TLS encryption': 'Activer le chiffrage SSL/TLS',
    'Enter the secret token (optional)': 'Entrer le token secret (optionnel)',
    'Enter the Aria2 RPC secret token (leave empty if authentication is not enabled)':
        'Entrer le token secret pour le RPC Aria2 (laisser vide si l\'authentification n\'est pas activée)',
    'Enter the username (optional)': 'Entrer le nom d\'utilisateur (optionnel)',
    'Enter the Aria2 RPC username (empty if authentication not enabled)':
        'Entrer le nom d\'utilisateur RPC Aria2 (laisser vide si l\authentification n\'est pas activée)',
    'Enter the password (optional)': 'Entrer le mot de passe (optionnel)',
    'Enter the Aria2 RPC password (empty if authentication not enabled)': 'Entrer le mot de passe RPC Aria2 (laisser vide si l\'authentification n\'est pas activée)',
    'Enter base URL (optional)': 'Entrez l\'URL de base',
    'Direct Download': 'Téléchargement direct',
    'If supplied, links will be created to enable direct download from the Aria2 server.':
        'S\'ils sont fournis, les liens seront créés pour activer le téléchargement direct depuis le serveur Aria2',
    '(Requires appropriate webserver to be configured.)': '(Nécessite un serveur web approprié pour être configuré)',
    'Save Connection configuration': 'Sauvegarder la configuration de connexion',
// server info modal
    'Aria2 server info': 'Infos serveur Aria2',
    'Aria2 Version': 'Version Aria2',
    'Features Enabled': 'Fonctionnalités activées',
// about modal
    'To download the latest version of the project, add issues or to contribute back, head on to':
        'Pour télécharger la dernière version du projet, signaler des problèmes ou pour contribuer, aller à l'adresse',
    'Or you can open the latest version in the browser through': 'Ou vous pouvez ouvrir la dernière version dans le navigateur depuis',
    'Close': 'Fermer',
// lables
    'Download status':'Statut de téléchargement',
    'Download Speed':'Vitesse de téléchargement',
    'Upload Speed':'Vitesse d\'envoi',
    'Estimated time':'Temps estimé',
    'Download Size':'Taille du téléchargement',
    'Downloaded':'Téléchargé',
    'Progress':'Avancement',
    'Download Path':'Chemin de téléchargement',
    'Uploaded':'Envoyé',
    'Download GID':'GID du téléchargement',
    'Number of Pieces':'Nombre de pièces',
    'Piece Length':'Taille de la pièce'
};
<|endoftext|>