<commit_before>'use strict';

var winsize = [1366, 768]
var game = new Phaser.Game(winsize[0], winsize[1], Phaser.AUTO, '', null, false, false);

var K = function() {};
K.prototype = {
    placingStone: false,
    hoveringStone: false,
    gameOver: false,
    consecutivePasses: 0,

    aiOn: false,
    aiPlayer: 0,

    currentPlayer: 0, // 0 black, 1 white
    capturedScore: [0, 0],
    areaScore: [0, 0],
    totalScore: [0, 0],
    placedStones: new Array(19), // hmm
    permittedPlacements: new Array(19),
    previouslyCaptured: [],
    previousPlacement: 0,

    boardPos: [0, 420],
    placementPositions: new Array(19),
    hoverBlackSprite: undefined,
    hoverWhiteSprite: undefined,
    spriteScale: 4.8,

    stateText: undefined,
    capturedTitleText: undefined,
    capturedBlackText: undefined,
    capturedWhiteText: undefined,
    captureScoreBlackText: undefined,
    captureScoreWhiteText: undefined,
    areaScoreBlackText: undefined,
    areaScoreWhiteText: undefined,
    totalScoreBlackText: undefined,
    totalScoreWhiteText: undefined,

    capturedBlackSprite: undefined,
    capturedWhiteSprite: undefined,

    init: function() {
        for(let i = 0; i < 19; ++i) {
            this.placementPositions[i] = [
                this.boardPos[0] + i * 15 * this.spriteScale,
                this.boardPos[1]
            ];
            this.permittedPlacements[i] = true;
        }
        // gameplay
        this.stateText = game.add.text(winsize[0] / 2, 80, "Black to move", {font: '56px nokiafc', fill: '#ffffff'});
        this.capturedTitleText = game.add.text(30, 550, "Captured pieces:", {font: '56px nokiafc', fill: '#ffffff'});
        this.capturedBlackText = game.add.text(130, 620, "x 0", {font: '56px nokiafc', fill: '#ffffff'});
        this.capturedWhiteText = game.add.text(130, 690, "x 0", {font: '56px nokiafc', fill: '#ffffff'});
        this.capturedBlackSprite = game.add.sprite(40, 615, 'black');
        this.capturedWhiteSprite = game.add.sprite(40, 685, 'white');
        this.hoverBlackSprite = game.add.sprite(0, 0, 'black_hover');
        this.hoverWhiteSprite = game.add.sprite(0, 0, 'white_hover');
        this.capturedBlackSprite.scale.set(k.spriteScale, k.spriteScale);
        this.capturedWhiteSprite.scale.set(k.spriteScale, k.spriteScale);
        this.hoverBlackSprite.scale.set(k.spriteScale, k.spriteScale);
        this.hoverWhiteSprite.scale.set(k.spriteScale, k.spriteScale);

        let xPos = 320;
        // endgame
        this.captureScoreBlackText = game.add.text(xPos, 560, "Capture score: 0", {font: '48px nokiafc', fill: '#ffffff'});
        this.captureScoreWhiteText = game.add.text(winsize[0] - xPos, 560, "Capture score: 0", {font: '48px nokiafc', fill: '#ffffff'});
        this.areaScoreBlackText = game.add.text(xPos, 630, "Area score: 0", {font: '48px nokiafc', fill: '#ffffff'});
        this.areaScoreWhiteText = game.add.text(winsize[0] - xPos, 630, "Area score: 0", {font: '48px nokiafc', fill: '#ffffff'});
        this.totalScoreBlackText = game.add.text(xPos, 710, "Total score: 0", {font: '56px nokiafc', fill: '#ffffff'});
        this.totalScoreWhiteText = game.add.text(winsize[0] - xPos, 710, "Total score: 0", {font: '56px nokiafc', fill: '#ffffff'});

        util.recentreText(this.stateText);
        util.recentreText(this.captureScoreBlackText);
        util.recentreText(this.captureScoreWhiteText);
        util.recentreText(this.areaScoreBlackText);
        util.recentreText(this.areaScoreWhiteText);
        util.recentreText(this.totalScoreBlackText);
        util.recentreText(this.totalScoreWhiteText);

        this.stateText.visible = false;
        this.capturedTitleText.visible = false;
        this.capturedBlackText.visible = false;
        this.capturedWhiteText.visible = false;
        this.capturedBlackSprite.visible = false;
        this.capturedWhiteSprite.visible = false;
        this.hoverBlackSprite.visible = false;
        this.hoverWhiteSprite.visible = false;
        this.captureScoreBlackText.visible = false;
        this.captureScoreWhiteText.visible = false;
        this.areaScoreBlackText.visible = false;
        this.areaScoreWhiteText.visible = false;
        this.totalScoreBlackText.visible = false;
        this.totalScoreWhiteText.visible = false;
    },
};
var k = new K();

var MainState = function() {};
MainState.prototype = {
    // buttons here to access functions lol
    buttonPvP: undefined,
    buttonAI: undefined,
    buttonPass: undefined,
    startGame: function() {
        this.buttonPvP.visible = false;
        this.buttonAI.visible = false;

        k.stateText.visible = true;
        k.capturedTitleText.visible = true;
        k.capturedBlackText.visible = true;
        k.capturedWhiteText.visible = true;
        k.capturedBlackSprite.visible = true;
        k.capturedWhiteSprite.visible = true;
        this.buttonPass.visible = true;

        k.placingStone = true;
    },
    pass: function() {
        if(++k.consecutivePasses == 2) {
            k.gameOver = true;
            this.endGame();
        }
        else {
            k.currentPlayer = !k.currentPlayer;
            k.previousPlacement = undefined;
            let colour = k.currentPlayer == 0 ? 'Black' : 'White';
            k.stateText.text = colour + " to move";
        }
    },
    endGame: function() {
        // calculate captured + dead stones
        // calculate area
        k.stateText.text = "Who won?";
        k.capturedTitleText.visible = false;
        k.capturedBlackText.visible = false;
        k.capturedWhiteText.visible = false;
        k.capturedBlackSprite.visible = false;
        k.capturedWhiteSprite.visible = false;
        this.buttonPass.visible = false;
        k.hoverBlackSprite.visible = false;
        k.hoverWhiteSprite.visible = false;
        k.captureScoreBlackText.visible = true;
        k.captureScoreWhiteText.visible = true;
        k.areaScoreBlackText.visible = true;
        k.areaScoreWhiteText.visible = true;
        k.totalScoreBlackText.visible = true;
        k.totalScoreWhiteText.visible = true;
    },
    preload: function() {
        game.load.image('black', 'assets/sprites/black.png');
        game.load.image('white', 'assets/sprites/white.png');
        game.load.image('black_hover', 'assets/sprites/black_hover.png');
        game.load.image('white_hover', 'assets/sprites/white_hover.png');
        game.load.image('bg', 'assets/sprites/bg.png');

        game.load.spritesheet('buttonPvP', 'assets/sprites/buttonpvp.png', 200, 60, 2);
        game.load.spritesheet('buttonAI', 'assets/sprites/buttonai.png', 200, 60, 2);
        game.load.spritesheet('buttonPass', 'assets/sprites/buttonpass.png', 200, 60, 2);

	    game.load.script('utilScript', '../js/util.js');
        game.add.text(0, 0, "", {font: '56px nokiafc', fill: '#ffffff'});
    },
    create: function() {
        var bg = game.add.sprite(k.boardPos[0], k.boardPos[1], 'bg');
        bg.scale.set(k.spriteScale, k.spriteScale);
        game.stage.backgroundColor = "#333333";
        game.scale.pageAlignHorizontally = true; game.scale.pageAlignVertically = true;
        game.stage.smoothed = false;
        game.input.mouse.mouseUpCallback = () => this.handleMouseUp(); // arrow function for correct `this`

        this.buttonAI = game.add.button(685, 200, 'buttonAI', this.startGame, this, 1, 0, 1);
        this.buttonPvP = game.add.button(481, 200, 'buttonPvP', this.startGame, this, 1, 0, 1);

        this.buttonPass = game.add.button(winsize[0] / 2 - 100, 200, 'buttonPass', this.pass, this, 1, 0, 1);
        this.buttonPass.visible = false;
        k.init();
    },
    update: function() {
        if(k.gameOver) {
        }
        else if(k.placingStone) {
            if(this.atPermittedPlacement()) {
                k.hoveringStone = true;
                let i = this.closestPos();
                if(k.currentPlayer == 0) {
                    k.hoverBlackSprite.visible = true;
                    k.hoverBlackSprite.x = k.placementPositions[i][0];
                    k.hoverBlackSprite.y = k.placementPositions[i][1];
                }
                else {
                    k.hoverWhiteSprite.visible = true;
                    k.hoverWhiteSprite.x = k.placementPositions[i][0];
                    k.hoverWhiteSprite.y = k.placementPositions[i][1];
                }
            }
            else {
                k.hoveringStone = false;
                k.hoverBlackSprite.visible = false;
                k.hoverWhiteSprite.visible = false;
            }
        }
    },
    closestPos: function() {
        return Math.floor(game.input.mousePointer.x / (winsize[0]/19));
    },
    atPermittedPlacement: function() {
        let mouseY = game.input.mousePointer.y;
        if(mouseY > k.boardPos[1] && mouseY < k.boardPos[1] + 72) {
            if(k.permittedPlacements[this.closestPos()] === true) {
                return true;
            }
        }
        return false;
    },
    handleMouseUp: function() {
        if(k.hoveringStone) {
            // add sprite
            let hej = this.closestPos();
            let colour = k.currentPlayer == 0 ? 'black' : 'white';
            let pos = k.placementPositions[hej];
            let newStone = game.add.sprite(pos[0], pos[1], colour);
            newStone.scale.set(k.spriteScale, k.spriteScale);
            k.placedStones[hej] = newStone;

            // check for captures
            let directions = [-1, 1];
            if(hej == 0) {
                directions = [1];
            }
            else if(hej == 18) {
                directions = [-1];
            }
            k.previouslyCaptured = [];
            let antiColour = k.currentPlayer == 0 ? 'white' : 'black';
            for(let dir of directions) {
                let maybeCaptured = [];
                let hej2 = hej + dir;
                while(k.placedStones[hej2] != undefined && k.placedStones[hej2].key == antiColour) {
                    maybeCaptured.push(hej2);
                    hej2 += dir;
                }
                if(maybeCaptured.length > 0) {
                    let sameColour = k.placedStones[hej2] != undefined && k.placedStones[hej2].key == colour;
                    if(hej2 == -1 || hej2 == 19 || sameColour) {
                        k.previouslyCaptured.push(...maybeCaptured);
                    }
                }
            }
            for(let i of k.previouslyCaptured) {
                k.placedStones[i].destroy();
                k.placedStones[i] = undefined;
            }
            k.capturedScore[k.currentPlayer] += k.previouslyCaptured.length;

            // updated permitted placements
                // check for suicides but keep in mind capture attacks
                // check for ko
            //permittedPlacements: new Array(19),

            // update other stuff
            k.consecutivePasses = 0;
            k.currentPlayer = k.currentPlayer == 0 ? 1 : 0;
            k.previousPlacement = i;
            colour = k.currentPlayer == 0 ? 'Black' : 'White';
            k.stateText.text = colour + " to move";
            k.capturedBlackText.text = "x " + k.capturedScore[1];
            k.capturedWhiteText.text = "x " + k.capturedScore[0];
        }
    },
};

game.state.add('MainState', MainState);
game.state.start('MainState');
<commit_msg>Go: Suicide prevention works<commit_after>'use strict';

var winsize = [1366, 768]
var game = new Phaser.Game(winsize[0], winsize[1], Phaser.AUTO, '', null, false, false);

var K = function() {};
K.prototype = {
    placingStone: false,
    hoveringStone: false,
    gameOver: false,
    consecutivePasses: 0,

    aiOn: false,
    aiPlayer: 0,

    currentPlayer: 0, // 0 black, 1 white
    capturedScore: [0, 0],
    areaScore: [0, 0],
    totalScore: [0, 0],
    placedStones: new Array(19), // hmm
    permittedPlacements: new Array(19),
    previouslyCaptured: [],
    previousPlacement: 0,

    boardPos: [0, 420],
    placementPositions: new Array(19),
    hoverBlackSprite: undefined,
    hoverWhiteSprite: undefined,
    spriteScale: 4.8,

    stateText: undefined,
    capturedTitleText: undefined,
    capturedBlackText: undefined,
    capturedWhiteText: undefined,
    captureScoreBlackText: undefined,
    captureScoreWhiteText: undefined,
    areaScoreBlackText: undefined,
    areaScoreWhiteText: undefined,
    totalScoreBlackText: undefined,
    totalScoreWhiteText: undefined,

    capturedBlackSprite: undefined,
    capturedWhiteSprite: undefined,

    init: function() {
        for(let i = 0; i < 19; ++i) {
            this.placementPositions[i] = [
                this.boardPos[0] + i * 15 * this.spriteScale,
                this.boardPos[1]
            ];
            this.permittedPlacements[i] = true;
        }
        // gameplay
        this.stateText = game.add.text(winsize[0] / 2, 80, "Black to move", {font: '56px nokiafc', fill: '#ffffff'});
        this.capturedTitleText = game.add.text(30, 550, "Captured pieces:", {font: '56px nokiafc', fill: '#ffffff'});
        this.capturedBlackText = game.add.text(130, 620, "x 0", {font: '56px nokiafc', fill: '#ffffff'});
        this.capturedWhiteText = game.add.text(130, 690, "x 0", {font: '56px nokiafc', fill: '#ffffff'});
        this.capturedBlackSprite = game.add.sprite(40, 615, 'black');
        this.capturedWhiteSprite = game.add.sprite(40, 685, 'white');
        this.hoverBlackSprite = game.add.sprite(0, 0, 'black_hover');
        this.hoverWhiteSprite = game.add.sprite(0, 0, 'white_hover');
        this.capturedBlackSprite.scale.set(k.spriteScale, k.spriteScale);
        this.capturedWhiteSprite.scale.set(k.spriteScale, k.spriteScale);
        this.hoverBlackSprite.scale.set(k.spriteScale, k.spriteScale);
        this.hoverWhiteSprite.scale.set(k.spriteScale, k.spriteScale);

        let xPos = 320;
        // endgame
        this.captureScoreBlackText = game.add.text(xPos, 560, "Capture score: 0", {font: '48px nokiafc', fill: '#ffffff'});
        this.captureScoreWhiteText = game.add.text(winsize[0] - xPos, 560, "Capture score: 0", {font: '48px nokiafc', fill: '#ffffff'});
        this.areaScoreBlackText = game.add.text(xPos, 630, "Area score: 0", {font: '48px nokiafc', fill: '#ffffff'});
        this.areaScoreWhiteText = game.add.text(winsize[0] - xPos, 630, "Area score: 0", {font: '48px nokiafc', fill: '#ffffff'});
        this.totalScoreBlackText = game.add.text(xPos, 710, "Total score: 0", {font: '56px nokiafc', fill: '#ffffff'});
        this.totalScoreWhiteText = game.add.text(winsize[0] - xPos, 710, "Total score: 0", {font: '56px nokiafc', fill: '#ffffff'});

        util.recentreText(this.stateText);
        util.recentreText(this.captureScoreBlackText);
        util.recentreText(this.captureScoreWhiteText);
        util.recentreText(this.areaScoreBlackText);
        util.recentreText(this.areaScoreWhiteText);
        util.recentreText(this.totalScoreBlackText);
        util.recentreText(this.totalScoreWhiteText);

        this.stateText.visible = false;
        this.capturedTitleText.visible = false;
        this.capturedBlackText.visible = false;
        this.capturedWhiteText.visible = false;
        this.capturedBlackSprite.visible = false;
        this.capturedWhiteSprite.visible = false;
        this.hoverBlackSprite.visible = false;
        this.hoverWhiteSprite.visible = false;
        this.captureScoreBlackText.visible = false;
        this.captureScoreWhiteText.visible = false;
        this.areaScoreBlackText.visible = false;
        this.areaScoreWhiteText.visible = false;
        this.totalScoreBlackText.visible = false;
        this.totalScoreWhiteText.visible = false;
    },
};
var k = new K();

var MainState = function() {};
MainState.prototype = {
    // buttons here to access functions lol
    buttonPvP: undefined,
    buttonAI: undefined,
    buttonPass: undefined,
    startGame: function() {
        this.buttonPvP.visible = false;
        this.buttonAI.visible = false;

        k.stateText.visible = true;
        k.capturedTitleText.visible = true;
        k.capturedBlackText.visible = true;
        k.capturedWhiteText.visible = true;
        k.capturedBlackSprite.visible = true;
        k.capturedWhiteSprite.visible = true;
        this.buttonPass.visible = true;

        k.placingStone = true;
    },
    pass: function() {
        if(++k.consecutivePasses == 2) {
            k.gameOver = true;
            this.endGame();
        }
        else {
            k.currentPlayer = !k.currentPlayer;
            k.previousPlacement = undefined;
            let colour = k.currentPlayer == 0 ? 'Black' : 'White';
            k.stateText.text = colour + " to move";
        }
    },
    endGame: function() {
        // calculate captured + dead stones
        // calculate area
        k.stateText.text = "Who won?";
        k.capturedTitleText.visible = false;
        k.capturedBlackText.visible = false;
        k.capturedWhiteText.visible = false;
        k.capturedBlackSprite.visible = false;
        k.capturedWhiteSprite.visible = false;
        this.buttonPass.visible = false;
        k.hoverBlackSprite.visible = false;
        k.hoverWhiteSprite.visible = false;
        k.captureScoreBlackText.visible = true;
        k.captureScoreWhiteText.visible = true;
        k.areaScoreBlackText.visible = true;
        k.areaScoreWhiteText.visible = true;
        k.totalScoreBlackText.visible = true;
        k.totalScoreWhiteText.visible = true;
    },
    preload: function() {
        game.load.image('black', 'assets/sprites/black.png');
        game.load.image('white', 'assets/sprites/white.png');
        game.load.image('black_hover', 'assets/sprites/black_hover.png');
        game.load.image('white_hover', 'assets/sprites/white_hover.png');
        game.load.image('bg', 'assets/sprites/bg.png');

        game.load.spritesheet('buttonPvP', 'assets/sprites/buttonpvp.png', 200, 60, 2);
        game.load.spritesheet('buttonAI', 'assets/sprites/buttonai.png', 200, 60, 2);
        game.load.spritesheet('buttonPass', 'assets/sprites/buttonpass.png', 200, 60, 2);

	    game.load.script('utilScript', '../js/util.js');
        game.add.text(0, 0, "", {font: '56px nokiafc', fill: '#ffffff'});
    },
    create: function() {
        var bg = game.add.sprite(k.boardPos[0], k.boardPos[1], 'bg');
        bg.scale.set(k.spriteScale, k.spriteScale);
        game.stage.backgroundColor = "#333333";
        game.scale.pageAlignHorizontally = true; game.scale.pageAlignVertically = true;
        game.stage.smoothed = false;
        game.input.mouse.mouseUpCallback = () => this.handleMouseUp(); // arrow function for correct `this`

        this.buttonAI = game.add.button(685, 200, 'buttonAI', this.startGame, this, 1, 0, 1);
        this.buttonPvP = game.add.button(481, 200, 'buttonPvP', this.startGame, this, 1, 0, 1);

        this.buttonPass = game.add.button(winsize[0] / 2 - 100, 200, 'buttonPass', this.pass, this, 1, 0, 1);
        this.buttonPass.visible = false;
        k.init();
    },
    update: function() {
        if(k.gameOver) {
        }
        else if(k.placingStone) {
            if(this.atPermittedPlacement()) {
                k.hoveringStone = true;
                let i = this.closestPos();
                if(k.currentPlayer == 0) {
                    k.hoverBlackSprite.visible = true;
                    k.hoverBlackSprite.x = k.placementPositions[i][0];
                    k.hoverBlackSprite.y = k.placementPositions[i][1];
                }
                else {
                    k.hoverWhiteSprite.visible = true;
                    k.hoverWhiteSprite.x = k.placementPositions[i][0];
                    k.hoverWhiteSprite.y = k.placementPositions[i][1];
                }
            }
            else {
                k.hoveringStone = false;
                k.hoverBlackSprite.visible = false;
                k.hoverWhiteSprite.visible = false;
            }
        }
    },
    closestPos: function() {
        return Math.floor(game.input.mousePointer.x / (winsize[0]/19));
    },
    atPermittedPlacement: function() {
        let mouseY = game.input.mousePointer.y;
        if(mouseY > k.boardPos[1] && mouseY < k.boardPos[1] + 72) {
            if(k.permittedPlacements[this.closestPos()] === true) {
                return true;
            }
        }
        return false;
    },
    handleMouseUp: function() {
        if(k.hoveringStone) {
            // add sprite
            let hej = this.closestPos();
            let colour = k.currentPlayer == 0 ? 'black' : 'white';
            let pos = k.placementPositions[hej];
            let newStone = game.add.sprite(pos[0], pos[1], colour);
            newStone.scale.set(k.spriteScale, k.spriteScale);
            k.placedStones[hej] = newStone;

            // check for captures
            let directions = [-1, 1];
            k.previouslyCaptured = [];
            let antiColour = k.currentPlayer == 0 ? 'white' : 'black';
            for(let dir of directions) {
                let maybeCaptured = [];
                let hoj = hej + dir;
                while(k.placedStones[hoj] != undefined && k.placedStones[hoj].key == antiColour) {
                    maybeCaptured.push(hoj);
                    hoj += dir;
                }
                if(maybeCaptured.length > 0) {
                    let sameColour = k.placedStones[hoj] != undefined && k.placedStones[hoj].key == colour;
                    if(hoj == -1 || hoj == 19 || sameColour) {
                        k.previouslyCaptured.push(...maybeCaptured);
                    }
                }
            }
            for(let i of k.previouslyCaptured) {
                k.placedStones[i].destroy();
                k.placedStones[i] = undefined;
            }
            k.capturedScore[k.currentPlayer] += k.previouslyCaptured.length;

            // updated permitted placements
            k.permittedPlacements.forEach(function(value, i) {
                let permitted = true;
                if(k.placedStones[i] != undefined) {
                    // may not place where stones exist
                    permitted = false;
                }
                else {
                    // suicide prevention
                    let directions = [-1, 1];
                    let surrounded = [0, 0]; // 0
                    let antiColour = k.currentPlayer == 0 ? 'white' : 'black';
                    directions.forEach(function(dir, j) {
                        let hoj = i + dir;
                        while(k.placedStones[hoj] != undefined && k.placedStones[hoj].key == antiColour) {
                            // skip over the stones of the next player's colour
                            hoj += dir;
                        }
                        if(k.placedStones[hoj] == undefined) {
                            if(hoj == -1 || hoj == 19) {
                                // an edge is equivalent to an enemy stone
                                surrounded[j] = true;
                            }
                            else {
                                surrounded[j] = false;
                            }
                        }
                        else if(k.placedStones[hoj].key == colour) {
                            surrounded[j] = true;
                        }
                    });
                    if(surrounded[0] && surrounded[1]) {
                        permitted = false;
                    }

                    // can capture
                        // is ko
                }
                k.permittedPlacements[i] = permitted;
            });

            // update other stuff
            k.consecutivePasses = 0;
            k.currentPlayer = k.currentPlayer == 0 ? 1 : 0;
            k.previousPlacement = hej;
            colour = k.currentPlayer == 0 ? 'Black' : 'White';
            k.stateText.text = colour + " to move";
            k.capturedBlackText.text = "x " + k.capturedScore[1];
            k.capturedWhiteText.text = "x " + k.capturedScore[0];
        }
    },
};

game.state.add('MainState', MainState);
game.state.start('MainState');
<|endoftext|>


<commit_before>function Midway() {
	$('.centerHorizontal').css('marginLeft', - + $('.centerHorizontal').width()/2);
	$('.centerVertical').css('marginTop', - + $('.centerVertical').height()/2);

	$(".centerHorizontal").css({
		'display' : 'inline',
		'position' : 'absolute',
		'top' : '50%',
		'left' : '50%'
	});

	$(".centerVertical").css({
		'display' : 'inline',
		'position' : 'absolute',
		'top' : '50%',
		'left' : '50%'
	});
}

$(window).load(function() {
	Midway();
	$(window).on('resize', Midway);
});<commit_msg>isolate horizontal and vertical<commit_after>function Midway() {
	$('.centerHorizontal').css('marginLeft', - + $('.centerHorizontal').width()/2);
	$('.centerVertical').css('marginTop', - + $('.centerVertical').height()/2);

	$(".centerHorizontal").css({
		'display' : 'inline',
		'position' : 'absolute',
		'left' : '50%'
	});

	$(".centerVertical").css({
		'display' : 'inline',
		'position' : 'absolute',
		'top' : '50%',
	});
}

$(window).load(function() {
	Midway();
	$(window).on('resize', Midway);
});
<|endoftext|>


<commit_before>/* Модуль сжатия js-скриптов, css-стилей, html-файлов и
 * конвертации картинок в css-стилях
 * в base64 и помещения их в файл стилей
 */
var Minify = {};
console.log('minify.js loaded...');

/* функция сжимает js-скрипты
 * и сохраняет их с именем .min.js
 */

var fs = require('fs');
var path=require('path');
var crypto = require('crypto');

var Hashes;

var MinFolder='min/';
/* function clear MinFolder
 * if we could not create
 * directory and it is
 * not exist
 */
var folderExist = function(pError, pStat){
    "use strict";
    /*file found and it's directory */
    if(!pError && pStat.isDirectory())
        console.log('folder exist: ' + MinFolder);
    else MinFolder='/';
};

/*
 * function says thet folder created
 * if everything is OK, or
 * moves to folderExist function
 */
var makeFolder = function(pError){
    "use strict";
    /*folder created successfully*/
    if(!pError)
        console.log('folder created: min');
    else fs.stat(MinFolder,folderExist);
};

/* Trying to create folder min
 * where woud be minifyed versions
 * of files 511(10)=777(8)
 * rwxrwxrwx
 */
fs.mkdir(MinFolder,511,makeFolder);

exports.MinFolder = MinFolder;
exports.Cache    = {};

/*********************************/
/* сжимаем код через uglify-js */
Minify._uglifyJS= function(pDdata){

    /* подключаем модуль uglify-js
     * если его нет - дальнейшая
     * работа функции не имеет смысла
     */
    var jsp;
    var pro;
    try{
        jsp = require("uglify-js").parser;
        pro = require("uglify-js").uglify;
    }catch(error){
        console.log('can\'n load uglify-js\n'                  +
            'to use js-minification you need to install uglify-js\n'    +
                'npm install uglify-js\n'                               +
                'https://github.com/mishoo/UglifyJS');
        return false;
    }

    var orig_code = pDdata.toString();
    var ast = jsp.parse(orig_code); // parse code and get the initial AST
    ast = pro.ast_mangle(ast); // get a new AST with mangled names
    ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
    var result_code = pro.gen_code(ast); // compressed code here
    return result_code;
};

/* сжимаем код через clean-css */
Minify._cleanCSS=function(pData){
    'use strict';
     /* connecting cleanCSS,
      * if we can't find it -
      * return false
      */
     var cleanCSS;
     try{
        cleanCSS = require('clean-css');
    }catch(error){
        console.log('can\'n load clean-css \n'                          +
            'to use css-minification you need to install clean-css \n'  +
                'npm install clean-css\n'                               +
                'https://github.com/GoalSmashers/clean-css');
        return false;
    }
        /* Сохраняем весь стиль в одну переменную*/
    return cleanCSS.process(pData);
};

/* сжимаем код через htmlMinify */
Minify.htmlMinify=function(pData){
    /* Сохраняем весь стиль в одну переменную*/

     /* connecting cleanCSS,
      * if we can't find it -
      * return false
      */
     var htmlMinifier;
     try{
        htmlMinifier = require('html-minifier');
    }catch(error){
        console.log('can\'n load html-minifier \n'                 +
            'to use html-minification you need to install html-minifier\n'  +
                'npm install html-minifier\n'                               +
                'https://github.com/kangax/html-minifier');
        return false;
    }

    var lOptions={
        removeComments:                 true,
        removeCommentsFromCDATA:        true,
        removeCDATASectionsFromCDATA:   true,
        collapseWhitespace:             true,
        collapseBooleanAttributes:      true,
        removeAttributeQuotes:          true,
        removeRedundantAttributes:      true,
        useShortDoctype:                true,
        removeEmptyAttributes:          true,
        /* оставляем, поскольку у нас
         * в элемент fm генерируеться
         * таблица файлов
         */
        removeEmptyElements:            false,
        removeOptionalTags:             true,
        removeScriptTypeAttributes:     true,
        removeStyleLinkTypeAttributes:  true
    };

    return htmlMinifier.minify(pData,lOptions);
};
/*********************************/

/*
 * Функция ищет в имени файла расширение
 * и если находит возвращает true
 * @pName - получает имя файла
 * @pExt - расширение
 */
Minify._checkExtension=function(pName,pExt)
{
    /* если длина имени больше
     * длинны расширения -
     * имеет смысл продолжать
     */
    if(pName.length>pExt.length){
        var lLength=pName.length;           /* длина имени*/
        var lExtNum=pName.lastIndexOf(pExt);/* последнее вхождение расширения*/
        var lExtSub=lLength-lExtNum;        /* длина расширения*/
        /* если pExt - расширение pName */
        if(lExtSub===pExt.length)
            return true;
        else
            return false;
    }
    else return false;
}

/* function minificate js,css and html files
 * @pFiles_a                - array of js, css and html file names or string, if name
 *                            single, or object if postProcessing neaded
 *                              {'client.js': function(pFinalCode){} }
 *                            or convertion images to base64 neaded
 *                              {'style.css': true}
 *                            or {'style.css':{minimize: true, func: function(){}}
 * @pCache_b                - if true files do not writes on disk, just saves
 *                              in Minify Cache
 */
exports.optimize = function(pFiles_a, pCache_b){
    'use strict';

     /* if passed string, or object
     * putting it to array
     */
    if (typeof pFiles_a === 'string' ||
        !pFiles_a[0])
            pFiles_a=[pFiles_a];

    var lName;

    var lAllCSS;
    var dataReaded_f=function(pFileName, pData, pLastFile_b){
        /*
         * if postProcessing function exist
         * getting it from pFileName object
         */
        var lMoreProcessing_f;
        if(typeof pFileName === 'object'){
            var lName;
            for(lName in pFileName){
                break;
            }
            lMoreProcessing_f = pFileName[lName];
            pFileName = lName;
        }
        console.log('file ' + pFileName + ' readed');

        var final_code;
        var minFileName;

        /* if it's js file - getting optimized version */
        if(Minify._checkExtension(pFileName,'js')){

            final_code=Minify._uglifyJS(pData);
            minFileName=pFileName.replace('.js', '.min.js');

        } else if (Minify._checkExtension(pFileName, 'html')) {

            final_code=Minify.htmlMinify(pData);
            minFileName=pFileName.replace('.html', '.min.html');

        } else if (Minify._checkExtension(pFileName, 'css')) {

            final_code=Minify._cleanCSS(pData);
            lAllCSS+=final_code;
            minFileName=pFileName.replace('.css','.min.css');

            /* in css could be lCSS_o
             * {img: true, moreProcessing: function(){}}
             */
            var lCSS_o = lMoreProcessing_f;
            if (typeof lCSS_o === 'object'){
                    lMoreProcessing_f = lCSS_o.moreProcessing;
            }
            /* if it's last file
             * and base64images setted up
             * se should convert it
             */
            if (pLastFile_b && (lCSS_o && lCSS_o.img) ||
                (lCSS_o === true)){
                    base64_images(lAllCSS);
            }
        } else
            return;

         if (!isFileChanged(pFileName, pData, pLastFile_b)) {
            console.log('file: ' + pFileName + ' do not changed...');
            return;
        }

        /* if lMoreProcessing_f seeted up
         * and function associated with
         * current file name exists -
         * run it
         */
        if(lMoreProcessing_f                    &&
            typeof lMoreProcessing_f === "function"){
                final_code = lMoreProcessing_f(final_code);
        }

        minFileName = path.basename(minFileName);

        /* записываем сжатый js-скрипт
         * в кэш если установлен pCache_b
         * или на диск, если не установлен
         */
        if(pCache_b){
            exports.Cache[minFileName] = final_code;
            console.log('file ' + minFileName + ' saved to cache...');
        }
        else{
            /* minimized file will be in min file
             * if it's possible if not -
             * in root
             */
            minFileName = MinFolder + minFileName;

            fs.writeFile(minFileName, final_code, fileWrited(minFileName));
        }
    };
    /* moving thru all elements of js files array */
    for(var i=0; pFiles_a[i]; i++){
        /* if postProcessing function exist
         * getting file name and passet next
         */
        var lPostProcessing_o = pFiles_a[i];
        if(typeof lPostProcessing_o === 'object'){
            for(lName in lPostProcessing_o){
            }
        } else lName = pFiles_a[i];
        console.log('reading file ' + lName + '...');

        /* if it's last file send true */
        fs.readFile(lName,
            fileReaded(pFiles_a[i],
                dataReaded_f,
                (i === pFiles_a.length-1)?true:false));
    }
    /* saving the name of last readed file for hash saving function */

    return true;
};

/* функция переводит картинки в base64 и записывает в css-файл*/
function base64_images(pFileContent_s){
    'use strict';
     var b64img;
     try{
        b64img = require('css-b64-images');
    }catch(error){
        console.log('can\'n load clean-css \n'                 +
            'to use images to base64 convertation you need to install css-base64-images \n'  +
                'npm install -g css-b64-images\n'                               +
                'https://github.com/Filirom1/css-base64-images');
        return false;
    }
    b64img.fromString(pFileContent_s, '.','', function(err, css){
        console.log('images converted to base64 and saved in css file');
        fs.writeFile(MinFolder + 'all.min.css', css, fileWrited(MinFolder + 'all.min.css'));
    });
}

/* Функция создаёт асинхроную версию
 * для чтения файла
 * @pFileName       - имя считываемого файла
 * @pProcessFunc    - функция обработки файла
 * @pLastFile_b     - последний файл?
 */
function fileReaded(pFileName,pProcessFunc, pLastFile_b){
    "use strict";
    return function(pError,pData){
        /* функция в которую мы попадаем,
         * если данные считались
         *
         * если ошибка - показываем её
         * иначе если переданная функция -
         * функция запускаем её
         */
        if(!pError)
            if (pProcessFunc && typeof pProcessFunc==="function")
                    pProcessFunc(pFileName,pData.toString(), pLastFile_b);
        else console.log(pError);
    };
}

/*
 * Функция вызываеться после записи файла
 * и выводит ошибку или сообщает,
 * что файл успешно записан
 */
function fileWrited(pFileName){
    "use strict";
    return function(error){
        console.log(error?error:('file '+pFileName+' writed...'));
    };
}

/*
 * Function reads hash table of files
 * checks is file changed or not
 * and return result.
 * @pFileName - name of file
 * @pFileData - data of file
 * result: boolean
 */
function isFileChanged(pFileName, pFileData, pLastFile_b){
        var lReadedHash;

        if(!Hashes)
            try {
                /* try to read file with hashes */
                Hashes = require('hashes.json');
                for(var lFileName in Hashes)
                    /* if founded row with file name
                     * saving hash
                     */
                    if (lFileName === pFileName) {
                        lReadedHash = Hashes[pFileName];
                        break;
                    }
            }catch(pError) {
                Hashes={};
            }
        /* create md5 hash of file data */
        var lFileHash = crypto.createHash('sha1');
        lFileHash.update(pFileData);
        lFileHash = lFileHash.digest('hex');

        console.log(pFileName + ': ' + lFileHash);

        if(lReadedHash &&
            lReadedHash === lFileHash){
                /* file did not change */
                return false;
        }else{
            Hashes[pFileName] = lFileHash;
            if(pLastFile_b)
                fs.writeFile('hashes.json', JSON.stringify(Hashes), fileWrited(pLastFile_b));

            return true;
        }
}<commit_msg>removed hash viewing<commit_after>/* Модуль сжатия js-скриптов, css-стилей, html-файлов и
 * конвертации картинок в css-стилях
 * в base64 и помещения их в файл стилей
 */
var Minify = {};
console.log('minify.js loaded...');

/* функция сжимает js-скрипты
 * и сохраняет их с именем .min.js
 */

var fs = require('fs');
var path=require('path');
var crypto = require('crypto');

var Hashes;

var MinFolder='min/';
/* function clear MinFolder
 * if we could not create
 * directory and it is
 * not exist
 */
var folderExist = function(pError, pStat){
    "use strict";
    /*file found and it's directory */
    if(!pError && pStat.isDirectory())
        console.log('folder exist: ' + MinFolder);
    else MinFolder='/';
};

/*
 * function says thet folder created
 * if everything is OK, or
 * moves to folderExist function
 */
var makeFolder = function(pError){
    "use strict";
    /*folder created successfully*/
    if(!pError)
        console.log('folder created: min');
    else fs.stat(MinFolder,folderExist);
};

/* Trying to create folder min
 * where woud be minifyed versions
 * of files 511(10)=777(8)
 * rwxrwxrwx
 */
fs.mkdir(MinFolder,511,makeFolder);

exports.MinFolder = MinFolder;
exports.Cache    = {};

/*********************************/
/* сжимаем код через uglify-js */
Minify._uglifyJS= function(pDdata){

    /* подключаем модуль uglify-js
     * если его нет - дальнейшая
     * работа функции не имеет смысла
     */
    var jsp;
    var pro;
    try{
        jsp = require("uglify-js").parser;
        pro = require("uglify-js").uglify;
    }catch(error){
        console.log('can\'n load uglify-js\n'                  +
            'to use js-minification you need to install uglify-js\n'    +
                'npm install uglify-js\n'                               +
                'https://github.com/mishoo/UglifyJS');
        return false;
    }

    var orig_code = pDdata.toString();
    var ast = jsp.parse(orig_code); // parse code and get the initial AST
    ast = pro.ast_mangle(ast); // get a new AST with mangled names
    ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
    var result_code = pro.gen_code(ast); // compressed code here
    return result_code;
};

/* сжимаем код через clean-css */
Minify._cleanCSS=function(pData){
    'use strict';
     /* connecting cleanCSS,
      * if we can't find it -
      * return false
      */
     var cleanCSS;
     try{
        cleanCSS = require('clean-css');
    }catch(error){
        console.log('can\'n load clean-css \n'                          +
            'to use css-minification you need to install clean-css \n'  +
                'npm install clean-css\n'                               +
                'https://github.com/GoalSmashers/clean-css');
        return false;
    }
        /* Сохраняем весь стиль в одну переменную*/
    return cleanCSS.process(pData);
};

/* сжимаем код через htmlMinify */
Minify.htmlMinify=function(pData){
    /* Сохраняем весь стиль в одну переменную*/

     /* connecting cleanCSS,
      * if we can't find it -
      * return false
      */
     var htmlMinifier;
     try{
        htmlMinifier = require('html-minifier');
    }catch(error){
        console.log('can\'n load html-minifier \n'                 +
            'to use html-minification you need to install html-minifier\n'  +
                'npm install html-minifier\n'                               +
                'https://github.com/kangax/html-minifier');
        return false;
    }

    var lOptions={
        removeComments:                 true,
        removeCommentsFromCDATA:        true,
        removeCDATASectionsFromCDATA:   true,
        collapseWhitespace:             true,
        collapseBooleanAttributes:      true,
        removeAttributeQuotes:          true,
        removeRedundantAttributes:      true,
        useShortDoctype:                true,
        removeEmptyAttributes:          true,
        /* оставляем, поскольку у нас
         * в элемент fm генерируеться
         * таблица файлов
         */
        removeEmptyElements:            false,
        removeOptionalTags:             true,
        removeScriptTypeAttributes:     true,
        removeStyleLinkTypeAttributes:  true
    };

    return htmlMinifier.minify(pData,lOptions);
};
/*********************************/

/*
 * Функция ищет в имени файла расширение
 * и если находит возвращает true
 * @pName - получает имя файла
 * @pExt - расширение
 */
Minify._checkExtension=function(pName,pExt)
{
    /* если длина имени больше
     * длинны расширения -
     * имеет смысл продолжать
     */
    if(pName.length>pExt.length){
        var lLength=pName.length;           /* длина имени*/
        var lExtNum=pName.lastIndexOf(pExt);/* последнее вхождение расширения*/
        var lExtSub=lLength-lExtNum;        /* длина расширения*/
        /* если pExt - расширение pName */
        if(lExtSub===pExt.length)
            return true;
        else
            return false;
    }
    else return false;
}

/* function minificate js,css and html files
 * @pFiles_a                - array of js, css and html file names or string, if name
 *                            single, or object if postProcessing neaded
 *                              {'client.js': function(pFinalCode){} }
 *                            or convertion images to base64 neaded
 *                              {'style.css': true}
 *                            or {'style.css':{minimize: true, func: function(){}}
 * @pCache_b                - if true files do not writes on disk, just saves
 *                              in Minify Cache
 */
exports.optimize = function(pFiles_a, pCache_b){
    'use strict';

     /* if passed string, or object
     * putting it to array
     */
    if (typeof pFiles_a === 'string' ||
        !pFiles_a[0])
            pFiles_a=[pFiles_a];

    var lName;

    var lAllCSS;
    var dataReaded_f=function(pFileName, pData, pLastFile_b){
        /*
         * if postProcessing function exist
         * getting it from pFileName object
         */
        var lMoreProcessing_f;
        if(typeof pFileName === 'object'){
            var lName;
            for(lName in pFileName){
                break;
            }
            lMoreProcessing_f = pFileName[lName];
            pFileName = lName;
        }
        console.log('file ' + pFileName + ' readed');

        var final_code;
        var minFileName;

        /* if it's js file - getting optimized version */
        if(Minify._checkExtension(pFileName,'js')){

            final_code=Minify._uglifyJS(pData);
            minFileName=pFileName.replace('.js', '.min.js');

        } else if (Minify._checkExtension(pFileName, 'html')) {

            final_code=Minify.htmlMinify(pData);
            minFileName=pFileName.replace('.html', '.min.html');

        } else if (Minify._checkExtension(pFileName, 'css')) {

            final_code=Minify._cleanCSS(pData);
            lAllCSS+=final_code;
            minFileName=pFileName.replace('.css','.min.css');

            /* in css could be lCSS_o
             * {img: true, moreProcessing: function(){}}
             */
            var lCSS_o = lMoreProcessing_f;
            if (typeof lCSS_o === 'object'){
                    lMoreProcessing_f = lCSS_o.moreProcessing;
            }
            /* if it's last file
             * and base64images setted up
             * se should convert it
             */
            if (pLastFile_b && (lCSS_o && lCSS_o.img) ||
                (lCSS_o === true)){
                    base64_images(lAllCSS);
            }
        } else
            return;

         if (!isFileChanged(pFileName, pData, pLastFile_b)) {
            console.log('file: ' + pFileName + ' do not changed...');
            return;
        }

        /* if lMoreProcessing_f seeted up
         * and function associated with
         * current file name exists -
         * run it
         */
        if(lMoreProcessing_f                    &&
            typeof lMoreProcessing_f === "function"){
                final_code = lMoreProcessing_f(final_code);
        }

        minFileName = path.basename(minFileName);

        /* записываем сжатый js-скрипт
         * в кэш если установлен pCache_b
         * или на диск, если не установлен
         */
        if(pCache_b){
            exports.Cache[minFileName] = final_code;
            console.log('file ' + minFileName + ' saved to cache...');
        }
        else{
            /* minimized file will be in min file
             * if it's possible if not -
             * in root
             */
            minFileName = MinFolder + minFileName;

            fs.writeFile(minFileName, final_code, fileWrited(minFileName));
        }
    };
    /* moving thru all elements of js files array */
    for(var i=0; pFiles_a[i]; i++){
        /* if postProcessing function exist
         * getting file name and passet next
         */
        var lPostProcessing_o = pFiles_a[i];
        if(typeof lPostProcessing_o === 'object'){
            for(lName in lPostProcessing_o){
            }
        } else lName = pFiles_a[i];
        console.log('reading file ' + lName + '...');

        /* if it's last file send true */
        fs.readFile(lName,
            fileReaded(pFiles_a[i],
                dataReaded_f,
                (i === pFiles_a.length-1)?true:false));
    }
    /* saving the name of last readed file for hash saving function */

    return true;
};

/* функция переводит картинки в base64 и записывает в css-файл*/
function base64_images(pFileContent_s){
    'use strict';
     var b64img;
     try{
        b64img = require('css-b64-images');
    }catch(error){
        console.log('can\'n load clean-css \n'                 +
            'to use images to base64 convertation you need to install css-base64-images \n'  +
                'npm install -g css-b64-images\n'                               +
                'https://github.com/Filirom1/css-base64-images');
        return false;
    }
    b64img.fromString(pFileContent_s, '.','', function(err, css){
        console.log('images converted to base64 and saved in css file');
        fs.writeFile(MinFolder + 'all.min.css', css, fileWrited(MinFolder + 'all.min.css'));
    });
}

/* Функция создаёт асинхроную версию
 * для чтения файла
 * @pFileName       - имя считываемого файла
 * @pProcessFunc    - функция обработки файла
 * @pLastFile_b     - последний файл?
 */
function fileReaded(pFileName,pProcessFunc, pLastFile_b){
    "use strict";
    return function(pError,pData){
        /* функция в которую мы попадаем,
         * если данные считались
         *
         * если ошибка - показываем её
         * иначе если переданная функция -
         * функция запускаем её
         */
        if(!pError)
            if (pProcessFunc && typeof pProcessFunc==="function")
                    pProcessFunc(pFileName,pData.toString(), pLastFile_b);
        else console.log(pError);
    };
}

/*
 * Функция вызываеться после записи файла
 * и выводит ошибку или сообщает,
 * что файл успешно записан
 */
function fileWrited(pFileName){
    "use strict";
    return function(error){
        console.log(error?error:('file '+pFileName+' writed...'));
    };
}

/*
 * Function reads hash table of files
 * checks is file changed or not
 * and return result.
 * @pFileName - name of file
 * @pFileData - data of file
 * result: boolean
 */
function isFileChanged(pFileName, pFileData, pLastFile_b){
        var lReadedHash;

        if(!Hashes)
            try {
                /* try to read file with hashes */
                Hashes = require('hashes.json');
                for(var lFileName in Hashes)
                    /* if founded row with file name
                     * saving hash
                     */
                    if (lFileName === pFileName) {
                        lReadedHash = Hashes[pFileName];
                        break;
                    }
            }catch(pError) {
                Hashes={};
            }
        /* create md5 hash of file data */
        var lFileHash = crypto.createHash('sha1');
        lFileHash.update(pFileData);
        lFileHash = lFileHash.digest('hex');

        if(lReadedHash &&
            lReadedHash === lFileHash){
                /* file did not change */
                return false;
        }else{
            Hashes[pFileName] = lFileHash;
            if(pLastFile_b)
                fs.writeFile('hashes.json', JSON.stringify(Hashes), fileWrited(pLastFile_b));

            return true;
        }
}<|endoftext|>


<commit_before>/* Модуль сжатия js-скриптов, css-стилей, html-файлов и
 * конвертации картинок в css-стилях
 * в base64 и помещения их в файл стилей
 */
(function(){
    'use strict';

    var DIR         = __dirname +'/',
        LIBDIR      = DIR + 'lib/',
        main        = require(LIBDIR + 'main'),

        crypto      = main.crypto,
        fs          = main.fs,
        path        = main.path,
        Util        = main.util,

        /* object contains hashes of files*/
        HASHESNAME  = DIR       + 'hashes',
        HASHES_JSON = HASHESNAME   + '.json',

        Hashes      = main.require(HASHESNAME) || [],
        HashesChanged;

    var MinFolder   = DIR + 'min/';

    function makeFolder(pExist){
        /* Trying to create folder min
         * where woud be minifyed versions
         * of files 511(10)=777(8)
         * rwxrwxrwx
         */
        if(!pExist)
            fs.mkdir(MinFolder, 511, function(pError){
                if(pError){
                    Util.log(pError);
                    MinFolder = '/';
                }
            });
    }

    fs.exists(MinFolder, makeFolder);

    /**
     * function minificate js,css and html files
     * @param pFiles_a  -   array of js, css and html file names or string, if name
     *                      single, or object if postProcessing neaded
     *                          {'client.js': function(pFinalCode){} }
     *                      or convertion images to base64 neaded
     *                          {'style.css': true}
     *                       or {'style.css':{minimize: true, func: function(){}}
     *
     * @param pOptions  -   object contain main options
     *
     * Example:
     * {callback: func(pData){}}
     */
    function optimize(pFileNames, pOptions){
        if ( !Util.isArray(pFileNames) )
            pFileNames = [pFileNames];

        var lAllCSS     = '',
            /* varible contains all readed files count */
            lReadedFilesCount = 0,

            /**
             * Processing of files
             * @param pFileData_o {name, data}
             */
            dataReaded_f = function(pFileData_o){
                ++lReadedFilesCount;

                var lFileName   = pFileData_o.name,
                    lData       = pFileData_o.data,
                    lIsLastFile = lReadedFilesCount === pFileNames.length;

                /*
                 * if postProcessing function exist
                 * getting it from lFileName object
                 */
                var lOptimizeParams;
                if( Util.isObject(lFileName) ){
                    for(var lName in lFileName){
                        break;
                    }
                    lOptimizeParams = lFileName[lName];
                    lFileName = lName;
                }
                Util.log('minify: file ' + path.basename(lFileName) + ' readed');

                var lExt            = Util.getExtension(lFileName),
                    lMinFileName    = getName(lFileName, lExt);

                var lProcessing_f   = function(pData){
                    pData = main.optimize({
                        ext : lExt,
                        data: pData
                    });

                    if(lExt === '.css')
                        lAllCSS    += pData;

                    if (lIsLastFile)
                        saveAllCSS(lOptimizeParams, lAllCSS);

                    var lWritedCallBack =  function(pParams){
                        var lRet    = Util.checkObj(pParams, 'data'),
                            p       = pParams;

                        if(lRet && pOptions){
                            if(pOptions.returnName)
                                Util.exec(pOptions.callback, {
                                    name: lMinFileName
                                });
                            else
                                Util.exec(pOptions.callback, p.data);
                        }
                    };

                    writeFile(lMinFileName, pData, Util.call(lWritedCallBack, {
                        data : pData
                    }) );

                if((pOptions && pOptions.force) || isFileChanged(lFileName, lData, lIsLastFile))
                    lProcessing_f(lData);

                /* if file was not changed */
                else
                    fs.readFile(lMinFileName, function(pError, pFinalCode){
                        /* if could not read file call forse minification */
                        if(pError)
                            lProcessing_f(lData);

                        else {
                            writeFile(lMinFileName, pData, Util.call(lWritedCallBack, {
                                data : pFinalCode
                            }) );

                            if(lExt === '.css')
                                lAllCSS += pFinalCode;
                        }

                         if ( lIsLastFile )
                            saveAllCSS( lOptimizeParams, lAllCSS );
                    });
            };

        };

        for(var i=0; pFileNames[i]; i++){
            /* if postProcessing function exist
             * getting file name and passet next
             */
            var lPostProcessing_o = pFileNames[i], lName;
            if( Util.isObject(lPostProcessing_o) )
                for(lName in lPostProcessing_o)
                    break;
            else
                lName = pFileNames[i];

            Util.log('minify: reading file ' + path.basename(lName) + '...');

            fs.readFile(lName, Util.call(fileReaded, {
                name    : pFileNames[i],
                callback: dataReaded_f
            }));
        }
    }

    /**
     * function get name of file in min folder
     * @param pName
     */
    function getName(pName, pExt){
        var lRet;

        if( Util.isString(pName) ){

            var lExt        = pExt || Util.getExtension(pName),
                lMinFileName = crypto.createHash('sha1')
                    .update(pName)
                    .digest('hex') + lExt;

            lRet = MinFolder + lMinFileName;
        }

        return lRet;
    }

    /**
     * Функция переводит картинки в base64 и записывает в css-файл
     * @param pData {String}
     */
    function base64_images(pData){
        var lPath  = MinFolder + 'all.min.css',
            b64img = main.require('css-b64-images');

        if(!b64img){
            Util.log('can\'n load clean-css \n'                 +
                    'npm install -g css-b64-images\n'           +
                    'https://github.com/Filirom1/css-base64-images');

            writeFile(lPath, pData);

            return pData;
        }
        else
            b64img.fromString(pData, '.', '', function(pError, pCSS){
                Util.log('minify: images converted to base64 and saved in css file');
                writeFile(lPath, pCSS);
            });
    }

    /** Функция создаёт асинхроную версию
     * для чтения файла
     * @pFileName       - имя считываемого файла
     * @pProcessFunc    - функция обработки файла
     */
    function fileReaded(pParams){
        var lRet =  Util.checkObj(pParams, ['error', 'data']) &&
                    Util.checkObjTrue(pParams.params, ['name', 'callback']);

        if(lRet){
            var p = pParams,
                d = p.params;

            Util.log(p.error);
            p.data = p.data.toString();

            Util.exec(d.callback, {
                name: d.name,
                data: p.data
            });
        }
    }

    /**
     * Функция записывает файла
     * и выводит ошибку или сообщает,
     * что файл успешно записан
     */
    function writeFile(pName, pData, pCallBack){
        fs.writeFile(pName, pData, function(pError){
            if(pError)
                Util.log(pError);
            else{
                pName = path.basename(pName);
                Util.log('minify: file ' + pName + ' writed...');
            }

            Util.exec(pCallBack);
        });
    }

    function saveAllCSS(pParams, pData){
        if(pParams && pParams.merge){
            if(pParams.img)
                base64_images(pData);
            else{
                var lPath = MinFolder + 'all.min.css';
                writeFile(lPath, pData);
            }
        }
    }

    function isFileChanged(pFileName, pFileData, pLastFile_b){
        var lReadedHash,
            i, n = Hashes.length;

        for(i = 0; i < n; i++){
            var lData = Hashes[i];

            /* if founded row with file name - save hash */
            if(lData.name === pFileName){
                lReadedHash = lData.hash;
                break;
            }
        }

        /* create hash of file data */
        var lFileHash   = crypto.createHash('sha1')
            .update(pFileData)
            .digest('hex');

        /* boolean hashes.json changed or not */
        if(lReadedHash !== lFileHash){
            Hashes[i]       = {
                name: pFileName,
                hash: lFileHash
            };

            HashesChanged   = true;
        }

        if(pLastFile_b){
            /* if hashes file was changes - write it */
            if(HashesChanged)
                writeFile(HASHES_JSON, Util.stringifyJSON(Hashes));
            else
                Util.log('minify: no one file has been changed');
        }
        /* has file changed? */
        return lFileHash;
    }

    exports.getName     = getName;
    exports.optimize    = optimize;
    exports.MinFolder   = MinFolder;

})();
<commit_msg>refactored<commit_after>/* Модуль сжатия js-скриптов, css-стилей, html-файлов и
 * конвертации картинок в css-стилях
 * в base64 и помещения их в файл стилей
 */
(function(){
    'use strict';

    var DIR         = __dirname +'/',
        LIBDIR      = DIR + 'lib/',
        main        = require(LIBDIR + 'main'),

        crypto      = main.crypto,
        fs          = main.fs,
        path        = main.path,
        Util        = main.util,

        /* object contains hashes of files*/
        HASHESNAME  = DIR       + 'hashes',
        HASHES_JSON = HASHESNAME   + '.json',

        Hashes      = main.require(HASHESNAME) || [],
        HashesChanged;

    var MinFolder   = DIR + 'min/';

    function makeFolder(pExist){
        /* Trying to create folder min
         * where woud be minifyed versions
         * of files 511(10)=777(8)
         * rwxrwxrwx
         */
        if(!pExist)
            fs.mkdir(MinFolder, 511, function(pError){
                if(pError){
                    Util.log(pError);
                    MinFolder = '/';
                }
            });
    }

    fs.exists(MinFolder, makeFolder);

    /**
     * function minificate js,css and html files
     * @param pFiles_a  -   array of js, css and html file names or string, if name
     *                      single, or object if postProcessing neaded
     *                          {'client.js': function(pFinalCode){} }
     *                      or convertion images to base64 neaded
     *                          {'style.css': true}
     *                       or {'style.css':{minimize: true, func: function(){}}
     *
     * @param pOptions  -   object contain main options
     *
     * Example:
     * {callback: func(pData){}}
     */
    function optimize(pFiles, pOptions){
        var lFiles = Util.isArray(pFiles) ? pFiles : [pFiles],

            lName       = '',
            lAllCSS     = '',
            /* varible contains all readed files count */
            lReadedFilesCount = 0,

            /**
             * Processing of files
             * @param pFileData_o {name, data}
             */
            dataReaded_f = function(pFileData_o){
                ++lReadedFilesCount;

                var lFileName   = pFileData_o.name,
                    lData       = pFileData_o.data,
                    lIsLastFile = lReadedFilesCount === lFiles.length;

                /*
                 * if postProcessing function exist
                 * getting it from lFileName object
                 */
                var lOptimizeParams;
                if( Util.isObject(lFileName) ){
                    var lName;
                    for(lName in lFileName){
                        break;
                    }

                    lOptimizeParams = lFileName[lName];
                    lFileName       = lName;
                }
                Util.log('minify: file ' + path.basename(lFileName) + ' readed');

                var lExt            = Util.getExtension(lFileName),
                    lMinFileName    = getName(lFileName, lExt);

                var lWritedCallBack =  function(pData){
                    if(pOptions){
                        if(pOptions.returnName)
                             Util.exec(pOptions.callback, {
                                 name: lMinFileName
                             });
                         else
                            Util.exec(pOptions.callback, pData);
                    }
                };

                var lProcessing_f = function(pData){
                    pData = main.optimize({
                        ext : lExt,
                        data: pData
                    });

                    if(lExt === '.css')
                        lAllCSS    += pData;

                    if (lIsLastFile)
                        saveAllCSS(lOptimizeParams, lAllCSS);

                    writeFile(lMinFileName, pData, Util.retExec(lWritedCallBack, pData) );

                };

                if((pOptions && pOptions.force) || isFileChanged(lFileName, lData, lIsLastFile))
                    lProcessing_f(lData);

                /* if file was not changed */
                else
                    fs.readFile(lMinFileName, function(pError, pFinalCode){
                        /* if could not read file call forse minification */
                        if(pError)
                            lProcessing_f(lData);

                        else {
                           writeFile(lMinFileName, pFinalCode, Util.retExec(lWritedCallBack, pFinalCode) );

                            if(lExt === '.css')
                                lAllCSS += pFinalCode;
                        }

                         if(lIsLastFile)
                            saveAllCSS(lOptimizeParams, lAllCSS);
                    });
            };

        /* moving thru all elements of js files array */
        for(var i=0; lFiles[i]; i++){
            /* if postProcessing function exist
             * getting file name and passet next
             */
            var lPostProcessing_o = lFiles[i];
            if( Util.isObject(lPostProcessing_o) )
                for(lName in lPostProcessing_o)
                    break;
            else
                lName = lFiles[i];

            Util.log('minify: reading file ' + path.basename(lName) + '...');

            /* if it's last file send true */
             fs.readFile(lName, Util.call(fileReaded, {
                name    : lFiles[i],
                callback: dataReaded_f
            }));
        }
    }

    /**
     * function get name of file in min folder
     * @param pName
     */
    function getName(pName, pExt){
        var lRet;

        if( Util.isString(pName) ){

            var lExt        = pExt || Util.getExtension(pName),
                lMinFileName = crypto.createHash('sha1')
                    .update(pName)
                    .digest('hex') + lExt;

            lRet = MinFolder + lMinFileName;
        }

        return lRet;
    }

    /**
     * Функция переводит картинки в base64 и записывает в css-файл
     * @param pData {String}
     */
    function base64_images(pData){
        var lPath  = MinFolder + 'all.min.css',
            b64img = main.require('css-b64-images');

        if(!b64img){
            Util.log('can\'n load clean-css \n'                 +
                    'npm install -g css-b64-images\n'           +
                    'https://github.com/Filirom1/css-base64-images');

            writeFile(lPath, pData);

            return pData;
        }
        else
            b64img.fromString(pData, '.', '', function(pError, pCSS){
                Util.log('minify: images converted to base64 and saved in css file');
                writeFile(lPath, pCSS);
            });
    }

    /* Функция создаёт асинхроную версию
     * для чтения файла
     * @pFileName       - имя считываемого файла
     * @pProcessFunc    - функция обработки файла
     */
    function fileReaded(pParams){
        var lRet =  Util.checkObj(pParams, ['error', 'data']) &&
                    Util.checkObjTrue(pParams.params, ['name', 'callback']);

        if(lRet){
            var p = pParams,
                d = p.params;

            Util.log(p.error);
            p.data = p.data.toString();

            Util.exec(d.callback, {
                name: d.name,
                data: p.data
            });
        }
    }

    /*
     * Функция записывает файла
     * и выводит ошибку или сообщает,
     * что файл успешно записан
     */
    function writeFile(pName, pData, pCallBack){
        fs.writeFile(pName, pData, function(pError){
            if(pError)
                Util.log(pError);
            else{
                pName = path.basename(pName);
                Util.log('minify: file ' + pName + ' writed...');
            }

            Util.exec(pCallBack);
        });
    }

    function saveAllCSS(pParams, pData){
       if(pParams && pParams.merge){
           if(pParams.img)
               base64_images(pData);
           else{
               var lPath = MinFolder + 'all.min.css';
               writeFile(lPath, pData);
           }
        }
    }

    function isFileChanged(pFileName, pFileData, pLastFile_b){
        var lReadedHash,
            i, n = Hashes.length;

        for(i = 0; i < n; i++){
            var lData = Hashes[i];

            /* if founded row with file name - save hash */
            if(lData.name === pFileName){
                lReadedHash = lData.hash;
                break;
            }
        }

        /* create hash of file data */
        var lFileHash   = crypto.createHash('sha1')
            .update(pFileData)
            .digest('hex');

        /* boolean hashes.json changed or not */
        if(lReadedHash !== lFileHash){
            Hashes[i]       = {
                name: pFileName,
                hash: lFileHash
            };

            HashesChanged   = true;
        }

        if(pLastFile_b){
            /* if hashes file was changes - write it */
            if(HashesChanged)
                writeFile(HASHES_JSON, Util.stringifyJSON(Hashes));
            else
                Util.log('minify: no one file has been changed');
        }
        /* has file changed? */
        return lFileHash;
    }

    exports.getName     = getName;
    exports.optimize    = optimize;
    exports.MinFolder   = MinFolder;

})();<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var crypto = require('crypto');
var onHeaders = require('on-headers');

var uglifyjs = require('uglify-js');
var cssmin = require('cssmin');
var sass;
var lessParser;
var stylus;
var coffee;

var memCache = {};

var TYPE_TEXT = 0;
var TYPE_JS = 1;
var TYPE_CSS = 2;
var TYPE_SASS = 3;
var TYPE_LESS = 4;
var TYPE_STYLUS = 5;
var TYPE_COFFEE = 6;

function precompileError(err, type) {
    return JSON.stringify(err);
}

function minifyIt(type, options, content, callback) {
    if (typeof callback != 'function') {
        return;
    }

    switch(type) {
        case TYPE_JS:
            var opt = {fromString: true};
            if (options.no_mangle) {
                opt.mangle = false;
            }
            var result = content;
            try {
                result = uglifyjs.minify(content, opt).code
            } catch(err) {
            }
            callback(result);
            break;
        case TYPE_CSS:
            var result = content;
            try {
                result = cssmin(content)
            } catch(err) {
            }
            callback(result);
            break;
        case TYPE_SASS:
            if (!sass) {
                sass = require('node-sass');
            }
            var result;
            try {
                result = sass.renderSync(content);
                try {
                    result = cssmin(result);
                } catch(err) {
                }
            } catch(err) {
                result = precompileError(err, type);
            }
            callback(result);
            break;
        case TYPE_LESS:
            if (!lessParser) {
                var less = require('less');
                lessParser = new less.Parser();
            }
            lessParser.parse(content, function(err, tree) {
                if (err) {
                    callback(precompileError(err, type));
                    return;
                }
                var result = tree.toCSS();
                try {
                    result = cssmin(result);
                } catch(err) {
                }
                callback(result);
            });
            break;
        case TYPE_STYLUS:
            if (!stylus) {
                stylus = require('stylus');
            }
            stylus.render(content, function(err, css) {
                if (err) {
                    callback(precompileError(err, type));
                    return;
                }
                var result = css;
                try {
                    result = cssmin(result);
                } catch(err) {
                }
                callback(result);
            });
            break;
        case TYPE_COFFEE:
            if (!coffee) {
                coffee = require('coffee-script');
            }
            var result;
            try {
                result = coffee.compile(content);
                var opt = {fromString: true};
                if (options.no_mangle) {
                    opt.mangle = false;
                }
                try {
                    result = uglifyjs.minify(result, opt).code
                } catch(err) {
                }
            } catch(err) {
                result = precompileError(err, type);
            }
            callback(result);
            break;
        default:
            callback(content);
            break;
    }
}

function cacheGetFile(hash, callback) {
    if (typeof callback != 'function') {
        return;
    }

    var filepath = this.toString();

    fs.readFile(filepath + hash, { encoding: 'utf8' }, function(err, data) {
        if (err) {
            callback(err);
            return;
        }
        try {
            data = JSON.parse(data).content;
            callback(null, data);
        } catch(err) {
            callback(err);
        }
    });
}

function cachePutFile(hash, minized, callback) {
    var filepath = this.toString();

    // fix issue #3
    // not ended file writing will cause wrong responding.
    // using temp files can mostly avoid the case.

    fs.writeFile(filepath + hash + '.tmp', JSON.stringify({content:minized}), { encoding: 'utf8' }, function(err) {
        if (err) {
            return callback(err);
        }
        fs.rename(filepath + hash + '.tmp', filepath + hash, callback);
    });
}

function cacheGetMem(hash, callback) {
    if (typeof callback != 'function') {
        return;
    }

    if (typeof memCache[hash] == 'undefined') {
        callback(new Error('miss'));
    } else {
        callback(null, memCache[hash]);
    }
}

function cachePutMem(hash, minized, callback) {
    memCache[hash] = minized;

    if (typeof callback == 'function') {
        callback(null);
    }
}

module.exports = function express_minify(options) {
    options = options || {};

    var js_match = options.js_match || /javascript/;
    var css_match = options.css_match || /css/;
    var sass_match = options.sass_match || /scss/;
    var less_match = options.less_match || /less/;
    var stylus_match = options.stylus_match || /stylus/;
    var coffee_match = options.coffee_match || /coffeescript/;
    var cache = options.cache || false;

    var cache_get = cacheGetMem;
    var cache_put = cachePutMem;

    if (cache) {
        cache = path.normalize(cache + '/').toString();

        fs.writeFile(cache + 'test.tmp', new Date().getTime().toString(), function(err) {
            if (err) {
                console.log('WARNING: express-minify cache directory is not valid or is not writeable.');
                return;
            }

            //Consider deleting the test file?

            //OK: rewrite functions
            cache_get = function() {
                return cacheGetFile.apply(cache, arguments);
            };
            cache_put = function() {
                return cachePutFile.apply(cache, arguments);
            };
        });
    }

    return function minify(req, res, next) {
        var _storeHeader = res._storeHeader;

        var write = res.write;
        var end = res.end;

        var buf = null;
        var type = TYPE_TEXT;

        onHeaders(res, function() {
            if (req.method === 'HEAD') {
                return;
            }

            if (res._no_minify) {
                return;
            }

            var contentType = res.getHeader('Content-Type');
            if (contentType === undefined) {
                return;
            }

            if (sass_match.test(contentType)) {
                type = TYPE_SASS;
                res.setHeader('Content-Type', 'text/css');
            } else if (less_match.test(contentType)) {
                type = TYPE_LESS;
                res.setHeader('Content-Type', 'text/css');
            } else if (stylus_match.test(contentType)) {
                type = TYPE_STYLUS;
                res.setHeader('Content-Type', 'text/css');
            } else if (coffee_match.test(contentType)) {
                type = TYPE_COFFEE;
                res.setHeader('Content-Type', 'text/javascript');
            } else if (js_match.test(contentType)) {
                type = TYPE_JS;
            } else if (css_match.test(contentType)) {
                type = TYPE_CSS;
            }

            if (type === TYPE_TEXT) {
                return;
            }

            res.removeHeader('Content-Length');

            // prepare the buffer
            buf = [];
        });

        res.write = function(chunk, encoding) {
            if (!this._header) {
                this._implicitHeader();
            }

            if (buf === null || this._no_minify) {
                return write.call(this, chunk, encoding);
            }

            if (!this._hasBody) {
                return true;
            }

            if (typeof chunk !== 'string' && !Buffer.isBuffer(chunk)) {
                throw new TypeError('first argument must be a string or Buffer');
            }

            if (chunk.length === 0) return true;

            // no chunked_encoding here
            if (typeof chunk == 'string') {
                chunk = new Buffer(chunk, encoding);
            }

            buf.push(chunk);
        }

        res.end = function(data, encoding) {
            if (this.finished) {
                return false;
            }

            if (!this._header) {
                this._implicitHeader();
            }

            if (data && !this._hasBody) {
                data = false;
            }

            if (buf === null || this._no_minify) {
                return end.call(this, data, encoding);
            }

            // TODO: implement hot-path optimization
            if (data) {
                this.write(data, encoding);
            }

            var buffer = Buffer.concat(buf);

            var sha1 = crypto.createHash('sha1').update(buffer).digest('hex').toString();
            var _this = this;

            cache_get(sha1, function(err, minized) {
                if (err) {
                    switch(type) {
                        case TYPE_TEXT:
                            // impossible to reach here
                            throw new Error('[express-minify] impossible to reach here. Please report the bug.');
                            break;
                        default:
                            // cache miss
                            minifyIt(type, { no_mangle: _this._no_mangle }, buffer.toString(encoding), function(minized) {
                                if (_this._no_cache) {
                                    // do not save cache for this response
                                    write.call(_this, minized, 'utf8');
                                    end.call(_this);
                                } else {
                                    cache_put(sha1, minized, function() {
                                        write.call(_this, minized, 'utf8');
                                        end.call(_this);
                                    });
                                }
                            });
                            break;
                    }
                } else {
                    write.call(_this, minized, 'utf8');
                    end.call(_this);
                }
            });
        }

        next();
    }
};<commit_msg>lessParser -> less<commit_after>var fs = require('fs');
var path = require('path');
var crypto = require('crypto');
var onHeaders = require('on-headers');

var uglifyjs = require('uglify-js');
var cssmin = require('cssmin');
var sass;
var less;
var stylus;
var coffee;

var memCache = {};

var TYPE_TEXT = 0;
var TYPE_JS = 1;
var TYPE_CSS = 2;
var TYPE_SASS = 3;
var TYPE_LESS = 4;
var TYPE_STYLUS = 5;
var TYPE_COFFEE = 6;

function precompileError(err, type) {
    return JSON.stringify(err);
}

function minifyIt(type, options, content, callback) {
    if (typeof callback != 'function') {
        return;
    }

    switch(type) {
        case TYPE_JS:
            var opt = {fromString: true};
            if (options.no_mangle) {
                opt.mangle = false;
            }
            var result = content;
            try {
                result = uglifyjs.minify(content, opt).code
            } catch(err) {
            }
            callback(result);
            break;
        case TYPE_CSS:
            var result = content;
            try {
                result = cssmin(content)
            } catch(err) {
            }
            callback(result);
            break;
        case TYPE_SASS:
            if (!sass) {
                sass = require('node-sass');
            }
            var result;
            try {
                result = sass.renderSync(content);
                try {
                    result = cssmin(result);
                } catch(err) {
                }
            } catch(err) {
                result = precompileError(err, type);
            }
            callback(result);
            break;
        case TYPE_LESS:
            if (!less) {
                less = new require('less').Parser();
            }
            less.parse(content, function(err, tree) {
                if (err) {
                    callback(precompileError(err, type));
                    return;
                }
                var result = tree.toCSS();
                try {
                    result = cssmin(result);
                } catch(err) {
                }
                callback(result);
            });
            break;
        case TYPE_STYLUS:
            if (!stylus) {
                stylus = require('stylus');
            }
            stylus.render(content, function(err, css) {
                if (err) {
                    callback(precompileError(err, type));
                    return;
                }
                var result = css;
                try {
                    result = cssmin(result);
                } catch(err) {
                }
                callback(result);
            });
            break;
        case TYPE_COFFEE:
            if (!coffee) {
                coffee = require('coffee-script');
            }
            var result;
            try {
                result = coffee.compile(content);
                var opt = {fromString: true};
                if (options.no_mangle) {
                    opt.mangle = false;
                }
                try {
                    result = uglifyjs.minify(result, opt).code
                } catch(err) {
                }
            } catch(err) {
                result = precompileError(err, type);
            }
            callback(result);
            break;
        default:
            callback(content);
            break;
    }
}

function cacheGetFile(hash, callback) {
    if (typeof callback != 'function') {
        return;
    }

    var filepath = this.toString();

    fs.readFile(filepath + hash, { encoding: 'utf8' }, function(err, data) {
        if (err) {
            callback(err);
            return;
        }
        try {
            data = JSON.parse(data).content;
            callback(null, data);
        } catch(err) {
            callback(err);
        }
    });
}

function cachePutFile(hash, minized, callback) {
    var filepath = this.toString();

    // fix issue #3
    // not ended file writing will cause wrong responding.
    // using temp files can mostly avoid the case.

    fs.writeFile(filepath + hash + '.tmp', JSON.stringify({content:minized}), { encoding: 'utf8' }, function(err) {
        if (err) {
            return callback(err);
        }
        fs.rename(filepath + hash + '.tmp', filepath + hash, callback);
    });
}

function cacheGetMem(hash, callback) {
    if (typeof callback != 'function') {
        return;
    }

    if (typeof memCache[hash] == 'undefined') {
        callback(new Error('miss'));
    } else {
        callback(null, memCache[hash]);
    }
}

function cachePutMem(hash, minized, callback) {
    memCache[hash] = minized;

    if (typeof callback == 'function') {
        callback(null);
    }
}

module.exports = function express_minify(options) {
    options = options || {};

    var js_match = options.js_match || /javascript/;
    var css_match = options.css_match || /css/;
    var sass_match = options.sass_match || /scss/;
    var less_match = options.less_match || /less/;
    var stylus_match = options.stylus_match || /stylus/;
    var coffee_match = options.coffee_match || /coffeescript/;
    var cache = options.cache || false;

    var cache_get = cacheGetMem;
    var cache_put = cachePutMem;

    if (cache) {
        cache = path.normalize(cache + '/').toString();

        fs.writeFile(cache + 'test.tmp', new Date().getTime().toString(), function(err) {
            if (err) {
                console.log('WARNING: express-minify cache directory is not valid or is not writeable.');
                return;
            }

            //Consider deleting the test file?

            //OK: rewrite functions
            cache_get = function() {
                return cacheGetFile.apply(cache, arguments);
            };
            cache_put = function() {
                return cachePutFile.apply(cache, arguments);
            };
        });
    }

    return function minify(req, res, next) {
        var _storeHeader = res._storeHeader;

        var write = res.write;
        var end = res.end;

        var buf = null;
        var type = TYPE_TEXT;

        onHeaders(res, function() {
            if (req.method === 'HEAD') {
                return;
            }

            if (res._no_minify) {
                return;
            }

            var contentType = res.getHeader('Content-Type');
            if (contentType === undefined) {
                return;
            }

            if (sass_match.test(contentType)) {
                type = TYPE_SASS;
                res.setHeader('Content-Type', 'text/css');
            } else if (less_match.test(contentType)) {
                type = TYPE_LESS;
                res.setHeader('Content-Type', 'text/css');
            } else if (stylus_match.test(contentType)) {
                type = TYPE_STYLUS;
                res.setHeader('Content-Type', 'text/css');
            } else if (coffee_match.test(contentType)) {
                type = TYPE_COFFEE;
                res.setHeader('Content-Type', 'text/javascript');
            } else if (js_match.test(contentType)) {
                type = TYPE_JS;
            } else if (css_match.test(contentType)) {
                type = TYPE_CSS;
            }

            if (type === TYPE_TEXT) {
                return;
            }

            res.removeHeader('Content-Length');

            // prepare the buffer
            buf = [];
        });

        res.write = function(chunk, encoding) {
            if (!this._header) {
                this._implicitHeader();
            }

            if (buf === null || this._no_minify) {
                return write.call(this, chunk, encoding);
            }

            if (!this._hasBody) {
                return true;
            }

            if (typeof chunk !== 'string' && !Buffer.isBuffer(chunk)) {
                throw new TypeError('first argument must be a string or Buffer');
            }

            if (chunk.length === 0) return true;

            // no chunked_encoding here
            if (typeof chunk == 'string') {
                chunk = new Buffer(chunk, encoding);
            }

            buf.push(chunk);
        }

        res.end = function(data, encoding) {
            if (this.finished) {
                return false;
            }

            if (!this._header) {
                this._implicitHeader();
            }

            if (data && !this._hasBody) {
                data = false;
            }

            if (buf === null || this._no_minify) {
                return end.call(this, data, encoding);
            }

            // TODO: implement hot-path optimization
            if (data) {
                this.write(data, encoding);
            }

            var buffer = Buffer.concat(buf);

            var sha1 = crypto.createHash('sha1').update(buffer).digest('hex').toString();
            var _this = this;

            cache_get(sha1, function(err, minized) {
                if (err) {
                    switch(type) {
                        case TYPE_TEXT:
                            // impossible to reach here
                            throw new Error('[express-minify] impossible to reach here. Please report the bug.');
                            break;
                        default:
                            // cache miss
                            minifyIt(type, { no_mangle: _this._no_mangle }, buffer.toString(encoding), function(minized) {
                                if (_this._no_cache) {
                                    // do not save cache for this response
                                    write.call(_this, minized, 'utf8');
                                    end.call(_this);
                                } else {
                                    cache_put(sha1, minized, function() {
                                        write.call(_this, minized, 'utf8');
                                        end.call(_this);
                                    });
                                }
                            });
                            break;
                    }
                } else {
                    write.call(_this, minized, 'utf8');
                    end.call(_this);
                }
            });
        }

        next();
    }
};<|endoftext|>


<commit_before>// Build
// TODO data-uri example
// TODO CSS Sprite generation

'use strict';

// TODO #1 Sourcemaps for CSS: Waiting for CSSO to support gulp-sourcemaps
// TODO #2 Wait for bug fix in gulp-rev relating to destination of manifest
// TODO #3 Remove gulp-addsrc when contra fixed issue gulpjs/gulp/issues/840

var
  gulp = require( 'gulp' ),
  fewu = require( './setup' );

gulp.task( 'build-clean', function( cb ) {
  var del = require( 'del' );

  del( fewu.config( 'build.directory' ), cb );
} );

gulp.task( 'build-css', [ 'build-clean' ], function() {

  var
    less    = require( 'gulp-less' ),
    prefix  = require( 'gulp-autoprefixer' ),
    cssMin  = require( 'gulp-minify-css' ),
    concat  = require( 'gulp-concat' ),
    // TODO #2 rev     = require( 'gulp-rev' );

    lessPaths = [ process.cwd() + '/node_modules/' ];

  return gulp.src( fewu.files( 'css' ) )
    // TODO #1 .pipe( maps.init() )
    .pipe( less( { paths: lessPaths } ) )
    .on( 'error', function() {
      console.log( arguments );
    } )
    .pipe( prefix( fewu.config( 'build.cssAutoprefix' ) ) )
    .pipe( cssMin() )
    .pipe( concat( fewu.config( 'build.css' ) ) )
    // TODO #1 .pipe( maps.write() )
    // TODO #2 .pipe( rev() )
    .pipe( gulp.dest( fewu.config( 'build.directory' ) ) );
    // TODO #2 .pipe( rev.manifest( 'rev-manifest.json', { merge: true } ) )
    // TODO #2 .pipe( gulp.dest( gulp.config( 'build' ) ) );

} );

gulp.task( 'build-js', [ 'build-clean' ], function() {

  var
    header  = require( 'gulp-header' ),
    htmlMin = require( 'gulp-minify-html' ),
    gulpif  = require( 'gulp-if' ),
    ngTemplateCache = require( 'gulp-angular-templatecache' ),
    // TODO #3
    add     = require( 'gulp-add-src' ),
    maps    = require( 'gulp-sourcemaps' ),
    ngAnnotate = require( 'gulp-ng-annotate' ),
    uglify  = require( 'gulp-uglify' ),
    concat  = require( 'gulp-concat' ),
    // TODO #1 rev     = require( 'gulp-rev' ),

    libs = fewu.files( 'libraries' ),
    libFiles = Object.keys( libs ).map( function( libFile ) {
      var libFilePath = libs[ libFile ];

      return /\.js$/.test( libFilePath ) ?
        libFilePath.replace( /\.\//g, '' ) : false;
    } ).filter( function( x ) {
      return x;
    } );

  return gulp.src( [
      fewu.config( 'root' ) + '/**/*.{html,svg}',
      '!' + fewu.config( 'root' ) + '/index.html'
    ] )
    .pipe( htmlMin( { conditionals: true } ) )
    .pipe( gulpif( !!fewu.config( 'angular.module' ),
      ngTemplateCache( {
        module: fewu.config( 'angular.module' ),
        root: fewu.config( 'angular.root' )
      } )
    ) )
    // TODO #3 .pipe( gulp.src( libFiles.concat( gulp.files( 'browser' )  ) )
    .pipe( add( libFiles.concat( fewu.files( 'browser' ) ) ) )
    .pipe( maps.init() )
    .pipe( gulpif( !!fewu.config( 'angular.module' ), ngAnnotate() ) )
    .pipe( uglify( { } ) )
    .pipe( concat( fewu.config( 'build.js' ) ) )
    .pipe( header(
      fewu.config( 'build.header' ),
      {
        pkg: require( process.cwd() + '/package' ),
        date: new Date()
      }
    ) )
    // TODO #2 .pipe( rev() )
    .pipe( maps.write( '.' ) )
    .pipe( gulp.dest( fewu.config( 'build.directory' ) ) );
    // TODO #2 .pipe( rev.manifest( {
    // TODO #2   base: 'dist/',
    // TODO #2   merge: true
    // TODO #2 } ) )
    // TODO #2 .pipe( gulp.dest( 'dist/' ) );

} );

gulp.task( 'build-html', [ 'build-clean' ], function() {

  var
    htmlreplace = require( 'gulp-html-replace' ),
    htmlMin = require( 'gulp-minify-html' ),

    indexBuild = gulp.src( fewu.config( 'root' ) + '/index.html' )
      .pipe( htmlreplace( {
        js: fewu.config( 'build.js' ), css: fewu.config( 'build.css' )
      } ) )
      .pipe( htmlMin( { conditionals: true } ) )
      .pipe( gulp.dest( fewu.config( 'build.directory' ) ) );

  return indexBuild;

} );

gulp.task( 'copy-assets', [ 'build-clean' ], function() {
    var
      merge   = require( 'merge-stream' ),
      imageMin = require( 'gulp-imagemin' ),

      staticAssetPaths = fewu.config( 'build.static' ),
      staticAssets = Object.keys( staticAssetPaths ),
      assetStreams = staticAssets.map( function( pattern ) {

        return gulp.src( pattern )
          .pipe( imageMin( {
            optimizationLevel: 7,
            progressive: true,
            interlaced: true
          } ) )
          .pipe( gulp.dest(
            fewu.config( 'build.directory' ) + staticAssetPaths[ pattern ]
          ) );
      } );

    return merge.apply( null, assetStreams );

} );
<commit_msg>add support for css sourcemaps<commit_after>// Build
// TODO data-uri example
// TODO CSS Sprite generation

'use strict';

// TODO #2 Wait for bug fix in gulp-rev relating to destination of manifest
// TODO #3 Remove gulp-addsrc when contra fixed issue gulpjs/gulp/issues/840

var
  gulp = require( 'gulp' ),
  fewu = require( './setup' );

gulp.task( 'build-clean', function( cb ) {
  var del = require( 'del' );

  del( fewu.config( 'build.directory' ), cb );
} );

gulp.task( 'build-css', [ 'build-clean' ], function() {

  var
    less    = require( 'gulp-less' ),
    maps    = require( 'gulp-sourcemaps' ),
    prefix  = require( 'gulp-autoprefixer' ),
    cssMin  = require( 'gulp-minify-css' ),
    concat  = require( 'gulp-concat' ),
    // TODO #2 rev     = require( 'gulp-rev' );

    lessPaths = [ process.cwd() + '/node_modules/' ];

  return gulp.src( fewu.files( 'css' ) )
    .pipe( maps.init() )
    .pipe( less( { paths: lessPaths } ) )
    .on( 'error', function() {
      console.log( arguments );
    } )
    .pipe( prefix( fewu.config( 'build.cssAutoprefix' ) ) )
    .pipe( cssMin() )
    .pipe( concat( fewu.config( 'build.css' ) ) )
    .pipe( maps.write( '.' ) )
    // TODO #2 .pipe( rev() )
    .pipe( gulp.dest( fewu.config( 'build.directory' ) ) );
    // TODO #2 .pipe( rev.manifest( 'rev-manifest.json', { merge: true } ) )
    // TODO #2 .pipe( gulp.dest( gulp.config( 'build' ) ) );

} );

gulp.task( 'build-js', [ 'build-clean' ], function() {

  var
    header  = require( 'gulp-header' ),
    htmlMin = require( 'gulp-minify-html' ),
    gulpif  = require( 'gulp-if' ),
    ngTemplateCache = require( 'gulp-angular-templatecache' ),
    // TODO #3
    add     = require( 'gulp-add-src' ),
    maps    = require( 'gulp-sourcemaps' ),
    ngAnnotate = require( 'gulp-ng-annotate' ),
    uglify  = require( 'gulp-uglify' ),
    concat  = require( 'gulp-concat' ),
    // TODO #2 rev     = require( 'gulp-rev' ),

    libs = fewu.files( 'libraries' ),
    libFiles = Object.keys( libs ).map( function( libFile ) {
      var libFilePath = libs[ libFile ];

      return /\.js$/.test( libFilePath ) ?
        libFilePath.replace( /\.\//g, '' ) : false;
    } ).filter( function( x ) {
      return x;
    } );

  return gulp.src( [
      fewu.config( 'root' ) + '/**/*.{html,svg}',
      '!' + fewu.config( 'root' ) + '/index.html'
    ] )
    .pipe( htmlMin( { conditionals: true } ) )
    .pipe( gulpif( !!fewu.config( 'angular.module' ),
      ngTemplateCache( {
        module: fewu.config( 'angular.module' ),
        root: fewu.config( 'angular.root' )
      } )
    ) )
    // TODO #3 .pipe( gulp.src( libFiles.concat( gulp.files( 'browser' )  ) )
    .pipe( add( libFiles.concat( fewu.files( 'browser' ) ) ) )
    .pipe( maps.init() )
    .pipe( gulpif( !!fewu.config( 'angular.module' ), ngAnnotate() ) )
    .pipe( uglify( { } ) )
    .pipe( concat( fewu.config( 'build.js' ) ) )
    .pipe( header(
      fewu.config( 'build.header' ),
      {
        pkg: require( process.cwd() + '/package' ),
        date: new Date()
      }
    ) )
    // TODO #2 .pipe( rev() )
    .pipe( maps.write( '.' ) )
    .pipe( gulp.dest( fewu.config( 'build.directory' ) ) );
    // TODO #2 .pipe( rev.manifest( {
    // TODO #2   base: 'dist/',
    // TODO #2   merge: true
    // TODO #2 } ) )
    // TODO #2 .pipe( gulp.dest( 'dist/' ) );

} );

gulp.task( 'build-html', [ 'build-clean' ], function() {

  var
    htmlreplace = require( 'gulp-html-replace' ),
    htmlMin = require( 'gulp-minify-html' ),

    indexBuild = gulp.src( fewu.config( 'root' ) + '/index.html' )
      .pipe( htmlreplace( {
        js: fewu.config( 'build.js' ), css: fewu.config( 'build.css' )
      } ) )
      .pipe( htmlMin( { conditionals: true } ) )
      .pipe( gulp.dest( fewu.config( 'build.directory' ) ) );

  return indexBuild;

} );

gulp.task( 'copy-assets', [ 'build-clean' ], function() {
    var
      merge   = require( 'merge-stream' ),
      imageMin = require( 'gulp-imagemin' ),

      staticAssetPaths = fewu.config( 'build.static' ),
      staticAssets = Object.keys( staticAssetPaths ),
      assetStreams = staticAssets.map( function( pattern ) {

        return gulp.src( pattern )
          .pipe( imageMin( {
            optimizationLevel: 7,
            progressive: true,
            interlaced: true
          } ) )
          .pipe( gulp.dest(
            fewu.config( 'build.directory' ) + staticAssetPaths[ pattern ]
          ) );
      } );

    return merge.apply( null, assetStreams );

} );
<|endoftext|>