<commit_before>/*jslint devel: true*/
/*global perfis */

// variables
var project, beam, mk, concrete, steel;
var tsd, as, bw, fcd;
var betax23, betax34, epc, eps, epyd, fyd, Es, fck, fyk, fckForm, fykForm;
var d, h, cob, diamEstForm, diamLongForm, diamEst, diamLong;
var a, b, c, delta, deltaR, x1, x2;
var x, mk, md;
var x2lim, x3lim, dominio, dl;
var gamac, gamaf, gamas, s;
var situationD, situationLN;
var dlc, xd, m1d, m2d, tlsd, asl, as1, as2, ast, situationS;

var bitola = [
    {
        "diametro": 5.0,
        "area": 0.20
    },
    {
        "diametro": 6.3,
        "area": 0.315
    },
    {
        "diametro": 8.0,
        "area": 0.50
    },
    {
        "diametro": 10.0,
        "area": 0.80
    },
    {
        "diametro": 12.5,
        "area": 1.25
    },
    {
        "diametro": 16.0,
        "area": 2.0
    },
    {
        "diametro": 20.0,
        "area": 3.15
    },
    {
        "diametro": 25.0,
        "area": 5.0
    },
    {
        "diametro": 32.0,
        "area": 8.0
    },
    {
        "diametro": 40.0,
        "area": 12.50
    }

];

var steelProp = [
    {
        "steelType": "CA25",
        "fy": 250,
        "fu": 217.4,
        "E": 21000
    },
    {
        "steelType": "CA50",
        "fy": 500,
        "fu": 434.8,
        "E": 21000
    },
    {
        "steelType": "CA60",
        "fy": 600,
        "fu": 521.7,
        "E": 21000
    }
];

var concreteProp = [
    {
        "concType": "C20",
        "fckProp": 200
    },
    {
        "concType": "C25",
        "fckProp": 250
    },
    {
        "concType": "C30",
        "fckProp": 300
    },
    {
        "concType": "C35",
        "fckProp": 350
    },

    {
        "concType": "C40",
        "fckProp": 400
    },
    {
        "concType": "C45",
        "fckProp": 450
    },
    {
        "concType": "C50",
        "fckProp": 500
    }

];

function processFormDC() {
    "use strict";
    var i, j, k;

    // Getting values from HTML form inputs
    project = document.formDC.projectDC.value;
    /* if (project === "") {
        alert("Por favor preencha o campo Projeto.");
        console.log("Por favor preencha o campo Projeto.");
        return false;
    } */
    if (project === "") {
        project = "Exemplo 1";
    }

    beam = document.formDC.beamDC.value;
    /* if (beam === "") {
        alert("Por favor preencha o campo Viga.");
        console.log("Por favor preencha o campo Viga.");
        return false;
    } */
    if (beam === "") {
        beam = "V1";
    }

    h = Number(document.formDC.hDC.value);
    if (document.formDC.hDC.value === "" || isNaN(h)) {
        alert("Por favor preencha o campo Altura da Viga com números.");
        console.log("Por favor preencha o campo Altura da Viga.");
        return false;
    }

    bw = Number(document.formDC.bwDC.value);
    if (document.formDC.bwDC.value === "" || isNaN(bw)) {
        alert("Por favor preencha o campo Largura da Viga com números.");
        console.log("Por favor preencha o campo Largura da Viga.");
        return false;
    }

    mk = Number(document.formDC.mDC.value);
    if (document.formDC.mDC.value === "" || isNaN(mk)) {
        alert("Por favor preencha o campo Momento com números.");
        console.log("Por favor preencha o campo Momento.");
        return false;
    }

    concrete = concreteProp[(document.formDC.concreteDC.value)].concType;
    steel = steelProp[(document.formDC.steelDC.value)].steelType;
    diamLongForm = bitola[Number(document.formDC.longDC.value)].diametro;
    diamEstForm = bitola[Number(document.formDC.estDC.value)].diametro;
    gamac = Number(document.formDC.gamacDC.value);
    gamaf = Number(document.formDC.gamafDC.value);
    gamas = Number(document.formDC.gamasDC.value);
    s = Number(document.formDC.sDC.value);
    cob = Number(document.formDC.cobDC.value);
    dl = Number(document.formDC.dlDC.value);
    dlc = Number(document.formDC.dlcDC.value);

    // Getting bitolaLong properties
    i = Number(document.getElementById("bitolaLong").value);

        // Getting concrete properties
    j = Number(document.getElementById("concrete").value);

    fckForm = concreteProp[j].fckProp;

    // Getting steel properties
    k = Number(document.getElementById("steel").value);

    fykForm = steelProp[k].fy;
    Es = steelProp[k].E;

    // Converting Units
    fyk = fykForm / 10;
    fck = fckForm / 100;
    diamEst = diamEstForm / 10;
    diamLong = diamLongForm / 10;

    // Calculating parameters
    fcd = fck / gamac;
    fyd = fyk / gamas;
    tsd = fyd;
    epc = 3.5;
    eps = 10.0;

    //CÁLCULO DO MOMENTO FLETOR MAJORADO
    md = mk * gamaf;

    //CÁLCULO DA ALTURA ÚTIL (d)
    d = h-dl;

    //CÁLCULO DOS LIMITES DOS DOMÍNIOS 2 E 3
    //DOMÍNIO 2
    betax23 = epc/(epc+eps)

    //DOMÍNIO 3
    epyd = (fyd/Es)*1000;
    betax34 = epc/(epc+epyd);

    //CÁLCULO DA POSIÇÃO DA LINHA NEUTRA (x)
    //md = 0.68*bw*x*fcd*(d-0.4*x);
    //(0.4*x^2)+(d*x)+(md/(0.68*bw*fcd))=0

    a = 0.4;
    b = -1*d;
    c = md/(0.68*bw*fcd);
    delta = Math.pow(b , 2) - ((4 * a)*c);

    if( delta > 0){
	deltaR = Math.sqrt(delta);
	x1 = ((-b +  deltaR)  /(2 * a));
	x2 = ((-b -  deltaR)  /(2 * a));
		    	}
                else{
			x1 = ("Sem raiz");
			x2 = ("Sem raiz");
                    }

    if (x1 > h) {
        x = x2
                }
            else{
            x = x1
                }

    //VERIFICAÇÃO DO DOMÍNIO DA VIGA

    x2lim = betax23 * d;
    x3lim = betax34 * d;

    if (x > x2lim && x < x3lim) {
        dominio = "Domínio 3";
        situationD = "Aprovado";
    } else if (x < x2lim) {
        dominio = "Domínio 2";
        situationD = "Aprovado";
    } else if (x > x3lim) {
        dominio = "Domínio 4";
        situationD = "Reprovado";
    } else {
        dominio = "Error";
    }

    //VERIFICAÇÃO DA RELAÇÃO x/d

    if (x / d <= 0.45) {
        situationLN = "Aprovada";
    } else {
        situationLN = "Reprovada";
    }

    //CÁLCULO DA ÁREA DE AÇO

    if (situationD = "Aprovado" && situationLN = "Aprovada") {
        situationS = "Simples";
	as = md/(tsd*(d-0.4*x));
        alert(as);

    } else {
        situationS = "Dupla";
        //Cálculo da nova posição da LN
        xd = 0.45*d;

        //Cálculo de M1d
        m1d = 0.68*bw*xd*fcd*(d-0.4*xd);

        //Cálculo de M2d
        m2d = md - m1d;

        //Encontrar valor de tlsd na tabela (!!!!)
        tlsd = 43.5;

        //Encontrar área de aço comprimida A's
        asl = m2d/(tlsd*(d-dlc));

        //Encontrar área de aço tracionada As
        as1 = m1d/(tsd*(d-0.4*xd));
        as2 = m2d/(tsd*(d-dlc));
        ast = as1 + as2;

        //Resultados
        result = "Área de aço comprimida = "+asl+". Área de aço tracionada = "+ast;
        alert(result);

    }

}

  /*

    var resultado =
        {
            "project":project,
            "beam":beam,
            "vk":vk,
            "mk":mk,
            "lbForm":lbForm,
            "section":section,
            "steel":steel,
            "fyForm":fyForm,
            "fuForm":fuForm,
            "EForm":EForm,
            "ml":ml,
            "d":d,
            "bf":bf,
            "tw":tw,
            "tf":tf,
            "h":h,
            "dl":dl,
            "area":area,
            "ix":ix,
            "wx":wx,
            "rx":rx,
            "zx":zx,
            "iy":iy,
            "wy":wy,
            "ry":ry,
            "zy":zy,
            "rt":rt,
            "it":it,
            "mesa":mesa,
            "alma":alma,
            "cw":cw,
            "u":u,
            "fy":fy,
            "fu":fu,
            "E":E,
            "aw":aw,
            "gama":gama,
            "gama1":gama1,
            "cb":cb,
            "kv":kv,
            "lb":lb,
            "kc":kc,
            "tr":tr,
            "beta1":beta1,
            "a":a,
            "lambV":lambV,
            "lambpV":lambpV,
            "lambrV":lambrV,
            "vpl":vpl,
            "vrd":vrd,
            "mpl":mpl,
            "lambA":lambA,
            "lambpA":lambpA,
            "lambrA":lambrA,
            "mrA":mrA,
            "mcrA":mcrA,
            "mrdA":mrdA,
            "lambM":lambM,
            "lambpM":lambpM,
            "lambrM":lambrM,
            "mrM":mrM,
            "mcrM":mcrM,
            "mrdM":mrdM,
            "lambT":lambT,
            "lambpT":lambpT,
            "lambrT":lambrT,
            "mrT":mrT,
            "mcrT":mcrT,
            "mrdT":mrdT,
            "vsd":vsd,
            "msd":msd,
            "vrd":vrd,
            "mrd":mrd,
            "mrdOut":mrdOut,
            "situationV":situationV,
            "situationA":situationA,
            "situationM":situationM,
            "situationT":situationT,
            "result":result,
            "ratioCSV":ratioCSV,
            "ratioCSM":ratioCSM,
            "ratiopCSV":ratiopCSV,
            "ratiopCSM":ratiopCSM
        };

    document.getElementById("botaoRelatorio").href = "reportCS.html?test=" + JSON.stringify(resultado);
} */

/*
    Cria um objeto bitola
*/
function criarBitola(nomeBitola, linhaBitola) {
    "use strict";
    var opcao = document.createElement("OPTION"),
        texto = document.createTextNode(nomeBitola);
    opcao.value = linhaBitola;
    opcao.appendChild(texto);
    return opcao;
}

var bitolaLista1 = document.getElementById("bitolaLong"),
    bitolaLista2 = document.getElementById("bitolaEst"),
    acoLista = document.getElementById("steel"),
    concretoLista = document.getElementById("concrete"),
    i;

for (i = 0; i < bitola.length; i += 1) {
    bitolaLista1.appendChild(criarBitola(bitola[i].diametro, i));
}

for (i = 0; i < bitola.length; i += 1) {
    bitolaLista2.appendChild(criarBitola(bitola[i].diametro, i));
}

function criarAco(nomeAco, linhaAco) {
    "use strict";
    var opcao = document.createElement("OPTION"),
        texto = document.createTextNode(nomeAco);
    opcao.value = linhaAco;
    opcao.appendChild(texto);
    return opcao;
}

for (i = 0; i < steelProp.length; i += 1) {
    acoLista.appendChild(criarAco(steelProp[i].steelType, i));
}

function criarConcreto(nomeConcreto, linhaConcreto) {
    "use strict";
    var opcao = document.createElement("OPTION"),
        texto = document.createTextNode(nomeConcreto);
    opcao.value = linhaConcreto;
    opcao.appendChild(texto);
    return opcao;
}

for (i = 0; i < concreteProp.length; i += 1) {
    concretoLista.appendChild(criarConcreto(concreteProp[i].concType, i));
}
<commit_msg>Update scriptDC-Dupla.js<commit_after>/*jslint devel: true*/
/*global perfis */

// variables
var project, beam, mk, concrete, steel;
var tsd, as, bw, fcd;
var betax23, betax34, epc, eps, epyd, fyd, Es, fck, fyk, fckForm, fykForm;
var d, h, cob, diamEstForm, diamLongForm, diamEst, diamLong;
var a, b, c, delta, deltaR, x1, x2;
var x, mk, md;
var x2lim, x3lim, dominio, dl;
var gamac, gamaf, gamas, s;
var situationD, situationLN;
var dlc, xd, m1d, m2d, tlsd, asl, as1, as2, ast, situationS;

var bitola = [
    {
        "diametro": 5.0,
        "area": 0.20
    },
    {
        "diametro": 6.3,
        "area": 0.315
    },
    {
        "diametro": 8.0,
        "area": 0.50
    },
    {
        "diametro": 10.0,
        "area": 0.80
    },
    {
        "diametro": 12.5,
        "area": 1.25
    },
    {
        "diametro": 16.0,
        "area": 2.0
    },
    {
        "diametro": 20.0,
        "area": 3.15
    },
    {
        "diametro": 25.0,
        "area": 5.0
    },
    {
        "diametro": 32.0,
        "area": 8.0
    },
    {
        "diametro": 40.0,
        "area": 12.50
    }

];

var steelProp = [
    {
        "steelType": "CA25",
        "fy": 250,
        "fu": 217.4,
        "E": 21000
    },
    {
        "steelType": "CA50",
        "fy": 500,
        "fu": 434.8,
        "E": 21000
    },
    {
        "steelType": "CA60",
        "fy": 600,
        "fu": 521.7,
        "E": 21000
    }
];

var concreteProp = [
    {
        "concType": "C20",
        "fckProp": 200
    },
    {
        "concType": "C25",
        "fckProp": 250
    },
    {
        "concType": "C30",
        "fckProp": 300
    },
    {
        "concType": "C35",
        "fckProp": 350
    },

    {
        "concType": "C40",
        "fckProp": 400
    },
    {
        "concType": "C45",
        "fckProp": 450
    },
    {
        "concType": "C50",
        "fckProp": 500
    }

];

function processFormDC() {
    "use strict";
    var i, j, k;

    // Getting values from HTML form inputs
    project = document.formDC.projectDC.value;
    /* if (project === "") {
        alert("Por favor preencha o campo Projeto.");
        console.log("Por favor preencha o campo Projeto.");
        return false;
    } */
    if (project === "") {
        project = "Exemplo 1";
    }

    beam = document.formDC.beamDC.value;
    /* if (beam === "") {
        alert("Por favor preencha o campo Viga.");
        console.log("Por favor preencha o campo Viga.");
        return false;
    } */
    if (beam === "") {
        beam = "V1";
    }

    h = Number(document.formDC.hDC.value);
    if (document.formDC.hDC.value === "" || isNaN(h)) {
        alert("Por favor preencha o campo Altura da Viga com números.");
        console.log("Por favor preencha o campo Altura da Viga.");
        return false;
    }

    bw = Number(document.formDC.bwDC.value);
    if (document.formDC.bwDC.value === "" || isNaN(bw)) {
        alert("Por favor preencha o campo Largura da Viga com números.");
        console.log("Por favor preencha o campo Largura da Viga.");
        return false;
    }

    mk = Number(document.formDC.mDC.value);
    if (document.formDC.mDC.value === "" || isNaN(mk)) {
        alert("Por favor preencha o campo Momento com números.");
        console.log("Por favor preencha o campo Momento.");
        return false;
    }

    concrete = concreteProp[(document.formDC.concreteDC.value)].concType;
    steel = steelProp[(document.formDC.steelDC.value)].steelType;
    diamLongForm = bitola[Number(document.formDC.longDC.value)].diametro;
    diamEstForm = bitola[Number(document.formDC.estDC.value)].diametro;
    gamac = Number(document.formDC.gamacDC.value);
    gamaf = Number(document.formDC.gamafDC.value);
    gamas = Number(document.formDC.gamasDC.value);
    s = Number(document.formDC.sDC.value);
    cob = Number(document.formDC.cobDC.value);
    dl = Number(document.formDC.dlDC.value);
    dlc = Number(document.formDC.dlcDC.value);

    // Getting bitolaLong properties
    i = Number(document.getElementById("bitolaLong").value);

        // Getting concrete properties
    j = Number(document.getElementById("concrete").value);

    fckForm = concreteProp[j].fckProp;

    // Getting steel properties
    k = Number(document.getElementById("steel").value);

    fykForm = steelProp[k].fy;
    Es = steelProp[k].E;

    // Converting Units
    fyk = fykForm / 10;
    fck = fckForm / 100;
    diamEst = diamEstForm / 10;
    diamLong = diamLongForm / 10;

    // Calculating parameters
    fcd = fck / gamac;
    fyd = fyk / gamas;
    tsd = fyd;
    epc = 3.5;
    eps = 10.0;

    //CÁLCULO DO MOMENTO FLETOR MAJORADO
    md = mk * gamaf;

    //CÁLCULO DA ALTURA ÚTIL (d)
    d = h-dl;

    //CÁLCULO DOS LIMITES DOS DOMÍNIOS 2 E 3
    //DOMÍNIO 2
    betax23 = epc/(epc+eps)

    //DOMÍNIO 3
    epyd = (fyd/Es)*1000;
    betax34 = epc/(epc+epyd);

    //CÁLCULO DA POSIÇÃO DA LINHA NEUTRA (x)
    //md = 0.68*bw*x*fcd*(d-0.4*x);
    //(0.4*x^2)+(d*x)+(md/(0.68*bw*fcd))=0

    a = 0.4;
    b = -1*d;
    c = md/(0.68*bw*fcd);
    delta = Math.pow(b , 2) - ((4 * a)*c);

    if( delta > 0){
	deltaR = Math.sqrt(delta);
	x1 = ((-b +  deltaR)  /(2 * a));
	x2 = ((-b -  deltaR)  /(2 * a));
		    	}
                else{
			x1 = ("Sem raiz");
			x2 = ("Sem raiz");
                    }

    if (x1 > h) {
        x = x2
                }
            else{
            x = x1
                }

    //VERIFICAÇÃO DO DOMÍNIO DA VIGA

    x2lim = betax23 * d;
    x3lim = betax34 * d;

    if (x > x2lim && x < x3lim) {
        dominio = "Domínio 3";
        situationD = "Aprovado";
    } else if (x < x2lim) {
        dominio = "Domínio 2";
        situationD = "Aprovado";
    } else if (x > x3lim) {
        dominio = "Domínio 4";
        situationD = "Reprovado";
    } else {
        dominio = "Error";
    }

    //VERIFICAÇÃO DA RELAÇÃO x/d

    if (x / d <= 0.45) {
        situationLN = "Aprovada";
    } else {
        situationLN = "Reprovada";
    }

    //CÁLCULO DA ÁREA DE AÇO

    //if (situationD = "Aprovado" && situationLN = "Aprovada") {
        //situationS = "Simples";
	as = md/(tsd*(d-0.4*x));
        //alert(as);

    //} else {
        //situationS = "Dupla";
        //Cálculo da nova posição da LN
        xd = 0.45*d;

        //Cálculo de M1d
        m1d = 0.68*bw*xd*fcd*(d-0.4*xd);

        //Cálculo de M2d
        m2d = md - m1d;

        //Encontrar valor de tlsd na tabela (!!!!)
        tlsd = 43.5;

        //Encontrar área de aço comprimida A's
        asl = m2d/(tlsd*(d-dlc));

        //Encontrar área de aço tracionada As
        as1 = m1d/(tsd*(d-0.4*xd));
        as2 = m2d/(tsd*(d-dlc));
        ast = as1 + as2;

        //Resultados
        //result = "Área de aço comprimida = "+asl+". Área de aço tracionada = "+ast;
        //alert(result);

    }

}

  /*
    document.getElementById("botaoRelatorio").href = "reportCS.html?test=" + JSON.stringify(resultado);
} */

/*
    Cria um objeto bitola
*/
function criarBitola(nomeBitola, linhaBitola) {
    "use strict";
    var opcao = document.createElement("OPTION"),
        texto = document.createTextNode(nomeBitola);
    opcao.value = linhaBitola;
    opcao.appendChild(texto);
    return opcao;
}

var bitolaLista1 = document.getElementById("bitolaLong"),
    bitolaLista2 = document.getElementById("bitolaEst"),
    acoLista = document.getElementById("steel"),
    concretoLista = document.getElementById("concrete"),
    i;

for (i = 0; i < bitola.length; i += 1) {
    bitolaLista1.appendChild(criarBitola(bitola[i].diametro, i));
}

for (i = 0; i < bitola.length; i += 1) {
    bitolaLista2.appendChild(criarBitola(bitola[i].diametro, i));
}

function criarAco(nomeAco, linhaAco) {
    "use strict";
    var opcao = document.createElement("OPTION"),
        texto = document.createTextNode(nomeAco);
    opcao.value = linhaAco;
    opcao.appendChild(texto);
    return opcao;
}

for (i = 0; i < steelProp.length; i += 1) {
    acoLista.appendChild(criarAco(steelProp[i].steelType, i));
}

function criarConcreto(nomeConcreto, linhaConcreto) {
    "use strict";
    var opcao = document.createElement("OPTION"),
        texto = document.createTextNode(nomeConcreto);
    opcao.value = linhaConcreto;
    opcao.appendChild(texto);
    return opcao;
}

for (i = 0; i < concreteProp.length; i += 1) {
    concretoLista.appendChild(criarConcreto(concreteProp[i].concType, i));
}
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var passport = require('passport');

var authentication = require('../middlewares/authentication');
var Publicacion = require('../models/PublicacionSchema');
var Comentario = require('../models/ComentarioSchema');
var Respuesta = require('../models/RespuestaSchema');
var Publicator = require('../modules/Publicator');
var UsuarioAccion = require('../models/UsuarioAccionSchema');
var UsuarioNotificacion = require('../models/UsuarioNotificacionSchema');
var PublicacionLike = require('../models/PublicacionLikeSchema');
var PublicacionDisLike = require('../models/PublicacionDisLikeSchema');
var ComentarioLike = require('../models/ComentarioLikeSchema');
var ComentarioDisLike = require('../models/ComentarioDisLikeSchema');
var RespuestaLike = require('../models/RespuestaLikeSchema');
var RespuestaDisLike = require('../models/RespuestaDisLikeSchema');
var UsuarioAceptacion = require('../models/UsuarioAceptacionSchema');

router.post('/propuestas', authentication.isCandidato, function(req, res, next) {
  Publicator.propuesta(req, res, next);
});

router.post('/quejas', authentication.isCiudadano, function(req, res, next) {
  Publicator.queja(req, res, next);
});

router.get('/', function(req, res, next) {
  Publicacion.find(function(err, publicaciones) {
    if (!err) {
      res.json(publicaciones);
    } else {
      return next(err);
    }
  });
});

router.get('/propuestas', function(req, res, next) {
  Publicacion.find( {propuesta: true}, function(err, publicaciones) {
    if (!err) {
      res.json(publicaciones);
    } else {
      return next(err);
    }
  });
});

router.get('/quejas', function(req, res, next) {
  Publicacion.find( {propuesta: false}, function(err, publicaciones) {
    if (!err) {
      res.json(publicaciones);
    } else {
      return next(err);
    }
  });
});

router.get('/comentarios/:id_comentario', function(req, res, next) {
  var id_comentario = req.params.id_comentario;

  Comentario.findOne({_id: id_comentario},'-__v', function(err, comentario) {
    if (!err) {
      res.json(comentario);
    } else {
      return next(err);
    }
  });
});

router.put('/likeComentario/:id_comentario', function(req, res, next) {
  var id_comentario = req.params.id_comentario;

  ComentarioLike.findOne({id_usuario: req.user.id_usuario, id_comentario: id_comentario}, function(err, comentarioLike) {
	if (!err) {
	  if (comentarioLike) {
		res.status(400).json({message: 'Sólo se puede dar un me gusta'});
	  } else {
        var comentarioLike = new ComentarioLike();

		comentarioLike.id_usuario = req.user.id_usuario;
		comentarioLike.id_comentario = id_comentario;

        comentarioLike.save(function(err) {
          if (!err) {
		    Comentario.findOneAndUpdate({_id: id_comentario},  {$inc: {cantidad_likes: 1}}, function(err, comentario) {
			  if (!err) {
				res.json(comentario);
			  } else {
				console.log(err);
				return next(err);
			  }
			});
          } else {
            res.status(400).json({message: 'Verifique los campos'});
          }
        });
      }
    } else {
            res.status(400).json({message: 'Error'});
    }
  });
});

router.put('/disLikeComentario/:id_comentario', function(req, res, next) {
  var id_comentario = req.params.id_comentario;

  ComentarioDisLike.findOne({id_usuario: req.user.id_usuario, id_comentario: id_comentario}, function(err, comentarioDisLike) {
	if (!err) {
	  if (comentarioDisLike) {
		res.status(400).json({message: 'Sólo se puede dar un no me gusta'});
	  } else {
        var comentarioDisLike = new ComentarioDisLike();

		comentarioDisLike.id_usuario = req.user.id_usuario;
		comentarioDisLike.id_comentario = id_comentario;

        comentarioDisLike.save(function(err) {
          if (!err) {
		    Comentario.findOneAndUpdate({_id: id_comentario},  {$inc: {cantidad_disLikes: 1}}, function(err, comentario) {
			  if (!err) {
				res.json(comentario);
			  } else {
				console.log(err);
				return next(err);
			  }
			});
          } else {
            res.status(400).json({message: 'Verifique los campos'});
          }
        });
      }
    } else {
            res.status(400).json({message: 'Error'});
    }
  });
});

router.put('/likeRespuesta/:id_respuesta', function(req, res, next) {
  var id_respuesta = req.params.id_respuesta;

  RespuestaLike.findOne({id_usuario: req.user.id_usuario, id_respuesta: id_respuesta}, function(err, respuestaLike) {
	if (!err) {
	  if (respuestaLike) {
		res.status(400).json({message: 'Sólo se puede dar un me gusta'});
	  } else {
        var respuestaLike = new RespuestaLike();

		respuestaLike.id_usuario = req.user.id_usuario;
		respuestaLike.id_respuesta = id_respuesta;

        respuestaLike.save(function(err) {
          if (!err) {
		    Respuesta.findOneAndUpdate({_id: id_respuesta},  {$inc: {cantidad_likes: 1}}, function(err, respuesta) {
			  if (!err) {
				res.json(respuesta);
			  } else {
				console.log(err);
				return next(err);
			  }
			});
          } else {
            res.status(400).json({message: 'Verifique los campos'});
          }
        });
      }
    } else {
            res.status(400).json({message: 'Error'});
    }
  });
});

router.put('/disLikeRespuesta/:id_respuesta', function(req, res, next) {
  var id_respuesta = req.params.id_respuesta;

  RespuestaDisLike.findOne({id_usuario: req.user.id_usuario, id_respuesta: id_respuesta}, function(err, respuestaDisLike) {
	if (!err) {
	  if (respuestaDisLike) {
		res.status(400).json({message: 'Sólo se puede dar un no me gusta'});
	  } else {
        var respuestaDisLike = new RespuestaDisLike();

		respuestaDisLike.id_usuario = req.user.id_usuario;
		respuestaDisLike.id_respuesta = id_respuesta;

        respuestaDisLike.save(function(err) {
          if (!err) {
		    Respuesta.findOneAndUpdate({_id: id_respuesta},  {$inc: {cantidad_disLikes: 1}}, function(err, respuesta) {
			  if (!err) {
				res.json(respuesta);
			  } else {
				console.log(err);
				return next(err);
			  }
			});
          } else {
            res.status(400).json({message: 'Verifique los campos'});
          }
        });
      }
    } else {
            res.status(400).json({message: 'Error'});
    }
  });
});

router.get('/:id_comentario/respuestas', function(req, res, next) {
  var id_comentario = req.params.id_comentario;

  Respuesta.find({id_comentario: id_comentario}, function(err, respuesta) {
    if (!err) {
      res.json(respuesta);
    } else {
      return next(err);
    }
  });
});

router.post('/:id_comentario/respuestas', authentication.isLoggedIn, function(req, res, next) {
  var id_comentario = req.params.id_comentario;

  var respuesta = new Respuesta();
  var comentario = Comentario.findOne({_id: id_comentario}, function(err, comentario) {
    if (err) {
      res.status(400).json({message: 'Comentario no encontrado'})
    } else {
      comentario.toObject();
      respuesta.id_usuario = req.user.id_usuario;
	  respuesta.imagen_perfil = req.user.imagen_perfil;
      respuesta.id_comentario = comentario['_id'];
      respuesta.descripcion = req.body.descripcion;

      respuesta.save(function(err) {
        if (!err) {
          res.json({message: 'Respuesta creada con exito'})
        } else {
          res.status(400).json({message: 'Verifique los campos'});
        }
      });
    }
  });
});

router.get('/:id_publicacion', function(req, res, next) {
  var id_publicacion = req.params.id_publicacion;

  Publicacion.findOne({_id: id_publicacion},'-__v', function(err, publicacion) {
    if (!err) {
      if (req.user){
        UsuarioAccion.findOneAndUpdate(
            {id_usuario: req.user.id_usuario},
            {$addToSet: {id_propuestas_vistas: id_publicacion}},
            function(err, usuarioAccion) {
              if (err) {
                res.status(500).json({message: 'Intente de nuevo'});
                return;
              } else {
                UsuarioNotificacion.findOneAndUpdate(
                    {id_usuario: req.user.id_usuario},
                    {$pull: {notificacion_publicaciones: {id_objeto: id_publicacion}}},
                    function(err, usuarioNotificacion) {
                      if (err) {
                        res.status(500).json({message: 'Intente de nuevo'});
                        return;
                      } else {
                        res.json(publicacion);
                      }
                    });
              }
            });
      } else {
        res.json(publicacion);
      }

    } else {
      return next(err);
    }
  });
});

router.put('/:id_publicacion/like', function(req, res, next) {
  var id_publicacion = req.params.id_publicacion;

  PublicacionLike.findOne({id_usuario: req.user.id_usuario, id_publicacion: id_publicacion}, function(err, publicacionLike) {
	if (!err) {
	  if (publicacionLike) {
		res.status(400).json({message: 'Sólo se puede dar un me gusta'});
	  } else {
        var publicacionLike = new PublicacionLike();

		publicacionLike.id_usuario = req.user.id_usuario;
		publicacionLike.id_publicacion = id_publicacion;

        publicacionLike.save(function(err) {
          if (!err) {
		    Publicacion.findOneAndUpdate({_id: id_publicacion},  {$inc: {cantidad_likes: 1}}, function(err, publicacion) {
			  if (!err) {
				res.json(publicacion);
			  } else {
				console.log(err);
				return next(err);
			  }
			});
          } else {
            res.status(400).json({message: 'Verifique los campos'});
          }
        });
      }
    } else {
            res.status(400).json({message: 'error'});
    }
  });
});

router.put('/:id_publicacion/disLike', function(req, res, next) {
  var id_publicacion = req.params.id_publicacion;

  PublicacionDisLike.findOne({id_usuario: req.user.id_usuario, id_publicacion: id_publicacion}, function(err, publicacionDisLike) {
	if (!err) {
	  if (publicacionDisLike) {
		res.status(400).json({message: 'Sólo se puede dar un no me gusta'});
	  } else {
        var publicacionDisLike = new PublicacionDisLike();

		publicacionDisLike.id_usuario = req.user.id_usuario;
		publicacionDisLike.id_publicacion = id_publicacion;

        publicacionDisLike.save(function(err) {
          if (!err) {
		    Publicacion.findOneAndUpdate({_id: id_publicacion},  {$inc: {cantidad_disLikes: 1}}, function(err, publicacion) {
			  if (!err) {
				res.json(publicacion);
			  } else {
				console.log(err);
				return next(err);
			  }
			});
          } else {
            res.status(400).json({message: 'Verifique los campos'});
          }
        });
      }
    } else {
            res.status(400).json({message: 'error'});
    }
  });
});

router.get('/:id_publicacion/comentarios', function(req, res, next) {
  var id_publicacion = req.params.id_publicacion;

  Comentario.find({id_publicacion: id_publicacion}, function(err, comentario) {
    if (!err) {
      res.json(comentario);
    } else {
      return next(err);
    }
  });
});

router.post('/:id_publicacion/comentarios', authentication.isLoggedIn, function(req, res, next) {
  var id_publicacion = req.params.id_publicacion;

  var comentario = new Comentario();
  var publicacion = Publicacion.findOne({_id: id_publicacion}, function(err, publicacion) {
    if (err) {
      res.status(400).json({message: 'Publicacion no encontrada'})
    } else {
      publicacion.toObject();
      comentario.id_usuario = req.user.id_usuario;
      comentario.imagen_perfil = req.user.imagen_perfil;
      comentario.id_publicacion = publicacion['_id'];
      comentario.descripcion = req.body.descripcion;

      comentario.save(function(err) {
        if (!err) {
          res.json({message: 'Comentario creada con exito'})
        } else {
          res.status(400).json({message: 'Verifique los campos'});
        }
      });
    }
  });
});

router.post('/:id_publicacion/aceptaciones', authentication.isLoggedIn, function(req, res, next) {
  var id_publicacion = req.params.id_publicacion;
  var id_usuario = req.user.id_usuario;

  var publicacion = Publicacion.findOne({_id: id_publicacion}, function(err, publicacion) {
    if (err) {
      res.status(500).json({message: 'Intente de nuevo'})
    } else {
      if (!publicacion) {
        res.status(400).json({message: 'Queja no encontrada'})
      }
      if (publicacion.aceptada_por) {
        res.status(400).json({message: 'La Queja ya fue aceptada'})
      }
      publicacion.aceptada = true;
      publicacion.aceptada_por = id_usuario;

      publicacion.save(function(err) {
        if (!err) {
          UsuarioAceptacion.findOneAndUpdate(
            {id_usuario: req.user.id_usuario},
            {$addToSet: {id_quejas_aceptadas: id_publicacion}},
            function(err, usuarioAccion) {
              if (err) {
                res.status(500).json({message: 'Intente de nuevo'})
              } else {
                res.json({message: 'Queja aceptada con exito!'})
              }
            });
        } else {
          res.status(400).json({message: 'Verifique los campos'});
        }
      });
    }
  });
});

module.exports = router;
<commit_msg>Revision and fix. Indent.<commit_after>var express = require('express');
var router = express.Router();
var passport = require('passport');

var authentication = require('../middlewares/authentication');
var Publicacion = require('../models/PublicacionSchema');
var Comentario = require('../models/ComentarioSchema');
var Respuesta = require('../models/RespuestaSchema');
var Publicator = require('../modules/Publicator');
var UsuarioAccion = require('../models/UsuarioAccionSchema');
var UsuarioNotificacion = require('../models/UsuarioNotificacionSchema');
var PublicacionLike = require('../models/PublicacionLikeSchema');
var PublicacionDisLike = require('../models/PublicacionDisLikeSchema');
var ComentarioLike = require('../models/ComentarioLikeSchema');
var ComentarioDisLike = require('../models/ComentarioDisLikeSchema');
var RespuestaLike = require('../models/RespuestaLikeSchema');
var RespuestaDisLike = require('../models/RespuestaDisLikeSchema');
var UsuarioAceptacion = require('../models/UsuarioAceptacionSchema');

router.post('/propuestas', authentication.isCandidato, function(req, res, next) {
  Publicator.propuesta(req, res, next);
});

router.post('/quejas', authentication.isCiudadano, function(req, res, next) {
  Publicator.queja(req, res, next);
});

router.get('/', function(req, res, next) {
  Publicacion.find(function(err, publicaciones) {
    if (!err) {
      res.json(publicaciones);
    } else {
      return next(err);
    }
  });
});

router.get('/propuestas', function(req, res, next) {
  Publicacion.find( {propuesta: true}, function(err, publicaciones) {
    if (!err) {
      res.json(publicaciones);
    } else {
      return next(err);
    }
  });
});

router.get('/quejas', function(req, res, next) {
  Publicacion.find( {propuesta: false}, function(err, publicaciones) {
    if (!err) {
      res.json(publicaciones);
    } else {
      return next(err);
    }
  });
});

router.get('/comentarios/:id_comentario', function(req, res, next) {
  var id_comentario = req.params.id_comentario;

  Comentario.findOne({_id: id_comentario},'-__v', function(err, comentario) {
    if (!err) {
      res.json(comentario);
    } else {
      return next(err);
    }
  });
});

router.put('/likeComentario/:id_comentario', function(req, res, next) {
  var id_comentario = req.params.id_comentario;

  ComentarioLike.findOne({id_usuario: req.user.id_usuario, id_comentario: id_comentario}, function(err, comentarioLike) {
    if (!err) {
      if (comentarioLike) {
        res.status(400).json({message: 'Sólo se puede dar un me gusta'});
      } else {
        var comentarioLike = new ComentarioLike();

        comentarioLike.id_usuario = req.user.id_usuario;
        comentarioLike.id_comentario = id_comentario;

        comentarioLike.save(function(err) {
          if (!err) {
            Comentario.findOneAndUpdate({_id: id_comentario},  {$inc: {cantidad_likes: 1}}, function(err, comentario) {
              if (!err) {
                res.json(comentario);
              } else {
                console.log(err);
                return next(err);
              }
            });
          } else {
            res.status(400).json({message: 'Verifique los campos'});
          }
        });
      }
    } else {
      res.status(400).json({message: 'Error'});
    }
  });
});

router.put('/disLikeComentario/:id_comentario', function(req, res, next) {
  var id_comentario = req.params.id_comentario;

  ComentarioDisLike.findOne({id_usuario: req.user.id_usuario, id_comentario: id_comentario}, function(err, comentarioDisLike) {
    if (!err) {
      if (comentarioDisLike) {
        res.status(400).json({message: 'Sólo se puede dar un no me gusta'});
      } else {
        var comentarioDisLike = new ComentarioDisLike();

        comentarioDisLike.id_usuario = req.user.id_usuario;
        comentarioDisLike.id_comentario = id_comentario;

        comentarioDisLike.save(function(err) {
          if (!err) {
            Comentario.findOneAndUpdate({_id: id_comentario},  {$inc: {cantidad_disLikes: 1}}, function(err, comentario) {
              if (!err) {
                res.json(comentario);
              } else {
                console.log(err);
                return next(err);
              }
            });
          } else {
            res.status(400).json({message: 'Verifique los campos'});
          }
        });
      }
    } else {
      res.status(400).json({message: 'Error'});
    }
  });
});

router.put('/likeRespuesta/:id_respuesta', function(req, res, next) {
  var id_respuesta = req.params.id_respuesta;

  RespuestaLike.findOne({id_usuario: req.user.id_usuario, id_respuesta: id_respuesta}, function(err, respuestaLike) {
    if (!err) {
      if (respuestaLike) {
        res.status(400).json({message: 'Sólo se puede dar un me gusta'});
      } else {
        var respuestaLike = new RespuestaLike();

        respuestaLike.id_usuario = req.user.id_usuario;
        respuestaLike.id_respuesta = id_respuesta;

        respuestaLike.save(function(err) {
          if (!err) {
            Respuesta.findOneAndUpdate({_id: id_respuesta},  {$inc: {cantidad_likes: 1}}, function(err, respuesta) {
              if (!err) {
                res.json(respuesta);
              } else {
                console.log(err);
                return next(err);
              }
            });
          } else {
            res.status(400).json({message: 'Verifique los campos'});
          }
        });
      }
    } else {
      res.status(400).json({message: 'Error'});
    }
  });
});

router.put('/disLikeRespuesta/:id_respuesta', function(req, res, next) {
  var id_respuesta = req.params.id_respuesta;

  RespuestaDisLike.findOne({id_usuario: req.user.id_usuario, id_respuesta: id_respuesta}, function(err, respuestaDisLike) {
    if (!err) {
      if (respuestaDisLike) {
        res.status(400).json({message: 'Sólo se puede dar un no me gusta'});
      } else {
        var respuestaDisLike = new RespuestaDisLike();

        respuestaDisLike.id_usuario = req.user.id_usuario;
        respuestaDisLike.id_respuesta = id_respuesta;

        respuestaDisLike.save(function(err) {
          if (!err) {
            Respuesta.findOneAndUpdate({_id: id_respuesta},  {$inc: {cantidad_disLikes: 1}}, function(err, respuesta) {
              if (!err) {
                res.json(respuesta);
              } else {
                console.log(err);
                return next(err);
              }
            });
          } else {
            res.status(400).json({message: 'Verifique los campos'});
          }
        });
      }
    } else {
      res.status(400).json({message: 'Error'});
    }
  });
});

router.get('/:id_comentario/respuestas', function(req, res, next) {
  var id_comentario = req.params.id_comentario;

  Respuesta.find({id_comentario: id_comentario}, function(err, respuesta) {
    if (!err) {
      res.json(respuesta);
    } else {
      return next(err);
    }
  });
});

router.post('/:id_comentario/respuestas', authentication.isLoggedIn, function(req, res, next) {
  var id_comentario = req.params.id_comentario;

  var respuesta = new Respuesta();
  var comentario = Comentario.findOne({_id: id_comentario}, function(err, comentario) {
    if (err) {
      res.status(400).json({message: 'Comentario no encontrado'})
    } else {
      comentario.toObject();
      respuesta.id_usuario = req.user.id_usuario;
      respuesta.imagen_perfil = req.user.imagen_perfil;
      respuesta.id_comentario = comentario['_id'];
      respuesta.descripcion = req.body.descripcion;

      respuesta.save(function(err) {
        if (!err) {
          res.json({message: 'Respuesta creada con exito'})
        } else {
          res.status(400).json({message: 'Verifique los campos'});
        }
      });
    }
  });
});

router.get('/:id_publicacion', function(req, res, next) {
  var id_publicacion = req.params.id_publicacion;

  Publicacion.findOne({_id: id_publicacion},'-__v', function(err, publicacion) {
    if (!err) {
      if (req.user){
        UsuarioAccion.findOneAndUpdate(
          {id_usuario: req.user.id_usuario},
          {$addToSet: {id_propuestas_vistas: id_publicacion}},
          function(err, usuarioAccion) {
            if (err) {
              res.status(500).json({message: 'Intente de nuevo'});
              return;
            } else {
              UsuarioNotificacion.findOneAndUpdate(
                {id_usuario: req.user.id_usuario},
                {$pull: {notificacion_publicaciones: {id_objeto: id_publicacion}}},
                function(err, usuarioNotificacion) {
                  if (err) {
                    res.status(500).json({message: 'Intente de nuevo'});
                    return;
                  } else {
                    res.json(publicacion);
                  }
                });
            }
          });
      } else {
        res.json(publicacion);
      }

    } else {
      return next(err);
    }
  });
});

router.put('/:id_publicacion/like', function(req, res, next) {
  var id_publicacion = req.params.id_publicacion;

  PublicacionLike.findOne({id_usuario: req.user.id_usuario, id_publicacion: id_publicacion}, function(err, publicacionLike) {
    if (!err) {
      if (publicacionLike) {
        res.status(400).json({message: 'Sólo se puede dar un me gusta'});
      } else {
        var publicacionLike = new PublicacionLike();

        publicacionLike.id_usuario = req.user.id_usuario;
        publicacionLike.id_publicacion = id_publicacion;

        publicacionLike.save(function(err) {
          if (!err) {
            Publicacion.findOneAndUpdate({_id: id_publicacion},  {$inc: {cantidad_likes: 1}}, function(err, publicacion) {
              if (!err) {
                res.json(publicacion);
              } else {
                console.log(err);
                return next(err);
              }
            });
          } else {
            res.status(400).json({message: 'Verifique los campos'});
          }
        });
      }
    } else {
      res.status(400).json({message: 'error'});
    }
  });
});

router.put('/:id_publicacion/disLike', function(req, res, next) {
  var id_publicacion = req.params.id_publicacion;

  PublicacionDisLike.findOne({id_usuario: req.user.id_usuario, id_publicacion: id_publicacion}, function(err, publicacionDisLike) {
    if (!err) {
      if (publicacionDisLike) {
        res.status(400).json({message: 'Sólo se puede dar un no me gusta'});
      } else {
        var publicacionDisLike = new PublicacionDisLike();

        publicacionDisLike.id_usuario = req.user.id_usuario;
        publicacionDisLike.id_publicacion = id_publicacion;

        publicacionDisLike.save(function(err) {
          if (!err) {
            Publicacion.findOneAndUpdate({_id: id_publicacion},  {$inc: {cantidad_disLikes: 1}}, function(err, publicacion) {
              if (!err) {
                res.json(publicacion);
              } else {
                console.log(err);
                return next(err);
              }
            });
          } else {
            res.status(400).json({message: 'Verifique los campos'});
          }
        });
      }
    } else {
      res.status(400).json({message: 'error'});
    }
  });
});

router.get('/:id_publicacion/comentarios', function(req, res, next) {
  var id_publicacion = req.params.id_publicacion;

  Comentario.find({id_publicacion: id_publicacion}, function(err, comentario) {
    if (!err) {
      res.json(comentario);
    } else {
      return next(err);
    }
  });
});

router.post('/:id_publicacion/comentarios', authentication.isLoggedIn, function(req, res, next) {
  var id_publicacion = req.params.id_publicacion;

  var comentario = new Comentario();
  var publicacion = Publicacion.findOne({_id: id_publicacion}, function(err, publicacion) {
    if (err) {
      res.status(400).json({message: 'Publicacion no encontrada'})
    } else {
      publicacion.toObject();
      comentario.id_usuario = req.user.id_usuario;
      comentario.imagen_perfil = req.user.imagen_perfil;
      comentario.id_publicacion = publicacion['_id'];
      comentario.descripcion = req.body.descripcion;

      comentario.save(function(err) {
        if (!err) {
          res.json({message: 'Comentario creada con exito'})
        } else {
          res.status(400).json({message: 'Verifique los campos'});
        }
      });
    }
  });
});

router.post('/:id_publicacion/aceptaciones', authentication.isLoggedIn, function(req, res, next) {
  var id_publicacion = req.params.id_publicacion;
  var id_usuario = req.user.id_usuario;

  var publicacion = Publicacion.findOne({_id: id_publicacion}, function(err, publicacion) {
    if (err) {
      res.status(500).json({message: 'Intente de nuevo'})
    } else {
      if (!publicacion) {
        res.status(400).json({message: 'Queja no encontrada'})
      }
      if (publicacion.aceptada_por) {
        res.status(400).json({message: 'La Queja ya fue aceptada'})
      }
      publicacion.aceptada = true;
      publicacion.aceptada_por = id_usuario;

      publicacion.save(function(err) {
        if (!err) {
          UsuarioAceptacion.findOneAndUpdate(
            {id_usuario: req.user.id_usuario},
            {$addToSet: {id_quejas_aceptadas: id_publicacion}},
            function(err, usuarioAccion) {
              if (err) {
                res.status(500).json({message: 'Intente de nuevo'})
              } else {
                res.json({message: 'Queja aceptada con exito!'})
              }
            });
        } else {
          res.status(400).json({message: 'Verifique los campos'});
        }
      });
    }
  });
});

module.exports = router;
<|endoftext|>


<commit_before>ScriptLoader.load('View.js');

var Grid = new Class({
	Extends: View,
	options:{
	    onSelect: $empty,
	    onSortChange: $empty,
	    onDoubleClick: $empty
    },

    initialize: function(element, options) {
        Asset.css('grid.css');
        this.setOptions(options);
        this.parent(element, this.options);
		this.headOff = this.element.getElement('.gridContainer thead');
		this.headOff.setStyle('display', 'none');
        this.tbody = this.element.getElement('.gridContainer tbody');

    },

    /*
     * {
     *     <fieldName>: {
     *         type: <fieldType>,
     *         [key: true,]
     *         [sort: 'asc'|'desc',]
     *         [visible: true]
     *     },
     *     ...
     * }
     */
    setMetadata: function(metadata) {
        /*
         * Проверяем соответствие видимых полей физической структуре таблицы,
         * определяем имя ключевого поля, и параметры сортировки.
         */
        var visibleFieldsCount = 0;
        for (var fieldName in metadata) {
            if (metadata[fieldName].key) this.keyFieldName = fieldName;
            if (metadata[fieldName].visible) {
                if (typeof metadata[fieldName].sort == 'string') {
                    if (['asc', 'desc'].test(metadata[fieldName].sort)) {
                        this.sortOrder = metadata[fieldName].sort;
                    }
                    this.changeSort(visibleFieldsCount);
                }

                visibleFieldsCount++;
            }
        }

        this.parent(metadata);
    },

    build: function() {
		var preiouslySelectedRecordKey = this.getSelectedRecordKey();

		this.headOff.setStyle('visibility', 'hidden');
		this.headOff.setStyle('display', 'table-header-group');

        if (this.data.length) {
			if(!this.dataKeyExists(preiouslySelectedRecordKey)){
				preiouslySelectedRecordKey = false;
			}
            this.data.each(
            	function(record, key) {
            		this.addRecord(record, key, preiouslySelectedRecordKey);
            	},
            	this
            );
            if (!preiouslySelectedRecordKey) {
				this.selectItem(this.tbody.getFirst());
            }
        }
        else {
            this.addRecord(null);
        }

		var headers = new Array();
		this.headOff.getElements('th').each(function(element, key){
			headers[key] = element.clientWidth;
		})

        this.element.getElements('.gridHeadContainer col').each(function(element, key){
			element.setStyle('width',headers[key]);
		});

        this.element.getElements('.gridContainer col').each(function(element, key){
			element.setStyle('width',headers[key]);
		});

		this.element.getElement('.gridContainer table.gridTable').setStyle('tableLayout', 'fixed');
		this.element.getElement('.gridHeadContainer table.gridTable').setStyle('tableLayout', 'fixed');
		this.headOff.setStyle('display', 'none');

    },

    isEmpty: function() {
        return !this.data.length;
    },

    getSelectedRecord: function() {
        if (!this.getSelectedItem()) return false;
        return this.getSelectedItem().record;
    },

    getSelectedRecordKey: function() {
        if (!this.keyFieldName) return false;
        return this.getSelectedRecord()[this.keyFieldName];
    },

    dataKeyExists: function(key){
		if(!this.data) return false;
		if (!this.keyFieldName) return false;

		return this.data.some(function(item, index){
			return (item[this.keyFieldName]== key);
		}.bind(this));
	},

    getSortFieldName: function() {
        //return this.headers[this.sortFieldIndex].fieldName;
    },

    getSortOrder: function() {
        return this.sortOrder;
    },

    clear: function() {
        this.selectItem(false);
        while (this.tbody.hasChildNodes()) {
            this.tbody.removeChild(this.tbody.firstChild);
        }
    },

    // Private methods:

    fitHeaders: function() {
        this.headersContainer.setStyle('visibility', '');
        var firstRow = this.tbody.getFirst();
        this.headers.each(function(header, i) {
            var delta = -(i == 0 ? 27 : 28);
            // Увеличиваем дельту на 16px (размер полосы прокрутки) если это последняя колонка и грид не пустой.
            if (i == firstRow.childNodes.length-1) {
                delta += ((this.data.length || this.prevDataLength > 0) ? 16 : 0);
                this.prevDataLength = this.data.length;
            }
            header.setStyle('width', firstRow.childNodes[i].getSize().size.x + delta + 'px');
        }, this);
        if (!this.data.length) this.tbody.getFirst().dispose();
    },

    addRecord: function(record, key, currentKey) {

        if (!record) {
            var row = new Element('tr').injectInside(this.tbody);
            return;
        }
        // Проверяем соответствие записи метаданным.
        for (var fieldName in record) {
            if (!this.metadata[fieldName]) {
                alert('Grid: record doesn\'t conform to metadata.');
                return false;
            }
        }

        // Создаем новую строку в таблице.
        var row = new Element('tr').addClass(((key/2)==Math.ceil(key/2))?'odd':'even').setProperty('unselectable', 'on').injectInside(this.tbody);
        // Сохраняем запись в объекте строки.
        row.record = record;

        for (var fieldName in record) {
            // Пропускаем невидимые поля.
            if (!this.metadata[fieldName].visible || this.metadata[fieldName].type == 'hidden') continue;
            var cell = new Element('td').injectInside(row);
            if (this.metadata[fieldName].type == 'boolean') {
                var checkbox = new Element('img').setProperties({
                    'src': 'images/checkbox_' + (record[fieldName] == true ? 'on' : 'off') + '.png',
                    'width': '13', 'height': '13'
                }).injectInside(cell);
                cell.setStyles({ 'text-align': 'center', 'vertical-align': 'middle' });
            }
            else if (this.metadata[fieldName].type == 'image') {
                if(record[fieldName]){
                    var image = new Element('img').setProperties({ 'src': record[fieldName], 'width':50, 'height':50 }).injectInside(cell);
                    cell.setStyles({ 'text-align': 'center', 'vertical-align': 'middle' });
                }
            }
            else {
                var fieldValue = record[fieldName].clean();
                if (fieldValue != '') cell.appendText(fieldValue);
                else cell.set('html', ' ');
            }
        }

        // Помечаем первую ячейку строки.
        row.getFirst().addClass('firstColumn');
		if (currentKey == record[this.keyFieldName]) {
			this.selectItem(row);
			new Fx.Scroll($(document.body).getElement('.gridContainer')).toElement(row);
		}

        var grid = this;
        row.addEvents({
            'mouseover': function() { if (this != grid.getSelectedItem()) this.addClass('highlighted'); },
            'mouseout': function() { this.removeClass('highlighted'); },
            'click': function() { if (this != grid.getSelectedItem()) grid.selectItem(this); },
            'dblclick': function() { this.fireEvent('onDoubleClick'); }.bind(this)
        });

    },

    changeSort: function(fieldIndex) {
        if (typeof this.sortFieldIndex == 'number') {
            if (fieldIndex == this.sortFieldIndex) {
                this.headers[this.sortFieldIndex].removeClass(this.sortOrder);
                this.sortOrder = (this.sortOrder == 'asc' ? 'desc' : 'asc');
                this.headers[this.sortFieldIndex].addClass(this.sortOrder);
            }
            else {
                this.headers[this.sortFieldIndex].removeClass('sortField').removeClass('asc').removeClass('desc');
            }
        }
        if (fieldIndex != this.sortFieldIndex) {
            this.sortFieldIndex = fieldIndex;
            this.sortOrder = 'asc';
            this.headers[this.sortFieldIndex].addClass('sortField').addClass(this.sortOrder);
        }
        this.fireEvent('onSortChange');
    }
});<commit_msg>Fixed bug with value:false in Grid<commit_after>ScriptLoader.load('View.js');

var Grid = new Class({
	Extends: View,
	options:{
	    onSelect: $empty,
	    onSortChange: $empty,
	    onDoubleClick: $empty
    },

    initialize: function(element, options) {
        Asset.css('grid.css');
        this.setOptions(options);
        this.parent(element, this.options);
		this.headOff = this.element.getElement('.gridContainer thead');
		this.headOff.setStyle('display', 'none');
        this.tbody = this.element.getElement('.gridContainer tbody');

    },

    /*
     * {
     *     <fieldName>: {
     *         type: <fieldType>,
     *         [key: true,]
     *         [sort: 'asc'|'desc',]
     *         [visible: true]
     *     },
     *     ...
     * }
     */
    setMetadata: function(metadata) {
        /*
         * Проверяем соответствие видимых полей физической структуре таблицы,
         * определяем имя ключевого поля, и параметры сортировки.
         */
        var visibleFieldsCount = 0;
        for (var fieldName in metadata) {
            if (metadata[fieldName].key) this.keyFieldName = fieldName;
            if (metadata[fieldName].visible) {
                if (typeof metadata[fieldName].sort == 'string') {
                    if (['asc', 'desc'].test(metadata[fieldName].sort)) {
                        this.sortOrder = metadata[fieldName].sort;
                    }
                    this.changeSort(visibleFieldsCount);
                }

                visibleFieldsCount++;
            }
        }

        this.parent(metadata);
    },

    build: function() {
		var preiouslySelectedRecordKey = this.getSelectedRecordKey();

		this.headOff.setStyle('visibility', 'hidden');
		this.headOff.setStyle('display', 'table-header-group');

        if (this.data.length) {
			if(!this.dataKeyExists(preiouslySelectedRecordKey)){
				preiouslySelectedRecordKey = false;
			}
            this.data.each(
            	function(record, key) {
            		this.addRecord(record, key, preiouslySelectedRecordKey);
            	},
            	this
            );
            if (!preiouslySelectedRecordKey) {
				this.selectItem(this.tbody.getFirst());
            }
        }
        else {
            this.addRecord(null);
        }

		var headers = new Array();
		this.headOff.getElements('th').each(function(element, key){
			headers[key] = element.clientWidth;
		})

        this.element.getElements('.gridHeadContainer col').each(function(element, key){
			element.setStyle('width',headers[key]);
		});

        this.element.getElements('.gridContainer col').each(function(element, key){
			element.setStyle('width',headers[key]);
		});

		this.element.getElement('.gridContainer table.gridTable').setStyle('tableLayout', 'fixed');
		this.element.getElement('.gridHeadContainer table.gridTable').setStyle('tableLayout', 'fixed');
		this.headOff.setStyle('display', 'none');

    },

    isEmpty: function() {
        return !this.data.length;
    },

    getSelectedRecord: function() {
        if (!this.getSelectedItem()) return false;
        return this.getSelectedItem().record;
    },

    getSelectedRecordKey: function() {
        if (!this.keyFieldName) return false;
        return this.getSelectedRecord()[this.keyFieldName];
    },

    dataKeyExists: function(key){
		if(!this.data) return false;
		if (!this.keyFieldName) return false;

		return this.data.some(function(item, index){
			return (item[this.keyFieldName]== key);
		}.bind(this));
	},

    getSortFieldName: function() {
        //return this.headers[this.sortFieldIndex].fieldName;
    },

    getSortOrder: function() {
        return this.sortOrder;
    },

    clear: function() {
        this.selectItem(false);
        while (this.tbody.hasChildNodes()) {
            this.tbody.removeChild(this.tbody.firstChild);
        }
    },

    // Private methods:

    fitHeaders: function() {
        this.headersContainer.setStyle('visibility', '');
        var firstRow = this.tbody.getFirst();
        this.headers.each(function(header, i) {
            var delta = -(i == 0 ? 27 : 28);
            // Увеличиваем дельту на 16px (размер полосы прокрутки) если это последняя колонка и грид не пустой.
            if (i == firstRow.childNodes.length-1) {
                delta += ((this.data.length || this.prevDataLength > 0) ? 16 : 0);
                this.prevDataLength = this.data.length;
            }
            header.setStyle('width', firstRow.childNodes[i].getSize().size.x + delta + 'px');
        }, this);
        if (!this.data.length) this.tbody.getFirst().dispose();
    },

    addRecord: function(record, key, currentKey) {

        if (!record) {
            var row = new Element('tr').injectInside(this.tbody);
            return;
        }
        // Проверяем соответствие записи метаданным.
        for (var fieldName in record) {
            if (!this.metadata[fieldName]) {
                alert('Grid: record doesn\'t conform to metadata.');
                return false;
            }
        }

        // Создаем новую строку в таблице.
        var row = new Element('tr').addClass(((key/2)==Math.ceil(key/2))?'odd':'even').setProperty('unselectable', 'on').injectInside(this.tbody);
        // Сохраняем запись в объекте строки.
        row.record = record;

        for (var fieldName in record) {
            // Пропускаем невидимые поля.
            if (!this.metadata[fieldName].visible || this.metadata[fieldName].type == 'hidden') continue;
            var cell = new Element('td').injectInside(row);
            if (this.metadata[fieldName].type == 'boolean') {
                var checkbox = new Element('img').setProperties({
                    'src': 'images/checkbox_' + (record[fieldName] == true ? 'on' : 'off') + '.png',
                    'width': '13', 'height': '13'
                }).injectInside(cell);
                cell.setStyles({ 'text-align': 'center', 'vertical-align': 'middle' });
            }
            else if (this.metadata[fieldName].type == 'image') {
                if(record[fieldName]){
                    var image = new Element('img').setProperties({ 'src': record[fieldName], 'width':50, 'height':50 }).injectInside(cell);
                    cell.setStyles({ 'text-align': 'center', 'vertical-align': 'middle' });
                }
            }
            else {
				var fieldValue = '';
				if (record[fieldName]){
					var fieldValue = record[fieldName].clean();
                }
                if (fieldValue != '') cell.appendText(fieldValue);
                else cell.set('html', ' ');
            }
        }

        // Помечаем первую ячейку строки.
        row.getFirst().addClass('firstColumn');
		if (currentKey == record[this.keyFieldName]) {
			this.selectItem(row);
			new Fx.Scroll($(document.body).getElement('.gridContainer')).toElement(row);
		}

        var grid = this;
        row.addEvents({
            'mouseover': function() { if (this != grid.getSelectedItem()) this.addClass('highlighted'); },
            'mouseout': function() { this.removeClass('highlighted'); },
            'click': function() { if (this != grid.getSelectedItem()) grid.selectItem(this); },
            'dblclick': function() { this.fireEvent('onDoubleClick'); }.bind(this)
        });

    },

    changeSort: function(fieldIndex) {
        if (typeof this.sortFieldIndex == 'number') {
            if (fieldIndex == this.sortFieldIndex) {
                this.headers[this.sortFieldIndex].removeClass(this.sortOrder);
                this.sortOrder = (this.sortOrder == 'asc' ? 'desc' : 'asc');
                this.headers[this.sortFieldIndex].addClass(this.sortOrder);
            }
            else {
                this.headers[this.sortFieldIndex].removeClass('sortField').removeClass('asc').removeClass('desc');
            }
        }
        if (fieldIndex != this.sortFieldIndex) {
            this.sortFieldIndex = fieldIndex;
            this.sortOrder = 'asc';
            this.headers[this.sortFieldIndex].addClass('sortField').addClass(this.sortOrder);
        }
        this.fireEvent('onSortChange');
    }
});<|endoftext|>


<commit_before>/**
 * @license Copyright 2016 The Lighthouse Authors. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
 */
'use strict';

/**
 * @fileoverview An end-to-end test runner for Lighthouse. Takes a set of smoke
 * test definitions and a method of running Lighthouse, returns whether all the
 * smoke tests passed.
 */

/* eslint-disable no-console */

/** @typedef {import('./lib/child-process-error.js').ChildProcessError} ChildProcessError */

/**
 * @typedef Run
 * @property {string[] | undefined} networkRequests
 * @property {LH.Result} lhr
 * @property {LH.Artifacts} artifacts
 * @property {string} lighthouseLog
 * @property {string} assertionLog
 */

/**
 * @typedef SmokehouseResult
 * @property {string} id
 * @property {number} passed
 * @property {number} failed
 * @property {Run[]} runs
 */

import {strict as assert} from 'assert';

import log from 'lighthouse-logger';

import {runLighthouse as cliLighthouseRunner} from './lighthouse-runners/cli.js';
import {getAssertionReport} from './report-assert.js';
import {LocalConsole} from './lib/local-console.js';
import {ConcurrentMapper} from './lib/concurrent-mapper.js';

// The number of concurrent (`!runSerially`) tests to run if `jobs` isn't set.
const DEFAULT_CONCURRENT_RUNS = 5;
const DEFAULT_RETRIES = 0;

/**
 * Runs the selected smoke tests. Returns whether all assertions pass.
 * @param {Array<Smokehouse.TestDfn>} smokeTestDefns
 * @param {Smokehouse.SmokehouseOptions} smokehouseOptions
 * @return {Promise<{success: boolean, testResults: SmokehouseResult[]}>}
 */
async function runSmokehouse(smokeTestDefns, smokehouseOptions) {
  const {
    isDebug,
    useFraggleRock,
    jobs = DEFAULT_CONCURRENT_RUNS,
    retries = DEFAULT_RETRIES,
    lighthouseRunner = cliLighthouseRunner,
    takeNetworkRequestUrls,
  } = smokehouseOptions;
  assertPositiveInteger('jobs', jobs);
  assertNonNegativeInteger('retries', retries);

  // Run each testDefn in parallel based on the concurrencyLimit.
  const concurrentMapper = new ConcurrentMapper();

  const testOptions = {isDebug, useFraggleRock, retries, lighthouseRunner, takeNetworkRequestUrls};
  const smokePromises = smokeTestDefns.map(testDefn => {
    // If defn is set to `runSerially`, we'll run it in succession with other tests, not parallel.
    const concurrency = testDefn.runSerially ? 1 : jobs;
    return concurrentMapper.runInPool(() => runSmokeTest(testDefn, testOptions), {concurrency});
  });
  const testResults = await Promise.all(smokePromises);

  // Print final summary.
  let passingCount = 0;
  let failingCount = 0;
  for (const testResult of testResults) {
    passingCount += testResult.passed;
    failingCount += testResult.failed;
  }
  if (passingCount) console.log(log.greenify(`${getAssertionLog(passingCount)} passing in total`));
  if (failingCount) console.log(log.redify(`${getAssertionLog(failingCount)} failing in total`));

  // Print id(s) and fail if there were failing tests.
  const failingDefns = testResults.filter(result => result.failed);
  if (failingDefns.length) {
    const testNames = failingDefns.map(d => d.id).join(', ');
    console.log(log.redify(`We have ${failingDefns.length} failing smoketest(s): ${testNames}`));
    return {success: false, testResults};
  }

  return {success: true, testResults};
}

/**
 * @param {string} loggableName
 * @param {number} value
 */
function assertPositiveInteger(loggableName, value) {
  if (!Number.isInteger(value) || value <= 0) {
    throw new Error(`${loggableName} must be a positive integer`);
  }
}
/**
 * @param {string} loggableName
 * @param {number} value
 */
function assertNonNegativeInteger(loggableName, value) {
  if (!Number.isInteger(value) || value < 0) {
    throw new Error(`${loggableName} must be a non-negative integer`);
  }
}

/** @param {string} str */
function purpleify(str) {
  return `${log.purple}${str}${log.reset}`;
}

/**
 * Run Lighthouse in the selected runner.
 * @param {Smokehouse.TestDfn} smokeTestDefn
 * @param {{isDebug?: boolean, useFraggleRock?: boolean, retries: number, lighthouseRunner: Smokehouse.LighthouseRunner, takeNetworkRequestUrls?: () => string[]}} testOptions
 * @return {Promise<SmokehouseResult>}
 */
async function runSmokeTest(smokeTestDefn, testOptions) {
  const {id, config: configJson, expectations} = smokeTestDefn;
  const {lighthouseRunner, retries, isDebug, useFraggleRock, takeNetworkRequestUrls} = testOptions;
  const requestedUrl = expectations.lhr.requestedUrl;

  console.log(`${purpleify(id)} smoketest starting…`);

  // Rerun test until there's a passing result or retries are exhausted to prevent flakes.
  /** @type {Run[]} */
  const runs = [];
  let result;
  let report;
  const bufferedConsole = new LocalConsole();
  bufferedConsole.log(`\n${purpleify(id)}: testing '${requestedUrl}'…`);
  for (let i = 0; i <= retries; i++) {
    if (i !== 0) {
      bufferedConsole.log(`  Retrying run (${i} out of ${retries} retries)…`);
    }

    // Run Lighthouse.
    try {
      result = {
        ...await lighthouseRunner(requestedUrl, configJson, {isDebug, useFraggleRock}),
        networkRequests: takeNetworkRequestUrls ? takeNetworkRequestUrls() : undefined,
      };
    } catch (e) {
      // Clear the network requests so that when we retry, we don't see duplicates.
      if (takeNetworkRequestUrls) takeNetworkRequestUrls();

      logChildProcessError(bufferedConsole, e);
      continue; // Retry, if possible.
    }

    // Assert result.
    report = getAssertionReport(result, expectations, {isDebug});

    runs.push({
      ...result,
      lighthouseLog: result.log,
      assertionLog: report.log,
    });

    if (report.failed) {
      bufferedConsole.log(`  ${getAssertionLog(report.failed)} failed.`);
      continue; // Retry, if possible.
    }

    break; // Passing result, no need to retry.
  }

  bufferedConsole.log(`  smoketest results:`);

  // Write result log if we have one.
  if (result) bufferedConsole.write(result.log);

  // If there's not an assertion report, just report the whole thing as a single failure.
  if (report) bufferedConsole.write(report.log);
  const passed = report ? report.passed : 0;
  const failed = report ? report.failed : 1;

  const correctStr = getAssertionLog(passed);
  const colorFn = passed === 0 ? log.redify : log.greenify;
  bufferedConsole.log(`  Correctly passed ${colorFn(correctStr)}`);

  if (failed) {
    const failedString = getAssertionLog(failed);
    bufferedConsole.log(`  Failed ${log.redify(failedString)}`);
  }
  bufferedConsole.log(`${purpleify(id)} smoketest complete.`);

  // Log now so right after finish, but all at once so not interleaved with other tests.
  console.log(bufferedConsole.getLog());

  return {
    id,
    passed,
    failed,
    runs,
  };
}

/**
 * Logs an error to the console, including stdout and stderr if `err` is a
 * `ChildProcessError`.
 * @param {LocalConsole} localConsole
 * @param {ChildProcessError|Error} err
 */
function logChildProcessError(localConsole, err) {
  if ('stdout' in err && 'stderr' in err) {
    localConsole.adoptStdStrings(err);
  }

  localConsole.log(log.redify('Error: ') + err + '\n' + err.stack);
}

/**
 * @param {number} count
 * @return {string}
 */
function getAssertionLog(count) {
  const plural = count === 1 ? '' : 's';
  return `${count} assertion${plural}`;
}

/**
 * Parses the cli `shardArg` flag into `shardNumber/shardTotal`. Splits
 * `testDefns` into `shardTotal` shards and returns the `shardNumber`th shard.
 * Shards will differ in size by at most 1.
 * Shard params must be 1 ≤ shardNumber ≤ shardTotal.
 * @param {Array<Smokehouse.TestDfn>} testDefns
 * @param {string=} shardArg
 * @return {Array<Smokehouse.TestDfn>}
 */
function getShardedDefinitions(testDefns, shardArg) {
  if (!shardArg) return testDefns;

  // eslint-disable-next-line max-len
  const errorMessage = `'shard' must be of the form 'n/d' and n and d must be positive integers with 1 ≤ n ≤ d. Got '${shardArg}'`;
  const match = /^(?<shardNumber>\d+)\/(?<shardTotal>\d+)$/.exec(shardArg);
  assert(match && match.groups, errorMessage);
  const shardNumber = Number(match.groups.shardNumber);
  const shardTotal = Number(match.groups.shardTotal);
  assert(shardNumber > 0 && Number.isInteger(shardNumber), errorMessage);
  assert(shardTotal > 0 && Number.isInteger(shardTotal));
  assert(shardNumber <= shardTotal, errorMessage);

  // Array is sharded with `Math.ceil(length / shardTotal)` shards first
  // and then the remaining `Math.floor(length / shardTotal) shards.
  // e.g. `[0, 1, 2, 3]` split into 3 shards is `[[0, 1], [2], [3]]`.
  const baseSize = Math.floor(testDefns.length / shardTotal);
  const biggerSize = baseSize + 1;
  const biggerShardCount = testDefns.length % shardTotal;

  // Since we don't have tests for this file, construct all shards so correct
  // structure can be asserted.
  const shards = [];
  let index = 0;
  for (let i = 0; i < shardTotal; i++) {
    const shardSize = i < biggerShardCount ? biggerSize : baseSize;
    shards.push(testDefns.slice(index, index + shardSize));
    index += shardSize;
  }
  assert.equal(shards.length, shardTotal);
  assert.deepEqual(shards.flat(), testDefns);

  const shardDefns = shards[shardNumber - 1];
  console.log(`In this shard (${shardArg}), running: ${shardDefns.map(d => d.id).join(' ')}\n`);
  return shardDefns;
}

export {
  runSmokehouse,
  getShardedDefinitions,
};
<commit_msg>misc: fix build-smokehouse-bundle build (#13500)<commit_after>/**
 * @license Copyright 2016 The Lighthouse Authors. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
 */
'use strict';

/**
 * @fileoverview An end-to-end test runner for Lighthouse. Takes a set of smoke
 * test definitions and a method of running Lighthouse, returns whether all the
 * smoke tests passed.
 */

/* eslint-disable no-console */

/** @typedef {import('./lib/child-process-error.js').ChildProcessError} ChildProcessError */

/**
 * @typedef Run
 * @property {string[] | undefined} networkRequests
 * @property {LH.Result} lhr
 * @property {LH.Artifacts} artifacts
 * @property {string} lighthouseLog
 * @property {string} assertionLog
 */

/**
 * @typedef SmokehouseResult
 * @property {string} id
 * @property {number} passed
 * @property {number} failed
 * @property {Run[]} runs
 */

import assert from 'assert';

import log from 'lighthouse-logger';

import {runLighthouse as cliLighthouseRunner} from './lighthouse-runners/cli.js';
import {getAssertionReport} from './report-assert.js';
import {LocalConsole} from './lib/local-console.js';
import {ConcurrentMapper} from './lib/concurrent-mapper.js';

// The number of concurrent (`!runSerially`) tests to run if `jobs` isn't set.
const DEFAULT_CONCURRENT_RUNS = 5;
const DEFAULT_RETRIES = 0;

/**
 * Runs the selected smoke tests. Returns whether all assertions pass.
 * @param {Array<Smokehouse.TestDfn>} smokeTestDefns
 * @param {Smokehouse.SmokehouseOptions} smokehouseOptions
 * @return {Promise<{success: boolean, testResults: SmokehouseResult[]}>}
 */
async function runSmokehouse(smokeTestDefns, smokehouseOptions) {
  const {
    isDebug,
    useFraggleRock,
    jobs = DEFAULT_CONCURRENT_RUNS,
    retries = DEFAULT_RETRIES,
    lighthouseRunner = cliLighthouseRunner,
    takeNetworkRequestUrls,
  } = smokehouseOptions;
  assertPositiveInteger('jobs', jobs);
  assertNonNegativeInteger('retries', retries);

  // Run each testDefn in parallel based on the concurrencyLimit.
  const concurrentMapper = new ConcurrentMapper();

  const testOptions = {isDebug, useFraggleRock, retries, lighthouseRunner, takeNetworkRequestUrls};
  const smokePromises = smokeTestDefns.map(testDefn => {
    // If defn is set to `runSerially`, we'll run it in succession with other tests, not parallel.
    const concurrency = testDefn.runSerially ? 1 : jobs;
    return concurrentMapper.runInPool(() => runSmokeTest(testDefn, testOptions), {concurrency});
  });
  const testResults = await Promise.all(smokePromises);

  // Print final summary.
  let passingCount = 0;
  let failingCount = 0;
  for (const testResult of testResults) {
    passingCount += testResult.passed;
    failingCount += testResult.failed;
  }
  if (passingCount) console.log(log.greenify(`${getAssertionLog(passingCount)} passing in total`));
  if (failingCount) console.log(log.redify(`${getAssertionLog(failingCount)} failing in total`));

  // Print id(s) and fail if there were failing tests.
  const failingDefns = testResults.filter(result => result.failed);
  if (failingDefns.length) {
    const testNames = failingDefns.map(d => d.id).join(', ');
    console.log(log.redify(`We have ${failingDefns.length} failing smoketest(s): ${testNames}`));
    return {success: false, testResults};
  }

  return {success: true, testResults};
}

/**
 * @param {string} loggableName
 * @param {number} value
 */
function assertPositiveInteger(loggableName, value) {
  if (!Number.isInteger(value) || value <= 0) {
    throw new Error(`${loggableName} must be a positive integer`);
  }
}
/**
 * @param {string} loggableName
 * @param {number} value
 */
function assertNonNegativeInteger(loggableName, value) {
  if (!Number.isInteger(value) || value < 0) {
    throw new Error(`${loggableName} must be a non-negative integer`);
  }
}

/** @param {string} str */
function purpleify(str) {
  return `${log.purple}${str}${log.reset}`;
}

/**
 * Run Lighthouse in the selected runner.
 * @param {Smokehouse.TestDfn} smokeTestDefn
 * @param {{isDebug?: boolean, useFraggleRock?: boolean, retries: number, lighthouseRunner: Smokehouse.LighthouseRunner, takeNetworkRequestUrls?: () => string[]}} testOptions
 * @return {Promise<SmokehouseResult>}
 */
async function runSmokeTest(smokeTestDefn, testOptions) {
  const {id, config: configJson, expectations} = smokeTestDefn;
  const {lighthouseRunner, retries, isDebug, useFraggleRock, takeNetworkRequestUrls} = testOptions;
  const requestedUrl = expectations.lhr.requestedUrl;

  console.log(`${purpleify(id)} smoketest starting…`);

  // Rerun test until there's a passing result or retries are exhausted to prevent flakes.
  /** @type {Run[]} */
  const runs = [];
  let result;
  let report;
  const bufferedConsole = new LocalConsole();
  bufferedConsole.log(`\n${purpleify(id)}: testing '${requestedUrl}'…`);
  for (let i = 0; i <= retries; i++) {
    if (i !== 0) {
      bufferedConsole.log(`  Retrying run (${i} out of ${retries} retries)…`);
    }

    // Run Lighthouse.
    try {
      result = {
        ...await lighthouseRunner(requestedUrl, configJson, {isDebug, useFraggleRock}),
        networkRequests: takeNetworkRequestUrls ? takeNetworkRequestUrls() : undefined,
      };
    } catch (e) {
      // Clear the network requests so that when we retry, we don't see duplicates.
      if (takeNetworkRequestUrls) takeNetworkRequestUrls();

      logChildProcessError(bufferedConsole, e);
      continue; // Retry, if possible.
    }

    // Assert result.
    report = getAssertionReport(result, expectations, {isDebug});

    runs.push({
      ...result,
      lighthouseLog: result.log,
      assertionLog: report.log,
    });

    if (report.failed) {
      bufferedConsole.log(`  ${getAssertionLog(report.failed)} failed.`);
      continue; // Retry, if possible.
    }

    break; // Passing result, no need to retry.
  }

  bufferedConsole.log(`  smoketest results:`);

  // Write result log if we have one.
  if (result) bufferedConsole.write(result.log);

  // If there's not an assertion report, just report the whole thing as a single failure.
  if (report) bufferedConsole.write(report.log);
  const passed = report ? report.passed : 0;
  const failed = report ? report.failed : 1;

  const correctStr = getAssertionLog(passed);
  const colorFn = passed === 0 ? log.redify : log.greenify;
  bufferedConsole.log(`  Correctly passed ${colorFn(correctStr)}`);

  if (failed) {
    const failedString = getAssertionLog(failed);
    bufferedConsole.log(`  Failed ${log.redify(failedString)}`);
  }
  bufferedConsole.log(`${purpleify(id)} smoketest complete.`);

  // Log now so right after finish, but all at once so not interleaved with other tests.
  console.log(bufferedConsole.getLog());

  return {
    id,
    passed,
    failed,
    runs,
  };
}

/**
 * Logs an error to the console, including stdout and stderr if `err` is a
 * `ChildProcessError`.
 * @param {LocalConsole} localConsole
 * @param {ChildProcessError|Error} err
 */
function logChildProcessError(localConsole, err) {
  if ('stdout' in err && 'stderr' in err) {
    localConsole.adoptStdStrings(err);
  }

  localConsole.log(log.redify('Error: ') + err + '\n' + err.stack);
}

/**
 * @param {number} count
 * @return {string}
 */
function getAssertionLog(count) {
  const plural = count === 1 ? '' : 's';
  return `${count} assertion${plural}`;
}

/**
 * Parses the cli `shardArg` flag into `shardNumber/shardTotal`. Splits
 * `testDefns` into `shardTotal` shards and returns the `shardNumber`th shard.
 * Shards will differ in size by at most 1.
 * Shard params must be 1 ≤ shardNumber ≤ shardTotal.
 * @param {Array<Smokehouse.TestDfn>} testDefns
 * @param {string=} shardArg
 * @return {Array<Smokehouse.TestDfn>}
 */
function getShardedDefinitions(testDefns, shardArg) {
  if (!shardArg) return testDefns;

  // eslint-disable-next-line max-len
  const errorMessage = `'shard' must be of the form 'n/d' and n and d must be positive integers with 1 ≤ n ≤ d. Got '${shardArg}'`;
  const match = /^(?<shardNumber>\d+)\/(?<shardTotal>\d+)$/.exec(shardArg);
  assert(match && match.groups, errorMessage);
  const shardNumber = Number(match.groups.shardNumber);
  const shardTotal = Number(match.groups.shardTotal);
  assert(shardNumber > 0 && Number.isInteger(shardNumber), errorMessage);
  assert(shardTotal > 0 && Number.isInteger(shardTotal));
  assert(shardNumber <= shardTotal, errorMessage);

  // Array is sharded with `Math.ceil(length / shardTotal)` shards first
  // and then the remaining `Math.floor(length / shardTotal) shards.
  // e.g. `[0, 1, 2, 3]` split into 3 shards is `[[0, 1], [2], [3]]`.
  const baseSize = Math.floor(testDefns.length / shardTotal);
  const biggerSize = baseSize + 1;
  const biggerShardCount = testDefns.length % shardTotal;

  // Since we don't have tests for this file, construct all shards so correct
  // structure can be asserted.
  const shards = [];
  let index = 0;
  for (let i = 0; i < shardTotal; i++) {
    const shardSize = i < biggerShardCount ? biggerSize : baseSize;
    shards.push(testDefns.slice(index, index + shardSize));
    index += shardSize;
  }
  assert.strictEqual(shards.length, shardTotal);
  assert.deepStrictEqual(shards.flat(), testDefns);

  const shardDefns = shards[shardNumber - 1];
  console.log(`In this shard (${shardArg}), running: ${shardDefns.map(d => d.id).join(' ')}\n`);
  return shardDefns;
}

export {
  runSmokehouse,
  getShardedDefinitions,
};
<|endoftext|>