<commit_before>///////

var questions = [];
var resposta = new Object();

function new_question(descricao, foto, medico, caritativo, social){
  questions.push({
    descricao: descricao,
    foto: "background-image: url('images/" + foto +"')",
    medico: medico,
    caritativo: caritativo,
    social: social
  });
};

new_question(
  "Moça em cadeiras de rodas tentando subir numa calçada.",
  "situacao1.jpg",
  "Essa moça devia procurar um médico, pois deve haver um tratamento para que ela volte a andar.",
  "Que pena, essa moça está presa a uma cadeira de rodas e sofre para se locomover pela cidade.",
  "A sociedade pode eliminar todas as barreiras para que essa moça se locomova pela cidade.");

new_question(
  "Criança com deficiência intelectual chegando na escola levada pela família.",
  "situacao2.jpg",
  "Ela precisa estudar numa escola especial. Deve existir algum remédio para melhorar a cabeça dela.",
  "Ela precisa viver numa instituição onde sempre tenha alguém para cuidar dela.",
  "Ela precisa estudar numa escola convencional, junto com as demais crianças, com e sem deficiência.");

new_question(
  "Mãe conversando com a filha surda em libras.",
  "situacao3.jpg",
  "Deve haver uma operação ou um aparelho auditivo que vai ajudar essa menina.",
  "Deve ser muito difícil ter uma filha surda que sempre vai precisar de ajuda.",
  "Queria saber como elas conseguem se comunicar tão bem!");

new_question(
  "Mulher cega andando pelas ruas de uma cidade com cão-guia.",
  "situacao4.jpg",
  "Será que não há alguma nova tecnologia que possa ajudar essa mulher a enxergar normal, como todos nós?",
  "Como podem deixar essa mulher sair sozinha na rua? Ela precisa de alguém para auxiliá-la.",
  "Será que esta mulher precisa de ajuda para atravessar a rua? Vou perguntar.");

new_question(
  "Pessoas sem deficiência e uma com deficiência física e acompanhante numa piscina.",
  "situacao5.jpg",
  "O ambiente aquático é um ótimo espaço para reabilitação.",
  "Que bom que ele está acompanhado para que não corra riscos.",
  "Que piscina linda! Eles vão se divertir!");

var nQuestions = questions.length;

var descricao = $(".description");
var foto = $(".scenes");
var medico = $(".medico");
var caritativo = $(".caritativo");
var social = $(".social");

function initQuestion(){
  atual = i+1;
  anterior = i;
  $(".scenes").removeClass("situacao" + anterior);
  $(".scenes").addClass("situacao" + atual);
  descricao.focus();

  descricao.text(questions[i].descricao);
  foto.attr("style",questions[i].foto);
  medico.text(questions[i].medico);
  caritativo.text(questions[i].caritativo);
  social.text(questions[i].social);
  i++;
  return i;
}

///////

var feedback = $(".feedback");
var navTitle = $("nav h1");
var intro = $(".intro");
var quiz = $(".quiz")
var score = $(".score");
var alternative = $(".alternatives button");
var btnStart = $(".btn-start");

function initRea(){

  console.log("chamando initRea");

  feedback.hide();
  quiz.hide();

  btnStart.on("click", function(){
    window.setTimeout(function(){
      intro.fadeOut();
      quiz.show();
      navTitle.fadeIn();
      // remove tabindex de logos após início
      $("nav *").attr("tabindex", "-1");
    }, 500);

    console.log("preenchendo dataInicio");
    resposta.ra = $("#ra").val();
    resposta.dataInicio = Date();
    console.log(resposta);
  })

}

var maxColor = 255;
var colorByQuestion = Math.floor(maxColor/nQuestions);

var r = 0;
var g = 0;
var b = 0;
score.css("background-color", "rgb("+r+","+g+","+b+")");

function changeColor(r = r, g = g, b = b){
  score.css("background-color", "rgb("+r+","+g+","+b+")");
};

function showFeedback(){

  console.log("showFeedback");
  resposta.dataTermino = Date();

  if(r/colorByQuestion == 1){
    var valueCaritativo = r/colorByQuestion + " situação"
  } else {
    var valueCaritativo = r/colorByQuestion + " situações"
  }
  resposta.caritativo = r/colorByQuestion;

  if(g/colorByQuestion == 1){
    var valueMedico = g/colorByQuestion + " situação"
  } else {
    var valueMedico = g/colorByQuestion + " situações"
  }
  resposta.medico = g/colorByQuestion;

  if(b/colorByQuestion == 1){
    var valueSocial = b/colorByQuestion + " situação"
  } else {
    var valueSocial = b/colorByQuestion + " situações"
  }
  resposta.social = b/colorByQuestion;

  feedback.fadeIn();
  feedback.css("background-color","rgb("+r+","+g+","+b+")");
  $(".feedbackMedico span").text(valueMedico);
  $(".feedbackCaritativo span").text(valueCaritativo);
  $(".feedbackSocial span").text(valueSocial);

  console.log(resposta);

  $.ajax({
    type: "POST",
    url: "submit",
    data: resposta
  })
  .done(function(data) {
    console.log("data");
    console.log(data);
  })
  .fail(function (jqXHR, textStatus, error) {
    console.log("jqXHR");
    console.log(jqXHR);
    console.log("textStatus");
    console.log(textStatus);
    console.log("error");
    console.log(error);
  })

};

function checkClick() {
  window.setTimeout(function(){

    if (clickCounter < nQuestions) {
      randomOrder();
      initQuestion();
      score.css("box-shadow","0 0 0 20px rgba("+r+","+g+","+b+", .5)");

    } else {
      var delay = 750;
      alternative.unbind();
      score.css("box-shadow","0 0 0 2500px rgba("+r+","+g+","+b+", 1)");
      window.setTimeout(function(){
        showFeedback();
        window.setTimeout(function(){
          quiz.remove();
        }, delay)
      }, delay);
    }

  }, 500);
  clickCounter++;
}
var clickCounter = 0;

/// Ordem randômica de questões

function randomOrder(){
  var random = Math.random();
  var aMedico = $(".alternatives .medico");
  var aCaritativo = $(".alternatives .caritativo");
  var aSocial = $(".alternatives .social");

  if(random <= 0.16){
    aMedico.css("order","3");
    aCaritativo.css("order","1");
    aSocial.css("order","2");
  } else if(random > 0.16 && random <= 0.32){
    aMedico.css("order","2");
    aCaritativo.css("order","1");
    aSocial.css("order","3");
  } else if(random > 0.32 && random <= 0.48){
    aMedico.css("order","2");
    aCaritativo.css("order","3");
    aSocial.css("order","1");
  } else if(random > 0.48 && random <= 0.64){
    aMedico.css("order","1");
    aCaritativo.css("order","3");
    aSocial.css("order","2");
  } else if(random > 0.64 && random <= 0.8){
    aMedico.css("order","3");
    aCaritativo.css("order","2");
    aSocial.css("order","1");
  } else {
    aMedico.css("order","1");
    aCaritativo.css("order","2");
    aSocial.css("order","3");
  }
};

///////

initRea();

var i = 0;
initQuestion();

alternative.on("click", function(){

  score.css("box-shadow","none");
  $(this).blur();

  if($(this).hasClass("medico")){
    g = g + colorByQuestion;
  } else if($(this).hasClass("caritativo")){
    r = r + colorByQuestion;
  } else if($(this).hasClass("social")){
    b = b + colorByQuestion;
  }
  changeColor(r, g, b);
  checkClick();

});

////
<commit_msg>Preventing multiple clicks<commit_after>///////

var questions = [];
var resposta = new Object();
var clicked = false;

function new_question(descricao, foto, medico, caritativo, social){
  questions.push({
    descricao: descricao,
    foto: "background-image: url('images/" + foto +"')",
    medico: medico,
    caritativo: caritativo,
    social: social
  });
};

new_question(
  "Moça em cadeiras de rodas tentando subir numa calçada.",
  "situacao1.jpg",
  "Essa moça devia procurar um médico, pois deve haver um tratamento para que ela volte a andar.",
  "Que pena, essa moça está presa a uma cadeira de rodas e sofre para se locomover pela cidade.",
  "A sociedade pode eliminar todas as barreiras para que essa moça se locomova pela cidade.");

new_question(
  "Criança com deficiência intelectual chegando na escola levada pela família.",
  "situacao2.jpg",
  "Ela precisa estudar numa escola especial. Deve existir algum remédio para melhorar a cabeça dela.",
  "Ela precisa viver numa instituição onde sempre tenha alguém para cuidar dela.",
  "Ela precisa estudar numa escola convencional, junto com as demais crianças, com e sem deficiência.");

new_question(
  "Mãe conversando com a filha surda em libras.",
  "situacao3.jpg",
  "Deve haver uma operação ou um aparelho auditivo que vai ajudar essa menina.",
  "Deve ser muito difícil ter uma filha surda que sempre vai precisar de ajuda.",
  "Queria saber como elas conseguem se comunicar tão bem!");

new_question(
  "Mulher cega andando pelas ruas de uma cidade com cão-guia.",
  "situacao4.jpg",
  "Será que não há alguma nova tecnologia que possa ajudar essa mulher a enxergar normal, como todos nós?",
  "Como podem deixar essa mulher sair sozinha na rua? Ela precisa de alguém para auxiliá-la.",
  "Será que esta mulher precisa de ajuda para atravessar a rua? Vou perguntar.");

new_question(
  "Pessoas sem deficiência e uma com deficiência física e acompanhante numa piscina.",
  "situacao5.jpg",
  "O ambiente aquático é um ótimo espaço para reabilitação.",
  "Que bom que ele está acompanhado para que não corra riscos.",
  "Que piscina linda! Eles vão se divertir!");

var nQuestions = questions.length;

var descricao = $(".description");
var foto = $(".scenes");
var medico = $(".medico");
var caritativo = $(".caritativo");
var social = $(".social");

function initQuestion(){
  atual = i+1;
  anterior = i;
  $(".scenes").removeClass("situacao" + anterior);
  $(".scenes").addClass("situacao" + atual);
  descricao.focus();

  descricao.text(questions[i].descricao);
  foto.attr("style",questions[i].foto);
  medico.text(questions[i].medico);
  caritativo.text(questions[i].caritativo);
  social.text(questions[i].social);
  i++;
  clicked = false;
  return i;
}

///////

var feedback = $(".feedback");
var navTitle = $("nav h1");
var intro = $(".intro");
var quiz = $(".quiz")
var score = $(".score");
var alternative = $(".alternatives button");
var btnStart = $(".btn-start");

function initRea(){

  feedback.hide();
  quiz.hide();

  btnStart.on("click", function(){
    window.setTimeout(function(){
      intro.fadeOut();
      quiz.show();
      navTitle.fadeIn();
      // remove tabindex de logos após início
      $("nav *").attr("tabindex", "-1");
    }, 500);

    resposta.ra = $("#ra").val();
    resposta.dataInicio = Date();
  })
}

var maxColor = 255;
var colorByQuestion = Math.floor(maxColor/nQuestions);

var r = 0;
var g = 0;
var b = 0;
score.css("background-color", "rgb("+r+","+g+","+b+")");

function changeColor(r = r, g = g, b = b){
  score.css("background-color", "rgb("+r+","+g+","+b+")");
};

function showFeedback(){

  resposta.dataTermino = Date();

  if(r/colorByQuestion == 1){
    var valueCaritativo = r/colorByQuestion + " situação"
  } else {
    var valueCaritativo = r/colorByQuestion + " situações"
  }
  resposta.caritativo = r/colorByQuestion;

  if(g/colorByQuestion == 1){
    var valueMedico = g/colorByQuestion + " situação"
  } else {
    var valueMedico = g/colorByQuestion + " situações"
  }
  resposta.medico = g/colorByQuestion;

  if(b/colorByQuestion == 1){
    var valueSocial = b/colorByQuestion + " situação"
  } else {
    var valueSocial = b/colorByQuestion + " situações"
  }
  resposta.social = b/colorByQuestion;

  feedback.fadeIn();
  feedback.css("background-color","rgb("+r+","+g+","+b+")");
  $(".feedbackMedico span").text(valueMedico);
  $(".feedbackCaritativo span").text(valueCaritativo);
  $(".feedbackSocial span").text(valueSocial);

  submitResponse(resposta);
};

function checkClick() {
  window.setTimeout(function(){

    if (clickCounter < nQuestions) {
      randomOrder();
      initQuestion();
      score.css("box-shadow","0 0 0 20px rgba("+r+","+g+","+b+", .5)");

    } else {
      var delay = 750;
      alternative.unbind();
      score.css("box-shadow","0 0 0 2500px rgba("+r+","+g+","+b+", 1)");
      window.setTimeout(function(){
        showFeedback();
        window.setTimeout(function(){
          quiz.remove();
        }, delay)
      }, delay);
    }

  }, 500);
  clickCounter++;
}
var clickCounter = 0;

/// Ordem randômica de questões

function randomOrder(){
  var random = Math.random();
  var aMedico = $(".alternatives .medico");
  var aCaritativo = $(".alternatives .caritativo");
  var aSocial = $(".alternatives .social");

  if(random <= 0.16){
    aMedico.css("order","3");
    aCaritativo.css("order","1");
    aSocial.css("order","2");
  } else if(random > 0.16 && random <= 0.32){
    aMedico.css("order","2");
    aCaritativo.css("order","1");
    aSocial.css("order","3");
  } else if(random > 0.32 && random <= 0.48){
    aMedico.css("order","2");
    aCaritativo.css("order","3");
    aSocial.css("order","1");
  } else if(random > 0.48 && random <= 0.64){
    aMedico.css("order","1");
    aCaritativo.css("order","3");
    aSocial.css("order","2");
  } else if(random > 0.64 && random <= 0.8){
    aMedico.css("order","3");
    aCaritativo.css("order","2");
    aSocial.css("order","1");
  } else {
    aMedico.css("order","1");
    aCaritativo.css("order","2");
    aSocial.css("order","3");
  }
};

///////

function submitResponse(obj) {
  $.ajax({
    type: "POST",
    url: "submit",
    data: obj
  })
  .done(function(data) {
    console.log("data");
    console.log(data);
  })
  .fail(function (jqXHR, textStatus, error) {
    console.log("jqXHR");
    console.log(jqXHR);
    console.log("textStatus");
    console.log(textStatus);
    console.log("error");
    console.log(error);
  })
}

///////

initRea();

var i = 0;
initQuestion();

alternative.on("click", function(){
  if (!clicked) {
    clicked = true;
    score.css("box-shadow","none");
    $(this).blur();

    if($(this).hasClass("medico")){
      g = g + colorByQuestion;
    } else if($(this).hasClass("caritativo")){
      r = r + colorByQuestion;
    } else if($(this).hasClass("social")){
      b = b + colorByQuestion;
    }
    changeColor(r, g, b);
    checkClick();
  }
});

////
<|endoftext|>


<commit_before>bbop.core.require('bbop', 'core');
bbop.core.require('bbop', 'model');
bbop.core.require('bbop', 'model', 'tree');
bbop.core.namespace('bbop', 'render', 'phylo');

// see default_config for a description of possible config settings
bbop.render.phylo.renderer = function (element_id, config){
    var elt_id = element_id;
    this.parentElem = document.getElementById(elt_id);

    var default_config = {
        // these are in pixels
        // height of the leaf node box
        box_height: 31,
        // vertical space between leaf boxes
        box_spacing: 12,
        // space between leaf box edge and leaf label
        leaf_padding: 4,
        // leaf border thickness
        leaf_border: 2,

        // leaf border color
        leaf_border_color: "blue",
        // font for leaf labels
        // if you set this, you probably also want to change
        // box_height or leaf_padding
        leaf_font: "16px sans-serif"
    };

    this.config = ("object" == typeof config
                   ? bbop.core.merge(default_config, config)
                   : default_config);

    this.node_style = {
        position: "absolute",
        background: "white",
        "z-index": 2,
        width: "8px",
        height: "8px",
        "margin-top": "-4px",
        "margin-left": "-4px",
        border: "1px solid black"
    };

    this.leaf_style = {
        position: "absolute",
        background: "#f1f1ff",
        padding: this.config.leaf_padding + "px",
        font: this.config.leaf_font,
        height: ( this.config.box_height
                  - (2 * this.config.leaf_padding)
                  - (2 * this.config.leaf_border) ) + "px",
        "border-radius": "5px"
    };

    if( this.config.leaf_border > 0 ){
        this.leaf_style.border =
            this.config.leaf_border + "px solid "
            + this.config.leaf_border_color;
    }

    this.connection_style = {
        position: "absolute",
        "border-left": "2px solid black",
        "border-top": "2px solid black",
        "border-bottom": "2px solid black",
        "border-right": "none"
    };

    ///
    /// Functions to handle internal graph management.
    ///

    var node_cache_hash = {};
    this._graph = new bbop.model.tree.graph();

    this.add_node = function(unique_id){
	var new_node = new bbop.model.tree.node(unique_id, unique_id);
	node_cache_hash[unique_id] = new_node;
	this._graph.add_node(new_node);
    };

    this.add_edge = function(nid1, nid2, dist){

	var retval = false;

	var n1 = node_cache_hash[nid1];
	var n2 = node_cache_hash[nid2];
	if( n1 && n2 ){
	    var new_edge = new bbop.model.tree.edge(n1, n2, dist);
	    this._graph.add_edge(new_edge);
	    retval = true;
	}

	return retval;
    };

    this.display = function () {
	var layout = this._graph.layout();
        if( this.container ){
            this.parentElem.removeChild(this.container);
        }
        this.container = document.createElement("div");
        this.container.style.cssText = [
            "position: absolute",
            "top: 0px",
            "left: 0px",
            "margin: 0px",
            "padding: 0px"
        ].join(";") + ";";

        // x_scale will be percentage units
	var x_scale = 100 / layout.max_distance;

        // row_height is in pixel units
	this.row_height = this.config.box_height + this.config.box_spacing;

        this.tree_height =
            (layout.max_width * this.row_height)
            - this.config.box_spacing;

        // the position values from the tree layout are center
        // positions, and the very top one has a y-position of 0.
        // if a leaf node box is centered at that point, then the
        // top half of the top box would get cut off.  So we move
        // all of the positions down by top_margin pixels.
        var top_margin = (this.config.box_height / 2);

	// Add phynodes and create lookup (hash) for use with connections.
	var phynodes = new Array();
	var phynode_hash = {};
        var leaves = new Array();

        var node_style = css_string(this.node_style);
        var leaf_style = css_string(this.leaf_style);

	for( var nidi = 0; nidi < layout.node_list.length; nidi++ ){
	    // Calculate position.
	    var node_id = layout.node_list[nidi];
	    var lpx = (layout.position_x[node_id] * x_scale);
	    var lpy =
                (layout.position_y[node_id] * this.row_height)
                + top_margin;

	    // Create node at place.
            var phynode = new graph_pnode(node_id, lpx, lpy,
                                          node_style, leaf_style,
                                          this.config.box_height,
                                          this._graph.is_leaf_node(node_id));
            this.container.appendChild(phynode.node_elem);

	    if( this._graph.is_leaf_node(node_id) ) leaves.push(phynode);
            phynodes.push(phynode);
	    phynode_hash[node_id] = phynode;
	}

        var conn_style = css_string(this.connection_style);
	for( var ei = 0; ei < layout.edge_list.length; ei++ ){
	    var edge = layout.edge_list[ei];
            var child_phynode = phynode_hash[edge[1]];
            child_phynode.set_parent(
                phynode_hash[edge[0]],
                conn_style
            );
            this.container.appendChild(child_phynode.conn_elem);
        }

        this.parentElem.style.cssText =
            "height: " + this.tree_height + "px;";
        this.container.style.height = this.tree_height + "px";
        this.parentElem.appendChild(this.container);

        // We wait until here to get the leaf box widths.
        // We don't want to do thatt while we're creating
        // the leaf DOM nodes because that would trigger
        // more browser layouts.
        this.max_leaf_width = 0;
        for( var li = 0; li < leaves.length; li++ ){
            this.max_leaf_width = Math.max(
                leaves[li].node_elem.offsetWidth,
                this.max_leaf_width
            );
        }
        this.width_changed()
    }

    this.width_changed = function() {
	this._render_frame_width = this.parentElem.offsetWidth;
        this.container.style.width =
            (this._render_frame_width - this.max_leaf_width) + "px"
    };

    ///
    /// graphical representation of a phylo node
    ///

    var graph_pnode = function(label, px, py,
                               node_style, leaf_style,
                               height, is_leaf){
        this.label = label;
        this.px = px;
        this.py = py;
        this.height = height;
        this.is_leaf = is_leaf;

        var node_elem = document.createElement("div");
        if( is_leaf ){
            node_elem.appendChild(document.createTextNode(label));
            node_elem.style.cssText =
                leaf_style + ";"
                + "left: " + px + "%;"
            // move the top coord of the box up by height/2;
            // this makes the box vertically centered on the given
            // (px, py) position
                + "top: " + (py - (height / 2)) + "px;";
        }else{
            node_elem.title = label;
            node_elem.style.cssText =
                node_style + ";"
                + "left: " + px + "%;"
                + "top: " + py + "px;";
        }
        this.node_elem = node_elem;
    };

    graph_pnode.prototype.set_parent = function(parent, conn_style) {
        this.parent = parent;
        this.conn_elem = document.createElement("div");

        var this_style =
            "top: " + Math.min(parent.py, this.py) + "px;"
            + "left: " + parent.px + "%;"
            + "width: " + (this.px - parent.px) + "%;"
            + "height: " + Math.abs(parent.py - this.py) + "px;";

        if( this.py < parent.py ){
            // upward connection
            this_style += "border-bottom: none;";
        }else{
            // downward connection
            this_style += "border-top: none;";
        }
        this.conn_elem.style.cssText = conn_style + this_style;
    };

    ///
    /// Util functions
    ///

    // takes an object like {"top": "10px", "left": "5px"}
    // and return a string like "top: 10px; left: 5px;"
    function css_string(css_object) {
        var result = "";
        if( "object" == typeof css_object ){
            for( var key in css_object ){
                result += key + ":" + css_object[key] + ";";
            }
        }
        return result;
    }
};
<commit_msg>faster layout<commit_after>bbop.core.require('bbop', 'core');
bbop.core.require('bbop', 'model');
bbop.core.require('bbop', 'model', 'tree');
bbop.core.namespace('bbop', 'render', 'phylo');

(function() {

bbop.render.phylo.divrenderer = divrenderer;

// will decide later whether to throw away, export, or move this
bbop.render.phylo.tree = tree;

// see default_config for a description of possible config settings
function divrenderer(element_id, config){
    var elt_id = element_id;
    this.parentElem = document.getElementById(elt_id);

    var default_config = {
        // these are in pixels
        // height of the leaf node box
        box_height: 31,
        // vertical space between leaf boxes
        box_spacing: 12,
        // space between leaf box edge and leaf label
        leaf_padding: 4,
        // leaf border thickness
        leaf_border: 2,
        // width/height of internal nodes
        node_size: 8,

        // leaf border color
        leaf_border_color: "blue",
        // font for leaf labels
        // if you set this, you probably also want to change
        // box_height or leaf_padding
        leaf_font: "16px sans-serif"
    };

    this.config = ("object" == typeof config
                   ? bbop.core.merge(default_config, config)
                   : default_config);

    this.node_style = {
        position: "absolute",
        background: "white",
        "z-index": 2,
        width: this.config.node_size + "px",
        height: this.config.node_size + "px",
        "margin-top": (-this.config.node_size / 2) + "px",
        "margin-left": (-this.config.node_size / 2) + "px",
        border: "1px solid black"
    };

    this.leaf_style = {
        position: "absolute",
        background: "#f1f1ff",
        padding: this.config.leaf_padding + "px",
        font: this.config.leaf_font,
        height: ( this.config.box_height
                  - (2 * this.config.leaf_padding)
                  - (2 * this.config.leaf_border) ) + "px",
        "border-radius": "5px"
    };

    if( this.config.leaf_border > 0 ){
        this.leaf_style.border =
            this.config.leaf_border + "px solid "
            + this.config.leaf_border_color;
    }

    this.connection_style = {
        position: "absolute",
        "border-left": "2px solid black",
        "border-top": "2px solid black",
        "border-bottom": "2px solid black",
        "border-right": "none"
    };

    this.tree = new tree();
};

divrenderer.prototype.add_node = function(unique_id){
    this.tree.add_node(unique_id, unique_id);
};

divrenderer.prototype.add_edge = function(nid1, nid2, dist){
    this.tree.add_edge(nid1, nid2, dist);
};

divrenderer.prototype.display = function () {
    var leaf_counts = {};
    this.tree.traverse(
        function() {}, null,
        function(node, child_results, down_data) {
            // if this is a leaf, call the leaf count 1
            var leaf_count = node.is_leaf() ? 1 : sum(child_results);
            leaf_counts[node.id] = leaf_count;
            return leaf_count;
        }
    );

    this.tree.sort_children(function(a, b) {
        // ladderize down
        //return leaf_counts[b.id] - leaf_counts[a.id];

        // ladderize up
        //return leaf_counts[a.id] - leaf_counts[b.id];

        // alphabetical
        if( a.id == b.id ) return 0;
        return (a.id < b.id) ? -1 : 1;
    });

    var layout = this.tree.layout();

    if( this.container ) this.parentElem.removeChild(this.container);

    this.container = document.createElement("div");
    this.container.style.cssText = [
        "position: absolute",
        "top: 0px",
        "margin: 0px",
        "padding: 0px"
    ].join(";") + ";";

    // x_scale will be percentage units
    var x_scale = 100 / this.tree.max_distance();

    // row_height is in pixel units
    this.row_height = this.config.box_height + this.config.box_spacing;

    // the position values from the tree layout are center
    // positions, and the very top one has a y-position of 0.
    // if a leaf node box is centered at that point, then the
    // top half of the top box would get cut off.  So we move
    // all of the positions down by top_margin pixels.
    var top_margin = (this.config.box_height / 2);

    // Add phynodes and create lookup (hash) for use with connections.
    var phynodes = new Array();
    var phynode_hash = {};
    var leaves = new Array();

    var node_style = css_string(this.node_style);
    var leaf_style = css_string(this.leaf_style);

    for( var nidi = 0; nidi < layout.length; nidi++ ){
	// Calculate position.
	var node_pos = layout[nidi];
	var lpx = (node_pos.x * x_scale);
	var lpy =
            (node_pos.y * this.row_height)
            + top_margin;
        var is_leaf = this.tree.nodes[node_pos.id].is_leaf();

	// Create node at place.
        var phynode = new graph_pnode(node_pos.id, lpx, lpy,
                                      node_style, leaf_style,
                                      this.config.box_height,
                                      is_leaf);
        this.container.appendChild(phynode.node_elem);

	if( is_leaf ) leaves.push(phynode);
        phynodes.push(phynode);
	phynode_hash[node_pos.id] = phynode;
    }

    var conn_style = css_string(this.connection_style);
    var container = this.container;
    this.tree.iterate_edges(function(parent, child) {
        var child_phynode = phynode_hash[child.id];
        child_phynode.set_parent(
            phynode_hash[parent.id],
            conn_style
        );
        container.appendChild(child_phynode.conn_elem);
    });

    this.tree_height =
        (leaves.length * this.row_height)
        - this.config.box_spacing;

    this.parentElem.style.height = this.tree_height + "px;";
    this.container.style.left = (this.config.node_size / 2) + "px",
    this.container.style.height = this.tree_height + "px";
    this.parentElem.appendChild(this.container);

    // We wait until here to get the leaf box widths.
    // We don't want to do thatt while we're creating
    // the leaf DOM nodes because that would trigger
    // more browser layouts.
    this.max_leaf_width = 0;
    for( var li = 0; li < leaves.length; li++ ){
        this.max_leaf_width = Math.max(
            leaves[li].node_elem.offsetWidth,
            this.max_leaf_width
        );
    }
    this.width_changed()
};

// call this when the width of the tree's parent element changes
divrenderer.prototype.width_changed = function() {
    this._render_frame_width = this.parentElem.offsetWidth;
    this.container.style.width =
        (this._render_frame_width
         - this.max_leaf_width
         - (this.config.node_size / 2)) + "px"
};

///
/// graphical representation of a phylo node
///

function graph_pnode(label, px, py,
                     node_style, leaf_style,
                     height, is_leaf){
    this.label = label;
    this.px = px;
    this.py = py;
    this.height = height;
    this.is_leaf = is_leaf;

    var node_elem = document.createElement("div");
    if( is_leaf ){
        node_elem.appendChild(document.createTextNode(label));
        node_elem.style.cssText =
            leaf_style + ";"
            + "left: " + px + "%;"
        // move the top coord of the box up by height/2;
        // this makes the box vertically centered on the given
        // (px, py) position
            + "top: " + (py - (height / 2)) + "px;";
    }else{
        node_elem.title = label;
        node_elem.style.cssText =
            node_style + ";"
            + "left: " + px + "%;"
            + "top: " + py + "px;";
    }

    this.node_elem = node_elem;
};

graph_pnode.prototype.set_parent = function(parent, conn_style) {
    this.parent = parent;
    this.conn_elem = document.createElement("div");

    var this_style =
        "top: " + Math.min(parent.py, this.py) + "px;"
        + "left: " + parent.px + "%;"
        + "width: " + (this.px - parent.px) + "%;"
        + "height: " + Math.abs(parent.py - this.py) + "px;";

    if( this.py < parent.py ){
        // upward connection
        this_style += "border-bottom: none;";
    }else{
        // downward connection
        this_style += "border-top: none;";
    }
    this.conn_elem.style.cssText = conn_style + this_style;
};

///
/// a tree with distances on each edge
///

function tree() {
    this.nodes = {};
    this._roots = null;
};

tree.prototype.add_node = function(id, label) {
    this.nodes[id] = new node(id, label);
};

tree.prototype.add_edge = function(parent_id, child_id, distance) {
    var parent = this.nodes[parent_id];
    var child = this.nodes[child_id];
    if( "undefined" == typeof parent ){
        throw "parent node " + parent_id + " not found";
    }
    if( "undefined" == typeof child ){
        throw "child node " + child_id + " not found";
    }
    parent.add_child(child, distance);
};

tree.prototype.sort_children = function(compare_fun) {
    var roots = this.roots();
    roots.sort(compare_fun);
    for (var i = 0; i < roots.length; i++) {
        roots[i].sort_children(compare_fun);
    }
};

tree.prototype.iterate_nodes = function(fun) {
    for( var id in this.nodes ){
        fun(this.nodes[id]);
    }
};

tree.prototype.iterate_edges = function(fun) {
    for( var id in this.nodes ){
        var parent = this.nodes[id];
        for( var i = 0; i < parent.children.length; i++ ){
            fun(parent, parent.children[i]);
        }
    }
};

tree.prototype.roots = function() {
    if (this._roots) return this._roots;
    var roots = [];
    this.iterate_nodes(function(node) {
        if( ! node.has_parent() ) roots.push(node);
    });
    this._roots = roots;
    return this._roots;
};

tree.prototype.traverse = function(down_fun, down_data, up_aggregator) {
    var roots = this.roots();
    var results = [];
    for (var i = 0; i < roots.length; i++) {
        results.push(roots[i].traverse(down_fun, down_data, up_aggregator));
    }
    return results;
};

tree.prototype.max_distance = function() {
    return max(this.traverse(
        function(node, down_data) {
            return down_data + node.parent_distance;
        },
        0,
        function(node, child_results, down_data) {
            return Math.max(down_data, max(child_results));
        }
    ) );
};

tree.prototype.layout = function() {
    var leaf_counter = 0;
    return [].concat.apply([], this.traverse(
        function(node, down_data) {
            return down_data + node.parent_distance;
        },
        0,
        function(node, child_results, down_data) {
            var immediate_child_y_sum = 0;
            var descendant_positions;
            for (var i = 0; i < child_results.length; i++) {
                // child_results will be an array of
                // arrays of position objects, where
                // the position object at index 0
                // is the position of the immediate child
                // (see how result is constructed below)
                immediate_child_y_sum += child_results[i][0].y;
            }

            // flatten child result arrays
            var result = [].concat.apply([], child_results);
            result.unshift({
                id: node.id,
                x: down_data,
                y: ( node.is_leaf()
                     ? leaf_counter++
                     : ( immediate_child_y_sum
                         / child_results.length ) )
            });
            return result;
        }
    ) );
};

///
/// tree node
///

function node(id, label) {
    this.id = id;
    this.label = label;
    this.parent = null;
    this.parent_distance = 0;
    this.children = [];
};

node.prototype.add_child = function(child_node, distance) {
    child_node.parent_distance = distance;
    child_node.parent = this;
    this.children.push(child_node);
};

node.prototype.is_leaf = function() {
    return 0 == this.children.length
};

node.prototype.has_parent = function() {
    return null != this.parent;
};

node.prototype.sort_children = function(compare_fun) {
    for( var i = 0; i < this.children.length; i++ ){
        this.children[i].sort_children(compare_fun);
    }
    this.children.sort(compare_fun);
};

// traverse down and then up the tree, passing some data down
// at each step, and aggregating traversal results from the
// children on the way up
// down_fun: gets called on each node on the way down
//           arguments: node, down_data
// down_data: starting value for the data to pass down
//            (down_fun on a root gets this value for down_data)
// up_aggregator: function to aggregate results on the way back up
//                arguments: node, child_results, down_data
node.prototype.traverse = function(down_fun, down_data, up_aggregator) {
    down_data = down_fun(this, down_data);

    var child_results = [];
    for( var i = 0; i < this.children.length; i++ ){
        child_results.push(
            this.children[i].traverse(down_fun,
                                      down_data,
                                      up_aggregator)
        );
    }

    return up_aggregator(this, child_results, down_data);
};

///
/// Util functions
///

// takes an object like {"top": "10px", "left": "5px"}
// and return a string like "top: 10px; left: 5px;"
function css_string(css_object) {
    var result = "";
    if( "object" == typeof css_object ){
        for( var key in css_object ){
            result += key + ":" + css_object[key] + ";";
        }
    }
    return result;
}

function max(list) {
    if (0 == list.length) return null;
    var result = list[0];
    for( var i = 1; i < list.length; i++ ){
        if( list[i] > result ) result = list[i];
    }
    return result;
}

function min(list) {
    if (0 == list.length) return null;
    var result = list[0];
    for( var i = 1; i < list.length; i++ ){
        if( list[i] < result ) result = list[i];
    }
    return result;
}

function sum(list) {
    var result = 0;
    for( var i = 1; i < list.length; i++ ){
        result += list[i];
    }
    return result;
}

})();
<|endoftext|>


<commit_before>// Static file handler. Serves all of the files from `public/root` directory
// under the main application root path.

'use strict';

const path        = require('path');
const send        = require('send');
const Promise     = require('bluebird');
const onFinished  = require('on-finished');

module.exports = function (N) {
  var root = path.join(__dirname, '../../../../static');

  N.validate('server_bin:nodeca-site.static', {
    // DON'T validate unknown params - those can exists,
    // if someone requests '/myfile.txt?xxxx' instead of '/myfile.txt/
    additionalProperties: true,
    properties: {
      path: {
        type: 'string',
        required: true
      }
    }
  });

  N.wire.on('server_bin:nodeca-site.static', async function static_file_send(env) {
    var req = env.origin.req,
        res = env.origin.res;

    if (req.method !== 'GET' && req.method !== 'HEAD') throw N.io.BAD_REQUEST;

    await new Promise((resolve, reject) => {
      let ss = send(req, env.params.path, { root, index: false, maxAge: '1y' });

      // Errors with status are not fatal,
      // rethrow those up as code, not as Error
      ss.on('error', err => reject(err.status || err));

      onFinished(res, () => {
        if (res.statusCode) env.status = res.statusCode;
        resolve();
      });

      ss.pipe(res);
    });
  });
};
<commit_msg>Remove on-finished module<commit_after>// Static file handler. Serves all of the files from `public/root` directory
// under the main application root path.

'use strict';

const path        = require('path');
const send        = require('send');
const stream      = require('stream');

module.exports = function (N) {
  var root = path.join(__dirname, '../../../../static');

  N.validate('server_bin:nodeca-site.static', {
    // DON'T validate unknown params - those can exists,
    // if someone requests '/myfile.txt?xxxx' instead of '/myfile.txt/
    additionalProperties: true,
    properties: {
      path: {
        type: 'string',
        required: true
      }
    }
  });

  N.wire.on('server_bin:nodeca-site.static', async function static_file_send(env) {
    var req = env.origin.req,
        res = env.origin.res;

    if (req.method !== 'GET' && req.method !== 'HEAD') throw N.io.BAD_REQUEST;

    await new Promise((resolve, reject) => {
      let ss = send(req, env.params.path, { root, index: false, maxAge: '1y' });

      // Errors with status are not fatal,
      // rethrow those up as code, not as Error
      ss.on('error', err => reject(err.status || err));

      stream.finished(res, () => {
        if (res.statusCode) env.status = res.statusCode;
        resolve();
      });

      ss.pipe(res);
    });
  });
};
<|endoftext|>


<commit_before>'use strict';

var height = $(window).height();
var width = $(window).width();

var game = new Phaser.Game(width, height, Phaser.AUTO, 'aws-serverless-samfarm', {
    preload: preload,
    create: create,
    update: update
});

var sprites;
var DESIRED_SAM_COUNT = 1;
var CURRENT_SAM_COUNT = 1;
var MAX_SAM_CHANGE = 10;
var LAST_CHANGE_TIME = Date.now();
var GET_SAM_COUNT_URL =  'https://1yhfbnimr1.execute-api.us-east-1.amazonaws.com/Prod/sam';
var TIME_BETWEEN_COUNT_UPDATE_MS = 4000;

function preload() {
    game.load.spritesheet('spinner', 'squirrel.png', 64, 64);
    game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
}

function create() {
    game.stage.disableVisibilityChange = true;
    game.stage.backgroundColor = '#967da7';
    sprites = game.add.physicsGroup(Phaser.Physics.ARCADE);
    createSprites(DESIRED_SAM_COUNT);
}

function createSprites(numberOfSprites) {
    for (var i = 0; i < numberOfSprites; i++) {
        var initX = game.rnd.integerInRange(100, width - 100);
        var initY = game.rnd.integerInRange(32, height - 32);
        var velX = game.rnd.integerInRange(-200, 200);
        var velY = game.rnd.integerInRange(-200, 200);

        var s = sprites.create(initX, initY, 'spinner');
        s.body.velocity.set(velX, velY);
        s.scale.setTo(1.4, 1.4);
    }

    sprites.setAll('body.collideWorldBounds', true);
    sprites.setAll('body.bounce.x', 1);
    sprites.setAll('body.bounce.y', 1);
}

function update() {
    game.physics.arcade.collide(sprites);

    var countToChange = DESIRED_SAM_COUNT - CURRENT_SAM_COUNT;

    samCountUpdate(countToChange);
}

function samCountUpdate(samCountChange) {

    var currTime = Date.now();

    // Only update the number of SAMs on the screen every second
    if (currTime - LAST_CHANGE_TIME > 1000) {

        // If we are increasing the amount of SAMs on the screen, take the min of the requested change value and
        // the max number that can be updated at a time.
        if (samCountChange > 0) {
            samCountChange = Math.min(samCountChange, MAX_SAM_CHANGE);

            createSprites(samCountChange);
            CURRENT_SAM_COUNT += samCountChange;
            LAST_CHANGE_TIME = currTime;

        } else if (samCountChange < 0) {
            // Else if we are decreasing the amount of SAMs on the screen, take the max of the negative change value and
            // the maximum that can be changed at a time
            samCountChange = Math.abs(Math.max(samCountChange, -MAX_SAM_CHANGE));

            // For each of the SAMs that are being removed, kill it's sprite
            for (var i = 0; i < samCountChange; i++) {
                sprites.children[i].kill();
            }

            // Remove killed SAMs from the array
            sprites.children.splice(0, samCountChange);

            CURRENT_SAM_COUNT -= samCountChange;
            LAST_CHANGE_TIME = currTime;
        }
    }
}

// Every X seconds as defined in the variable TIME_BETWEEN_COUNT_UPDATE_MS, call our API endpoint to tell use how many
// SAMs should be on the screen.
setInterval(() => {
    // Make frontend call to lambda
    $.get({
        url: GET_SAM_COUNT_URL
    }).done(function(data) {
        DESIRED_SAM_COUNT = data;
    });

}, TIME_BETWEEN_COUNT_UPDATE_MS);
<commit_msg>smena na pi Pro/sam ap endpoint<commit_after>'use strict';

var height = $(window).height();
var width = $(window).width();

var game = new Phaser.Game(width, height, Phaser.AUTO, 'aws-serverless-samfarm', {
    preload: preload,
    create: create,
    update: update
});

var sprites;
var DESIRED_SAM_COUNT = 1;
var CURRENT_SAM_COUNT = 1;
var MAX_SAM_CHANGE = 10 ;
var LAST_CHANGE_TIME = Date.now();
var GET_SAM_COUNT_URL =  'https://1yhfbnimr1.execute-api.us-east-1.amazonaws.com/Stage/sam';
var TIME_BETWEEN_COUNT_UPDATE_MS = 4000;

function preload() {
    game.load.spritesheet('spinner', 'squirrel.png', 64, 64);
    game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
}

function create() {
    game.stage.disableVisibilityChange = true;
    game.stage.backgroundColor = '#967da7';
    sprites = game.add.physicsGroup(Phaser.Physics.ARCADE);
    createSprites(DESIRED_SAM_COUNT);
}

function createSprites(numberOfSprites) {
    for (var i = 0; i < numberOfSprites; i++) {
        var initX = game.rnd.integerInRange(100, width - 100);
        var initY = game.rnd.integerInRange(32, height - 32);
        var velX = game.rnd.integerInRange(-200, 200);
        var velY = game.rnd.integerInRange(-200, 200);

        var s = sprites.create(initX, initY, 'spinner');
        s.body.velocity.set(velX, velY);
        s.scale.setTo(1.4, 1.4);
    }

    sprites.setAll('body.collideWorldBounds', true);
    sprites.setAll('body.bounce.x', 1);
    sprites.setAll('body.bounce.y', 1);
}

function update() {
    game.physics.arcade.collide(sprites);

    var countToChange = DESIRED_SAM_COUNT - CURRENT_SAM_COUNT;

    samCountUpdate(countToChange);
}

function samCountUpdate(samCountChange) {

    var currTime = Date.now();

    // Only update the number of SAMs on the screen every second
    if (currTime - LAST_CHANGE_TIME > 1000) {

        // If we are increasing the amount of SAMs on the screen, take the min of the requested change value and
        // the max number that can be updated at a time.
        if (samCountChange > 0) {
            samCountChange = Math.min(samCountChange, MAX_SAM_CHANGE);

            createSprites(samCountChange);
            CURRENT_SAM_COUNT += samCountChange;
            LAST_CHANGE_TIME = currTime;

        } else if (samCountChange < 0) {
            // Else if we are decreasing the amount of SAMs on the screen, take the max of the negative change value and
            // the maximum that can be changed at a time
            samCountChange = Math.abs(Math.max(samCountChange, -MAX_SAM_CHANGE));

            // For each of the SAMs that are being removed, kill it's sprite
            for (var i = 0; i < samCountChange; i++) {
                sprites.children[i].kill();
            }

            // Remove killed SAMs from the array
            sprites.children.splice(0, samCountChange);

            CURRENT_SAM_COUNT -= samCountChange;
            LAST_CHANGE_TIME = currTime;
        }
    }
}

// Every X seconds as defined in the variable TIME_BETWEEN_COUNT_UPDATE_MS, call our API endpoint to tell use how many
// SAMs should be on the screen.
setInterval(() => {
    // Make frontend call to lambda
    $.get({
        url: GET_SAM_COUNT_URL
    }).done(function(data) {
        DESIRED_SAM_COUNT = data;
    });

}, TIME_BETWEEN_COUNT_UPDATE_MS);
<|endoftext|>


<commit_before>angular.module('starter', ['ionic', 'ngCordova', 'starter.controllers', 'starter.services', 'hc.marked'])
  .run(function ($ionicPlatform) {
    $ionicPlatform.ready(function () {
      if (typeof analytics !== 'undefined'){
        analytics.startTrackerWithId('UA-71907748-1');
        analytics.trackView('Android App')
      } else {
        console.log("Google Analytics plugin could not be loaded.")
      }
      if (window.cordova && window.cordova.plugins.Keyboard) {
        cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
        cordova.plugins.Keyboard.disableScroll(true);

      }
      if (window.StatusBar) {
        // org.apache.cordova.statusbar required
        //StatusBar.styleDefault();
        StatusBar.backgroundColorByHexString("#387ef5");
      }
    });
  })
  .config(['markedProvider', function (markedProvider) {
    marked.Lexer.rules.gfm.heading = marked.Lexer.rules.normal.heading;
    marked.Lexer.rules.tables.heading = marked.Lexer.rules.normal.heading;

    markedProvider.setOptions({
      gfm: true,
      tables: true,
      breaks: false,
      pedantic: false,
      sanitize: true,
      smartLists: true,
      smartypants: false,
      highlight: function (code, lang) {
        if (lang) {
          return hljs.highlight(lang, code, true).value;
        } else {
          return hljs.highlightAuto(code).value;
        }
      }
    });
    markedProvider.setRenderer({
      heading: function (text, level) {
        var escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
        return '<h' + level + '>' +
          text + '</h' + level + '>';
      },
      image: function (href) {
        return "";
      }
    });
  }])
  .config(function ($stateProvider, $urlRouterProvider) {
    $stateProvider

      .state('app', {
        url: '/app',
        abstract: true,
        templateUrl: 'templates/menu.html',
        controller: 'AppCtrl'
      })

      .state('app.main', {
        url: '/main',
        views: {
          'menuContent': {
            templateUrl: 'templates/main.html'
          }
        }
      })

      .state('app.wiki', {
        url: "/wiki",
        views: {
          'menuContent': {
            templateUrl: "templates/game/wiki.html",
            controller: 'WikiCtrl'
          }
        }
      })
      .state('app.language', {
        url: "/language/:language_info",
        views: {
          'menuContent': {
            templateUrl: "templates/game/wiki_detail.html",
            controller: 'WikiDetailCtrl'
          }
        }
      })
      .state('app.levelSelect', {
        url: "/level",
        views: {
          'menuContent': {
            templateUrl: "templates/game/level.html",
            controller: 'LevelSelectCtrl'
          }
        }
      })

      .state('app.single', {
        url: "/level/:level",
        views: {
          'menuContent': {
            templateUrl: "templates/game/level_quiz.html",
            controller: 'QuizCtrl'
          }
        }
      })

      .state('app.about', {
        url: '/about',
        views: {
          'menuContent': {
            templateUrl: 'templates/about.html'
          }
        }
      })

      .state('app.skilltree', {
        url: '/skilltree',
        views: {
          'menuContent': {
            templateUrl: 'templates/skilltree.html'
          }
        }
      })

      .state('app.article', {
        url: '/article/:slug',
        views: {
          'menuContent': {
            templateUrl: 'templates/read/article-detail.html',
            controller: 'ArticleCtrl'
          }
        }
      })

      .state('app.articles', {
        url: '/articles',
        views: {
          'menuContent': {
            templateUrl: 'templates/read/article-list.html',
            controller: 'ArticleListCtrl'
          }
        }
      })

      .state('app.review', {
        url: '/review/:slug',
        views: {
          'menuContent': {
            templateUrl: 'templates/read/review-detail.html',
            controller: 'ReviewCtrl'
          }
        }
      })

      .state('app.reviews', {
        url: '/reviews',
        views: {
          'menuContent': {
            templateUrl: 'templates/read/review-list.html',
            controller: 'ReviewListCtrl'
          }
        }
      })

      .state('app.book', {
        url: '/book',
        views: {
          'menuContent': {
            templateUrl: 'templates/read/book.html'
          }
        }
      })

      .state('app.seven', {
        url: '/seven',
        views: {
          'menuContent': {
            templateUrl: 'templates/more/seven.html'
          }
        }
      })

      .state('app.faq', {
        url: '/faq',
        views: {
          'menuContent': {
            templateUrl: 'templates/more/faq.html'
          }
        }
      })

      .state('app.feedback', {
        url: '/feedback',
        views: {
          'menuContent': {
            templateUrl: 'templates/more/feedback.html',
            controller: 'FeedbackCtrl'
          }
        }
      })

      .state('app.exam', {
        url: '/exam',
        views: {
          'menuContent': {
            templateUrl: 'templates/exam.html'
          }
        }
      })

      .state('app.day1', {
        url: '/day/1',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day1.html',
            controller: 'DayCtrl'
          }
        }
      })
      .state('app.day2', {
        url: '/day/2',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day2.html',
            controller: 'DayCtrl'
          }
        }
      })
      .state('app.day3', {
        url: '/day/3',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day3.html',
            controller: 'DayCtrl'
          }
        }
      })
      .state('app.day4', {
        url: '/day/4',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day4.html',
            controller: 'DayCtrl'
          }
        }
      })
      .state('app.day5', {
        url: '/day/5',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day5.html',
            controller: 'DayCtrl'
          }
        }
      })
      .state('app.day6', {
        url: '/day/6',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day6.html',
            controller: 'DayCtrl'
          }
        }
      })
      .state('app.day7', {
        url: '/day/7',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day7.html',
            controller: 'DayCtrl'
          }
        }
      })

      .state('app.prog', {
        url: '/books/prog',
        views: {
          'menuContent': {
            templateUrl: 'templates/books/prog.html'
          }
        }
      })
      .state('app.arch', {
        url: '/books/arch',
        views: {
          'menuContent': {
            templateUrl: 'templates/books/arch.html'
          }
        }
      })
      .state('app.method', {
        url: '/books/method',
        views: {
          'menuContent': {
            templateUrl: 'templates/books/method.html'
          }
        }
      })
      .state('app.think', {
        url: '/books/think',
        views: {
          'menuContent': {
            templateUrl: 'templates/books/think.html'
          }
        }
      })

      .state('app.quiz', {
        url: '/quiz/:slug',
        views: {
          'menuContent': {
            templateUrl: 'templates/game/quiz.html',
            controller: 'AllQuizCtrl'
          }
        }
      })
    ;

    $urlRouterProvider.otherwise('/app/main');
  });
<commit_msg>update track view<commit_after>angular.module('starter', ['ionic', 'ngCordova', 'starter.controllers', 'starter.services', 'hc.marked'])
  .run(function ($ionicPlatform) {
    $ionicPlatform.ready(function () {
      if (typeof analytics !== 'undefined'){
        analytics.startTrackerWithId('UA-71907748-1');
        analytics.trackView('Growth')
      } else {
        console.log("Google Analytics plugin could not be loaded.")
      }
      if (window.cordova && window.cordova.plugins.Keyboard) {
        cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
        cordova.plugins.Keyboard.disableScroll(true);

      }
      if (window.StatusBar) {
        // org.apache.cordova.statusbar required
        //StatusBar.styleDefault();
        StatusBar.backgroundColorByHexString("#387ef5");
      }
    });
  })
  .config(['markedProvider', function (markedProvider) {
    marked.Lexer.rules.gfm.heading = marked.Lexer.rules.normal.heading;
    marked.Lexer.rules.tables.heading = marked.Lexer.rules.normal.heading;

    markedProvider.setOptions({
      gfm: true,
      tables: true,
      breaks: false,
      pedantic: false,
      sanitize: true,
      smartLists: true,
      smartypants: false,
      highlight: function (code, lang) {
        if (lang) {
          return hljs.highlight(lang, code, true).value;
        } else {
          return hljs.highlightAuto(code).value;
        }
      }
    });
    markedProvider.setRenderer({
      heading: function (text, level) {
        var escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
        return '<h' + level + '>' +
          text + '</h' + level + '>';
      },
      image: function (href) {
        return "";
      }
    });
  }])
  .config(function ($stateProvider, $urlRouterProvider) {
    $stateProvider

      .state('app', {
        url: '/app',
        abstract: true,
        templateUrl: 'templates/menu.html',
        controller: 'AppCtrl'
      })

      .state('app.main', {
        url: '/main',
        views: {
          'menuContent': {
            templateUrl: 'templates/main.html'
          }
        }
      })

      .state('app.wiki', {
        url: "/wiki",
        views: {
          'menuContent': {
            templateUrl: "templates/game/wiki.html",
            controller: 'WikiCtrl'
          }
        }
      })
      .state('app.language', {
        url: "/language/:language_info",
        views: {
          'menuContent': {
            templateUrl: "templates/game/wiki_detail.html",
            controller: 'WikiDetailCtrl'
          }
        }
      })
      .state('app.levelSelect', {
        url: "/level",
        views: {
          'menuContent': {
            templateUrl: "templates/game/level.html",
            controller: 'LevelSelectCtrl'
          }
        }
      })

      .state('app.single', {
        url: "/level/:level",
        views: {
          'menuContent': {
            templateUrl: "templates/game/level_quiz.html",
            controller: 'QuizCtrl'
          }
        }
      })

      .state('app.about', {
        url: '/about',
        views: {
          'menuContent': {
            templateUrl: 'templates/about.html'
          }
        }
      })

      .state('app.skilltree', {
        url: '/skilltree',
        views: {
          'menuContent': {
            templateUrl: 'templates/skilltree.html'
          }
        }
      })

      .state('app.article', {
        url: '/article/:slug',
        views: {
          'menuContent': {
            templateUrl: 'templates/read/article-detail.html',
            controller: 'ArticleCtrl'
          }
        }
      })

      .state('app.articles', {
        url: '/articles',
        views: {
          'menuContent': {
            templateUrl: 'templates/read/article-list.html',
            controller: 'ArticleListCtrl'
          }
        }
      })

      .state('app.review', {
        url: '/review/:slug',
        views: {
          'menuContent': {
            templateUrl: 'templates/read/review-detail.html',
            controller: 'ReviewCtrl'
          }
        }
      })

      .state('app.reviews', {
        url: '/reviews',
        views: {
          'menuContent': {
            templateUrl: 'templates/read/review-list.html',
            controller: 'ReviewListCtrl'
          }
        }
      })

      .state('app.book', {
        url: '/book',
        views: {
          'menuContent': {
            templateUrl: 'templates/read/book.html'
          }
        }
      })

      .state('app.seven', {
        url: '/seven',
        views: {
          'menuContent': {
            templateUrl: 'templates/more/seven.html'
          }
        }
      })

      .state('app.faq', {
        url: '/faq',
        views: {
          'menuContent': {
            templateUrl: 'templates/more/faq.html'
          }
        }
      })

      .state('app.feedback', {
        url: '/feedback',
        views: {
          'menuContent': {
            templateUrl: 'templates/more/feedback.html',
            controller: 'FeedbackCtrl'
          }
        }
      })

      .state('app.exam', {
        url: '/exam',
        views: {
          'menuContent': {
            templateUrl: 'templates/exam.html'
          }
        }
      })

      .state('app.day1', {
        url: '/day/1',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day1.html',
            controller: 'DayCtrl'
          }
        }
      })
      .state('app.day2', {
        url: '/day/2',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day2.html',
            controller: 'DayCtrl'
          }
        }
      })
      .state('app.day3', {
        url: '/day/3',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day3.html',
            controller: 'DayCtrl'
          }
        }
      })
      .state('app.day4', {
        url: '/day/4',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day4.html',
            controller: 'DayCtrl'
          }
        }
      })
      .state('app.day5', {
        url: '/day/5',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day5.html',
            controller: 'DayCtrl'
          }
        }
      })
      .state('app.day6', {
        url: '/day/6',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day6.html',
            controller: 'DayCtrl'
          }
        }
      })
      .state('app.day7', {
        url: '/day/7',
        views: {
          'menuContent': {
            templateUrl: 'templates/days/day7.html',
            controller: 'DayCtrl'
          }
        }
      })

      .state('app.prog', {
        url: '/books/prog',
        views: {
          'menuContent': {
            templateUrl: 'templates/books/prog.html'
          }
        }
      })
      .state('app.arch', {
        url: '/books/arch',
        views: {
          'menuContent': {
            templateUrl: 'templates/books/arch.html'
          }
        }
      })
      .state('app.method', {
        url: '/books/method',
        views: {
          'menuContent': {
            templateUrl: 'templates/books/method.html'
          }
        }
      })
      .state('app.think', {
        url: '/books/think',
        views: {
          'menuContent': {
            templateUrl: 'templates/books/think.html'
          }
        }
      })

      .state('app.quiz', {
        url: '/quiz/:slug',
        views: {
          'menuContent': {
            templateUrl: 'templates/game/quiz.html',
            controller: 'AllQuizCtrl'
          }
        }
      })
    ;

    $urlRouterProvider.otherwise('/app/main');
  });
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import pureRender from 'pure-render-decorator'
import { Map, TileLayer } from 'react-leaflet'
import { Circle } from 'leaflet'
import EarthquakesCanvasLayer from './earthquakes-canvas-layer'
import EarthquakePopup from './earthquake-popup'
import VolcanoPopup from './volcano-popup'
import {PlatesLayerS, PlatesLayerC} from './plates-layer'
import VolcanosLayer from './volcanos-layer'
import PlateMovementLayer from './plate-movement-layer'
import CrossSectionDrawLayer from './cross-section-draw-layer'
import addTouchSupport from '../custom-leaflet/touch-support'
import { mapLayer } from '../map-layer-tiles'
import log from '../logger'
import config from '../config'

import '../../css/leaflet/leaflet.css'
import '../../css/seismic-eruptions-map.less'

const INITIAL_BOUNDS = [
  [config.minLat, config.minLng],
  [config.maxLat, config.maxLng]
]

// It delays download of earthquakes data on map moveend event, so user can pan or zoom map
// a few times quickly before the download starts.
const EARTQUAKES_DOWNLOAD_DELAY = 600 // ms

// Leaflet map doesn't support custom touch events by default.
addTouchSupport()

var zoomLevel = 2;

@pureRender
export default class SeismicEruptionsMap extends Component {
  constructor(props) {
    super(props)
    this.state = {
      selectedEarthquake: null,
      selectedVolcano: null
    }
    this.handleMoveStart = this.handleMoveStart.bind(this)
    this.handleEarthquakeClick = this.handleEarthquakeClick.bind(this)
    this.handleEarthquakePopupClose = this.handleEarthquakePopupClose.bind(this)
    this.handleVolcanoClick = this.handleVolcanoClick.bind(this)
    this.handleVolcanoPopupClose = this.handleVolcanoPopupClose.bind(this)
    this.handleMoveEnd = this.handleMoveEnd.bind(this)
    this.handleZoomEnd = this.handleZoomEnd.bind(this)
  }

  get map() {
    return this.refs.map.getLeafletElement()
  }

  latLngToPoint(latLng) {
    return this.map.latLngToContainerPoint(latLng)
  }

  componentDidMount() {
    // Make sure that SVG overlay layer exists from the very beginning. It's created dynamically when the first
    // SVG object is added to the map. It solves / simplifies some issues. For example we don't want earthquakes
    // to be clickable when we enter cross-section drawing mode. The simplest solution is to set Canvas z-index
    // lower than SVG z-index, but it won't work if the SVG layer hasn't been created yet.
    createSVGOverlayLayer(this.map)
    // Initially Leaflet always triggers mapmove event, as probably real bounds are a bit different than
    // provided bounds (due to screen size etc.). That is causing mark2DViewModified to be called and reset view icon
    // being shown. This is unwanted, as there has been no user interaction. Mark this view unmodified again.
    const { mark2DViewModified } = this.props
    mark2DViewModified(false)
  }

  componentDidUpdate() {
    const { selectedEarthquake } = this.state
    const { earthquakes } = this.props
    if (selectedEarthquake) {
      let found = false
      // Check if selected earthquake is still present and visible.
      earthquakes.forEach(e => {
        if (e.id === selectedEarthquake.id && e.visible) found = true
      })
      // Reset it if not.
      if (!found) this.setState({selectedEarthquake: null})
    }
  }

  handleMoveStart() {
    this._mapBeingDragged = true
    const { mark2DViewModified } = this.props
    mark2DViewModified(true)
  }

  handleMoveEnd(event) {
    this._mapBeingDragged = false

    clearTimeout(this._tilesDownloadTimoutID)
    this._tilesDownloadTimoutID = setTimeout(() => {
      const { updateEarthquakesData } = this.props
      const map = event.target
      const bounds = map.getBounds()
      const region = [bounds.getSouthWest(), bounds.getNorthWest(), bounds.getNorthEast(), bounds.getSouthEast()]
                      .map(p => [p.lat, p.lng])
      updateEarthquakesData(region, map.getZoom())
      log('MapRegionChanged', {
        minLat: bounds.getSouthWest().lat,
        minLng: bounds.getSouthWest().lng,
        maxLat: bounds.getNorthEast().lat,
        maxLng: bounds.getNorthEast().lng
      })
    }, EARTQUAKES_DOWNLOAD_DELAY)
  }

  handleZoomEnd(event) {
    const map = event.target
    log('MapZoomChanged', {zoom: map.getZoom()})

    zoomLevel = map.getZoom()

    // const { layers } = this.props
    // const layer = getKML(map.getZoom())
    // return layers.get('plates') && <PlatesLayer />
  }

  handleEarthquakeClick(event, earthquake) {
    // Do not open earthquake popup if click was part of the map dragging action.
    if (this._mapBeingDragged) return
    this.setState({selectedEarthquake: earthquake})
    log('EarthquakeClicked', earthquake)
  }

  handleEarthquakePopupClose() {
    this.setState({selectedEarthquake: null})
  }
  handleVolcanoPopupClose(){
    this.setState({selectedVolcano: null})
  }
  handleVolcanoClick(event, volcano){
    if (this._mapBeingDragged) return
    this.setState({selectedVolcano: volcano})
    log('Volcano Clicked', volcano)
  }

  fitBounds(bounds = INITIAL_BOUNDS) {
    const { mark2DViewModified } = this.props
    this.map.fitBounds(bounds)
    mark2DViewModified(false)
    log('ResetMapClicked')
  }

  renderBaseLayer() {
    // #key attribute is very important here. #subdomains is not a dynamic property, so we can't reuse the same
    // component instance when we switch between maps with subdomains and without.
    const { layers } = this.props
    const layer = mapLayer(layers.get('base'))
    return <TileLayer key={layers.get('base') } url={layer.url} subdomains={layer.subdomains} attribution={layer.attribution}/>
  }

  //Displays layer only if zoom level is > 4
  renderZoomLayerC() {
    const layer = new PlatesLayerC();
    if(zoomLevel > 4){
      return layer && <PlatesLayerC />
    }
    else {
      return null
    }
   }
  //Displays layer only if zoom level is < 4
  renderZoomLayerS() {
    const layer = new PlatesLayerS()
    if(zoomLevel <= 4){
      return layer && <PlatesLayerS />
    }
    else {
      return null
    }
  }

  render() {
    const { mode, earthquakes, layers, crossSectionPoints, setCrossSectionPoint } = this.props
    const { selectedEarthquake, selectedVolcano } = this.state
    return (
      <div className={`seismic-eruptions-map mode-${mode}`}>
        <Map ref='map' className='map' onLeafletMovestart={this.handleMoveStart} onLeafletMoveend={this.handleMoveEnd}
             onLeafletZoomend={this.handleZoomEnd}
             bounds={INITIAL_BOUNDS} minZoom={2} maxZoom={13}>
          {this.renderBaseLayer()}
          {this.renderZoomLayerS()}
          {this.renderZoomLayerC()}
          {/*{layers.get('plates') && <PlatesLayer />}*/}
          {layers.get('volcanos') && <VolcanosLayer volcanoClick={this.handleVolcanoClick}/>}
          {layers.get('platemovement') && <PlateMovementLayer />}

          {mode !== '3d' && layers.get('earthquakes') &&
            /* Performance optimization. Update of this component is expensive. Remove it when the map is invisible. */
            <EarthquakesCanvasLayer earthquakes={earthquakes} earthquakeClick={this.handleEarthquakeClick}/>
          }
          {mode === '2d' && selectedEarthquake &&
            <EarthquakePopup earthquake={selectedEarthquake} onPopupClose={this.handleEarthquakePopupClose}/>
          }
          {mode === '2d' && selectedVolcano &&
            <VolcanoPopup volcano={selectedVolcano} onPopupClose={this.handleVolcanoPopupClose}/>
          }
          {mode === 'cross-section' &&
            <CrossSectionDrawLayer crossSectionPoints={crossSectionPoints} setCrossSectionPoint={setCrossSectionPoint}/>
          }
        </Map>

    )
  }
}

function createSVGOverlayLayer(map) {
  map.addLayer(new Circle([0,0], 0, {opacity: 0, fillOpacity: 0}))
}
<commit_msg>Fixed: Plates map always visible<commit_after>import React, { Component } from 'react'
import pureRender from 'pure-render-decorator'
import { Map, TileLayer } from 'react-leaflet'
import { Circle } from 'leaflet'
import EarthquakesCanvasLayer from './earthquakes-canvas-layer'
import EarthquakePopup from './earthquake-popup'
import VolcanoPopup from './volcano-popup'
import {PlatesLayerS, PlatesLayerC} from './plates-layer'
import VolcanosLayer from './volcanos-layer'
import PlateMovementLayer from './plate-movement-layer'
import CrossSectionDrawLayer from './cross-section-draw-layer'
import addTouchSupport from '../custom-leaflet/touch-support'
import { mapLayer } from '../map-layer-tiles'
import log from '../logger'
import config from '../config'

import '../../css/leaflet/leaflet.css'
import '../../css/seismic-eruptions-map.less'

const INITIAL_BOUNDS = [
  [config.minLat, config.minLng],
  [config.maxLat, config.maxLng]
]

// It delays download of earthquakes data on map moveend event, so user can pan or zoom map
// a few times quickly before the download starts.
const EARTQUAKES_DOWNLOAD_DELAY = 600 // ms

// Leaflet map doesn't support custom touch events by default.
addTouchSupport()

var zoomLevel = 2;

@pureRender
export default class SeismicEruptionsMap extends Component {
  constructor(props) {
    super(props)
    this.state = {
      selectedEarthquake: null,
      selectedVolcano: null
    }
    this.handleMoveStart = this.handleMoveStart.bind(this)
    this.handleEarthquakeClick = this.handleEarthquakeClick.bind(this)
    this.handleEarthquakePopupClose = this.handleEarthquakePopupClose.bind(this)
    this.handleVolcanoClick = this.handleVolcanoClick.bind(this)
    this.handleVolcanoPopupClose = this.handleVolcanoPopupClose.bind(this)
    this.handleMoveEnd = this.handleMoveEnd.bind(this)
    this.handleZoomEnd = this.handleZoomEnd.bind(this)
  }

  get map() {
    return this.refs.map.getLeafletElement()
  }

  latLngToPoint(latLng) {
    return this.map.latLngToContainerPoint(latLng)
  }

  componentDidMount() {
    // Make sure that SVG overlay layer exists from the very beginning. It's created dynamically when the first
    // SVG object is added to the map. It solves / simplifies some issues. For example we don't want earthquakes
    // to be clickable when we enter cross-section drawing mode. The simplest solution is to set Canvas z-index
    // lower than SVG z-index, but it won't work if the SVG layer hasn't been created yet.
    createSVGOverlayLayer(this.map)
    // Initially Leaflet always triggers mapmove event, as probably real bounds are a bit different than
    // provided bounds (due to screen size etc.). That is causing mark2DViewModified to be called and reset view icon
    // being shown. This is unwanted, as there has been no user interaction. Mark this view unmodified again.
    const { mark2DViewModified } = this.props
    mark2DViewModified(false)
  }

  componentDidUpdate() {
    const { selectedEarthquake } = this.state
    const { earthquakes } = this.props
    if (selectedEarthquake) {
      let found = false
      // Check if selected earthquake is still present and visible.
      earthquakes.forEach(e => {
        if (e.id === selectedEarthquake.id && e.visible) found = true
      })
      // Reset it if not.
      if (!found) this.setState({selectedEarthquake: null})
    }
  }

  handleMoveStart() {
    this._mapBeingDragged = true
    const { mark2DViewModified } = this.props
    mark2DViewModified(true)
  }

  handleMoveEnd(event) {
    this._mapBeingDragged = false

    clearTimeout(this._tilesDownloadTimoutID)
    this._tilesDownloadTimoutID = setTimeout(() => {
      const { updateEarthquakesData } = this.props
      const map = event.target
      const bounds = map.getBounds()
      const region = [bounds.getSouthWest(), bounds.getNorthWest(), bounds.getNorthEast(), bounds.getSouthEast()]
                      .map(p => [p.lat, p.lng])
      updateEarthquakesData(region, map.getZoom())
      log('MapRegionChanged', {
        minLat: bounds.getSouthWest().lat,
        minLng: bounds.getSouthWest().lng,
        maxLat: bounds.getNorthEast().lat,
        maxLng: bounds.getNorthEast().lng
      })
    }, EARTQUAKES_DOWNLOAD_DELAY)
  }

  handleZoomEnd(event) {
    const map = event.target
    log('MapZoomChanged', {zoom: map.getZoom()})

    zoomLevel = map.getZoom()
  }

  handleEarthquakeClick(event, earthquake) {
    // Do not open earthquake popup if click was part of the map dragging action.
    if (this._mapBeingDragged) return
    this.setState({selectedEarthquake: earthquake})
    log('EarthquakeClicked', earthquake)
  }

  handleEarthquakePopupClose() {
    this.setState({selectedEarthquake: null})
  }
  handleVolcanoPopupClose(){
    this.setState({selectedVolcano: null})
  }
  handleVolcanoClick(event, volcano){
    if (this._mapBeingDragged) return
    this.setState({selectedVolcano: volcano})
    log('Volcano Clicked', volcano)
  }

  fitBounds(bounds = INITIAL_BOUNDS) {
    const { mark2DViewModified } = this.props
    this.map.fitBounds(bounds)
    mark2DViewModified(false)
    log('ResetMapClicked')
  }

  renderBaseLayer() {
    // #key attribute is very important here. #subdomains is not a dynamic property, so we can't reuse the same
    // component instance when we switch between maps with subdomains and without.
    const { layers } = this.props
    const layer = mapLayer(layers.get('base'))
    return <TileLayer key={layers.get('base') } url={layer.url} subdomains={layer.subdomains} attribution={layer.attribution}/>
  }

  //Displays layer only if zoom level is > 4
  renderZoomLayerC() {
    const { layers } = this.props;
    const layer = new PlatesLayerC();
    if(zoomLevel > 4 && layers.get('plates')){
      return layer && <PlatesLayerC />
    }
    else {
      return null
    }
   }
  //Displays layer only if zoom level is < 4
  renderZoomLayerS() {
    const { layers } = this.props;
    const layer = new PlatesLayerS()
    if(zoomLevel <= 4 && layers.get('plates')){
      return layer && <PlatesLayerS />
    }
    else {
      return null
    }
  }

  render() {
    const { mode, earthquakes, layers, crossSectionPoints, setCrossSectionPoint } = this.props
    const { selectedEarthquake, selectedVolcano } = this.state
    return (
      <div className={`seismic-eruptions-map mode-${mode}`}>
        <Map ref='map' className='map' onLeafletMovestart={this.handleMoveStart} onLeafletMoveend={this.handleMoveEnd}
             onLeafletZoomend={this.handleZoomEnd}
             bounds={INITIAL_BOUNDS} minZoom={2} maxZoom={13}>
          {this.renderBaseLayer()}
          {this.renderZoomLayerS()}
          {this.renderZoomLayerC()}
          {/*{layers.get('plates') && <PlatesLayer />}*/}
          {layers.get('volcanos') && <VolcanosLayer volcanoClick={this.handleVolcanoClick}/>}
          {layers.get('platemovement') && <PlateMovementLayer />}

          {mode !== '3d' && layers.get('earthquakes') &&
            /* Performance optimization. Update of this component is expensive. Remove it when the map is invisible. */
            <EarthquakesCanvasLayer earthquakes={earthquakes} earthquakeClick={this.handleEarthquakeClick}/>
          }
          {mode === '2d' && selectedEarthquake &&
            <EarthquakePopup earthquake={selectedEarthquake} onPopupClose={this.handleEarthquakePopupClose}/>
          }
          {mode === '2d' && selectedVolcano &&
            <VolcanoPopup volcano={selectedVolcano} onPopupClose={this.handleVolcanoPopupClose}/>
          }
          {mode === 'cross-section' &&
            <CrossSectionDrawLayer crossSectionPoints={crossSectionPoints} setCrossSectionPoint={setCrossSectionPoint}/>
          }
        </Map>

    )
  }
}

function createSVGOverlayLayer(map) {
  map.addLayer(new Circle([0,0], 0, {opacity: 0, fillOpacity: 0}))
}
<|endoftext|>