<commit_before>var orderedByStatus = true;
var sortByStat;
var sortByParty;
var draw;
var drawMerged;
var grouped=null;
var api_root;
var utils, highlight;

(function(){

  var thelawfactory = window.thelawfactory || (window.thelawfactory = {});

  thelawfactory.mod2 = function(){

    function get_status_img(e){
        if (e.sort==="adopté") return "img/ok.png";
        if (e.sort==="rejeté") return "img/ko.png";
        if (e.sort==="non-voté") return "img/nd.png";
    }

  	function vis(selection){

		var articles;
        utils = $('.mod2').scope();
        highlight = utils.highlightGroup;
        selection.each(function(d,i){
            utils.groups = d.groupes;
            articles=d.sujets;
            api_root=d.api_root_url;
        })
        if (Object.keys(articles).length < 2)
            $('#display_menu').parent().hide();

        selectRow = function(art,pos) {

            if(d3.event) d3.event.stopPropagation();

            var sel = d3.select("."+art);

            if(!sel.empty()) {

                d3.selectAll("g").style("opacity", 0.1);

                sel.style("opacity", 0.9);

                if(pos) $("#viz").animate({ scrollTop: sel.attr("data-offset") })
            }
        };

        deselectRow = function() {
            d3.selectAll("g").style("opacity",0.9);
        };

	artArray=d3.values(articles).sort(function(a,b){return a.order-b.order})

		var w = $("#viz").width()-30,
		    rw = $("#viz").width(),
		    lineh = 30,
		    h = lineh*artArray.length+40,
		    z = 20,
            x= 0,
		    nsq = Math.round((w-40) / z),
		    y = h / z;
	    var jumpLines = 0
		var offset = 0
		var svg = d3.select("#viz").append("svg")
		    .attr("width", rw)
		    .attr("height", h)
            .on("click",deselectRow);

        d3.select("svg").on("click", function(){ utils.resetHighlight('amds'); });

        function chk_scroll(e) {
            e.stopPropagation();
            var elem = $(e.currentTarget);
            if (elem[0].scrollHeight - elem.scrollTop() == elem.outerHeight())
            {
                $(".end-tip").fadeOut(200);
            }
            else {
                if(!$(".end-tip").is(":visible")) $(".end-tip").fadeIn(200);

            }

        }

        var compare_partys = function(a,b){
                if (utils.groups[a].order < utils.groups[b].order) return -1;
                if (utils.groups[a].order > utils.groups[b].order) return 1;
            },
            statsorder = {"adopté": 0, "rejeté": 1, "non-voté": 2},
            compare_stats = function(a,b){
                if (statsorder[a] < statsorder[b]) return -1;
                if (statsorder[a] > statsorder[b]) return 1;
            },
            compare_by_party = function(a,b){
                if (a.groupe != b.groupe) return compare_partys(a.groupe, b.groupe);
                if (a.sort != b.sort) return compare_stats(a.sort, b.sort);
                return a.numero - b.numero;
            },
            compare_by_stat = function(a,b){
                if (a.sort != b.sort) return compare_stats(a.sort, b.sort);
                if (a.groupe != b.groupe) return compare_partys(a.groupe, b.groupe);
                return a.numero - b.numero;
            },
            order_grouped = function(){
                grouped['amendements'].sort((orderedByStatus ? compare_by_stat : compare_by_party));
            },
            order_ungrouped = function(){
                artArray.forEach(function(d,i) {
                    d['amendements'].sort((orderedByStatus ? compare_by_stat : compare_by_party));
                });
            },
            clear_screen = function(){
                $("svg").empty();
                $(".text-container").empty();
                jumpLines=0;
            },
            redraw = function(){
                (grouped ? drawMerged() : draw());
            },
            check_half = function(g) {
                var half_col=false;
                var maxlen;
                if(!g)  maxlen = d3.max(artArray,function(d){return d.amendements.length});
                else  maxlen = grouped.amendements.length;

                if(maxlen<nsq/2) {
                    x=nsq/2-1;
                    half_col=true;
                }
                else x = nsq;
                return half_col;
            };

        sortByStat = function() {
            $("#display_order .chosen").removeClass('chosen');
            $("#display_order #do-stat").addClass('chosen');
            orderedByStatus = true;
            redraw();
        };

        sortByParty = function() {
            $("#display_order .chosen").removeClass('chosen');
            $("#display_order #do-party").addClass('chosen');
            orderedByStatus = false;
            redraw();
        };

        draw = function() {
            $("#display_menu .chosen").removeClass('chosen');
            $("#display_menu #dm-draw").addClass('chosen');
            grouped=null;
            order_ungrouped();
            clear_screen();
            var half_col=check_half();
                artArray.forEach(function (d, i) {
                    drawLines(d, i, half_col)
                });
            adjustHeight();
        }

        drawMerged = function() {

            $("#display_menu .chosen").removeClass('chosen');
            $("#display_menu #dm-merged").addClass('chosen');
            if(!grouped) {
                grouped = {titre:'Tous les amendements',key: 'all articles', amendements:[]}
                artArray.forEach(function(d,i) {
                    grouped.amendements=grouped.amendements.concat(d.amendements)
                });
            }

            var half_col=check_half(grouped);
            order_grouped();
            clear_screen();
            drawLines(grouped,0,half_col);
            adjustHeight();
        }

        function adjustHeight() {
            var a = d3.select("svg").select("g:last-child").attr("data-offset"),
               ah = d3.select("svg").select("g:last-child").node().getBBox().height;
            svg.attr("height",z+parseInt(a)+ah);
        }

        function drawLines(d,i, half) {

          var multi=false;
          len = d.amendements.length;
		  lines = Math.ceil(len/x);
          if(lines>1) {
              multi=true;
              lines += 1;
          }
              var k = Math.floor(i / 2);
              d.offset = offset;

              var curRow = svg.append("g")
                  .attr("class", d.titre.replace(/ |'/g, '_').toLowerCase())
                  .attr("transform", function () {
                      if (!half) return "translate(" + 10 + "," + (i * 20 + i * lineh + 10 + jumpLines * (lineh - 10)) + ")";
                      else {
                          return "translate(" + (10 + (i % 2) * w / 2) + "," + (k * 20 + k * lineh + 10 + jumpLines * (lineh - 10)) + ")";
                      }
                  })
                  .attr("data-offset", function () {
                      if (!half) return i * 20 + i * lineh + 10 + jumpLines * (lineh - 10)
                      else return k * 20 + k * lineh + 10 + jumpLines * (lineh - 10);
                  })
                  .call(function () {
                    if(!multi) {
                        n = d.amendements.length;
                        offset = Math.floor(n / x)
                        jumpLines = jumpLines + offset;
                    }
                      else jumpLines+=lines-1;
                  })

              curRow.append("text")
                  .attr("x", 20)
                  .attr("class", "row-txt")
                  .attr("y", 15)
                  .style("fill", "#333")
                  .attr("font-size", "0.85em")
                  .text(d.titre)
                  .on("click", function () {
                      selectRow(d.titre.toLowerCase().replace(/ |'/g, '_'), false)
                  });

              var bg = curRow
                  .selectAll(".bg")
                  .data(d3.range(x * lines))
                  .enter()

              bg.append("rect")
                  .attr("x", function (f) {
                      return (f % x) * z + 21
                  })
                  .attr("y", function (f) {
                      return Math.floor(f / x) * z + 21
                  })
                  .attr("width", z - 2)
                  .attr("height", z - 2)
                  .attr("rx", 2)
                  .attr("ry", 2)
                  .attr("class", "bg")
                  .style("fill", "#E6E6E6")

             /* var margin = d.offset == 0 ? 'style="margin-top : 10px"' : 'style="margin-top : ' + (10 + 20 * d.offset) + 'px "',
                  subj = d.titre;*/

              curRow.append("text")
                  .attr("x", 20)
                  .attr("class", "row-txt")
                  .attr("y", 15)
                  .style("fill", "#333")
                  .attr("font-size", "0.85em")
                  .text(d.titre)
                  .on("click", function () {
                      selectRow(d.titre.toLowerCase().replace(/ |'/g, '_'), false)
                  });

              var popover = function (d) {
                  var date = d.date.split('-'),
                      div = d3.select(document.createElement("div")).style("width", "100%");
                  div.append("p").html("" + utils.groups[d.groupe].nom + "");
                  div.append("p").html("Sort : " + d.sort + "");
                  div.append("p").html("" + [date[2], date[1], date[0]].join("/") + "");
                  return {
                      title: "Amendement " + d.numero,
                      content: div,
                      placement: "mouse",
                      gravity: "right",
                      displacement: [10, -90],
                      mousemove: true
                  };
              }

              var amds = curRow
                  .selectAll(".amd")
                  .data(d.amendements)
                  .enter();
              amds.append("rect")
                  .attr("x", function (f, i) {
                      if(multi)  return Math.floor(i / lines) * z + 21;
                      else return (i % x) * z + 21
                  })
                  .attr("y", function (f, i) {
                      if(multi)  return Math.floor(Math.floor(i / lines) % 2 == 0 ? i % lines : lines - (i % lines) - 1) * z + 21;
                      return Math.floor(i / x) * z + 21
                  })
                  .attr("width", z - 2)
                  .attr("height", z - 2)
                  .attr("rx", 2)
                  .attr("ry", 2)
                  .attr("id", function (d) {
                      return "a_" + d.numero.replace(/[^a-z\d]/ig, '')
                  })
                  .attr("class", "amd")
                  .attr("class", function(d) { return utils.slugGroup(d.groupe) + " " + utils.slugGroup(d.sort); })
                  .style("fill", color_amd)
                  .popover(popover)
                  .on("click", select);

              var imgs = curRow
                  .selectAll("image")
                  .data(d.amendements)
                  .enter();
              imgs.append("svg:image")
                  .attr("x", function (f, i) {
                      if(multi)  return Math.floor(i / lines) * z + 25;
                      else return (i % x) * z + 25
                  })
                  .attr("y", function (f, i) {
                      if(multi)  return Math.floor(Math.floor(i / lines) % 2 == 0 ? i % lines : lines - (i % lines) - 1) * z + 25;
                      return Math.floor(i / x) * z + 25
                  })
                  .attr("width", z - 10)
                  .attr("height", z - 10)
                  .attr("xlink:href", get_status_img)
                  .popover(popover)
                  .on("click", select);

        }

		function select(d) {
            d3.event.stopPropagation()
            utils.resetHighlight('amds');

			d3.json(api_root+d.id_api+'/json',function(error,json){
				var currAmd = json.amendement,
                    source_am = '.fr — ',
                    statico = get_status_img(d),
                    col = color_amd(d);
                if (currAmd.url_nosdeputes) source_am = currAmd.url_nosdeputes+'">NosDéputés'+source_am+'Assemblée nationale';
                else if(currAmd.url_nossenateurs) source_am = currAmd.url_nossenateurs+'">NosSénateurs'+source_am+'Sénat';
                $(".text-container").html(
                    "
Date : " + d3.time.format("%d/%m/%Y")(d3.time.format("%Y-%m-%d").parse(d.date)) + "
" +
                    "
Objet : " + currAmd.sujet+"
" +
                    "
Signataires : " + currAmd.signataires+"
" +
                    "
Statut : " + currAmd.sort + "
" +
                    "
Exposé des motifs : " + currAmd.expose+"
" +
                    "
Texte : " + currAmd.texte +
                    '
Sources : <a target="_blank" href="' + source_am + "
"
                );
			})
            d3.selectAll("#a_"+d.numero.replace(/[^a-z\d]/ig, ''))
                .attr("class","actv-amd")
                .style("stroke", "#D80053")
                .style("stroke-width", 2);
            $("#text-title").text("Amendement "+d.numero)
			$('.text-container').scrollTop(0);
			if(!$(".end-tip").is(":visible")) $(".end-tip").fadeIn(200);
		}

		function color_amd(d) {
			if(utils.groups[d.groupe]) {
				return utils.adjustColor(utils.groups[d.groupe].color).toString();
			} else return "#E6E6E6";
        }

        $(document).ready(function() {
            utils.drawGroupsLegend();
            if ($(".others div").length) $(".others").append('

');
            [
                {nom: 'Adopté', id: 'adopt', img: 'ok'},
                {nom: 'Rejeté', id: 'rejet', img: 'ko'},
                {nom: 'Non voté', id: 'nonvot', img: 'nd'}
            ].forEach(function(d) {
                $(".others").append('

'+d.nom+'

');
            });
            $(".leg-item").tooltip();
            $('.text-container').bind('scroll',chk_scroll);
            draw();
        });

    }; //end function vis

    return vis;
  };

})();
<commit_msg>mod2 fix removed class after deselection<commit_after>var orderedByStatus = true;
var sortByStat;
var sortByParty;
var draw;
var drawMerged;
var grouped=null;
var api_root;
var utils, highlight;

(function(){

  var thelawfactory = window.thelawfactory || (window.thelawfactory = {});

  thelawfactory.mod2 = function(){

    function get_status_img(e){
        if (e.sort==="adopté") return "img/ok.png";
        if (e.sort==="rejeté") return "img/ko.png";
        if (e.sort==="non-voté") return "img/nd.png";
    }

  	function vis(selection){

		var articles;
        utils = $('.mod2').scope();
        highlight = utils.highlightGroup;
        selection.each(function(d,i){
            utils.groups = d.groupes;
            articles=d.sujets;
            api_root=d.api_root_url;
        })
        if (Object.keys(articles).length < 2)
            $('#display_menu').parent().hide();

        selectRow = function(art,pos) {

            if(d3.event) d3.event.stopPropagation();

            var sel = d3.select("."+art);

            if(!sel.empty()) {

                d3.selectAll("g").style("opacity", 0.1);

                sel.style("opacity", 0.9);

                if(pos) $("#viz").animate({ scrollTop: sel.attr("data-offset") })
            }
        };

        deselectRow = function() {
            d3.selectAll("g").style("opacity",0.9);
        };

	artArray=d3.values(articles).sort(function(a,b){return a.order-b.order})

		var w = $("#viz").width()-30,
		    rw = $("#viz").width(),
		    lineh = 30,
		    h = lineh*artArray.length+40,
		    z = 20,
            x= 0,
		    nsq = Math.round((w-40) / z),
		    y = h / z;
	    var jumpLines = 0
		var offset = 0
		var svg = d3.select("#viz").append("svg")
		    .attr("width", rw)
		    .attr("height", h)
            .on("click",deselectRow);

        d3.select("svg").on("click", function(){ utils.resetHighlight('amds'); });

        function chk_scroll(e) {
            e.stopPropagation();
            var elem = $(e.currentTarget);
            if (elem[0].scrollHeight - elem.scrollTop() == elem.outerHeight())
            {
                $(".end-tip").fadeOut(200);
            }
            else {
                if(!$(".end-tip").is(":visible")) $(".end-tip").fadeIn(200);

            }

        }

        var compare_partys = function(a,b){
                if (utils.groups[a].order < utils.groups[b].order) return -1;
                if (utils.groups[a].order > utils.groups[b].order) return 1;
            },
            statsorder = {"adopté": 0, "rejeté": 1, "non-voté": 2},
            compare_stats = function(a,b){
                if (statsorder[a] < statsorder[b]) return -1;
                if (statsorder[a] > statsorder[b]) return 1;
            },
            compare_by_party = function(a,b){
                if (a.groupe != b.groupe) return compare_partys(a.groupe, b.groupe);
                if (a.sort != b.sort) return compare_stats(a.sort, b.sort);
                return a.numero - b.numero;
            },
            compare_by_stat = function(a,b){
                if (a.sort != b.sort) return compare_stats(a.sort, b.sort);
                if (a.groupe != b.groupe) return compare_partys(a.groupe, b.groupe);
                return a.numero - b.numero;
            },
            order_grouped = function(){
                grouped['amendements'].sort((orderedByStatus ? compare_by_stat : compare_by_party));
            },
            order_ungrouped = function(){
                artArray.forEach(function(d,i) {
                    d['amendements'].sort((orderedByStatus ? compare_by_stat : compare_by_party));
                });
            },
            clear_screen = function(){
                $("svg").empty();
                $(".text-container").empty();
                jumpLines=0;
            },
            redraw = function(){
                (grouped ? drawMerged() : draw());
            },
            check_half = function(g) {
                var half_col=false;
                var maxlen;
                if(!g)  maxlen = d3.max(artArray,function(d){return d.amendements.length});
                else  maxlen = grouped.amendements.length;

                if(maxlen<nsq/2) {
                    x=nsq/2-1;
                    half_col=true;
                }
                else x = nsq;
                return half_col;
            };

        sortByStat = function() {
            $("#display_order .chosen").removeClass('chosen');
            $("#display_order #do-stat").addClass('chosen');
            orderedByStatus = true;
            redraw();
        };

        sortByParty = function() {
            $("#display_order .chosen").removeClass('chosen');
            $("#display_order #do-party").addClass('chosen');
            orderedByStatus = false;
            redraw();
        };

        draw = function() {
            $("#display_menu .chosen").removeClass('chosen');
            $("#display_menu #dm-draw").addClass('chosen');
            grouped=null;
            order_ungrouped();
            clear_screen();
            var half_col=check_half();
                artArray.forEach(function (d, i) {
                    drawLines(d, i, half_col)
                });
            adjustHeight();
        }

        drawMerged = function() {

            $("#display_menu .chosen").removeClass('chosen');
            $("#display_menu #dm-merged").addClass('chosen');
            if(!grouped) {
                grouped = {titre:'Tous les amendements',key: 'all articles', amendements:[]}
                artArray.forEach(function(d,i) {
                    grouped.amendements=grouped.amendements.concat(d.amendements)
                });
            }

            var half_col=check_half(grouped);
            order_grouped();
            clear_screen();
            drawLines(grouped,0,half_col);
            adjustHeight();
        }

        function adjustHeight() {
            var a = d3.select("svg").select("g:last-child").attr("data-offset"),
               ah = d3.select("svg").select("g:last-child").node().getBBox().height;
            svg.attr("height",z+parseInt(a)+ah);
        }

        function drawLines(d,i, half) {

          var multi=false;
          len = d.amendements.length;
		  lines = Math.ceil(len/x);
          if(lines>1) {
              multi=true;
              lines += 1;
          }
              var k = Math.floor(i / 2);
              d.offset = offset;

              var curRow = svg.append("g")
                  .attr("class", d.titre.replace(/ |'/g, '_').toLowerCase())
                  .attr("transform", function () {
                      if (!half) return "translate(" + 10 + "," + (i * 20 + i * lineh + 10 + jumpLines * (lineh - 10)) + ")";
                      else {
                          return "translate(" + (10 + (i % 2) * w / 2) + "," + (k * 20 + k * lineh + 10 + jumpLines * (lineh - 10)) + ")";
                      }
                  })
                  .attr("data-offset", function () {
                      if (!half) return i * 20 + i * lineh + 10 + jumpLines * (lineh - 10)
                      else return k * 20 + k * lineh + 10 + jumpLines * (lineh - 10);
                  })
                  .call(function () {
                    if(!multi) {
                        n = d.amendements.length;
                        offset = Math.floor(n / x)
                        jumpLines = jumpLines + offset;
                    }
                      else jumpLines+=lines-1;
                  })

              curRow.append("text")
                  .attr("x", 20)
                  .attr("class", "row-txt")
                  .attr("y", 15)
                  .style("fill", "#333")
                  .attr("font-size", "0.85em")
                  .text(d.titre)
                  .on("click", function () {
                      selectRow(d.titre.toLowerCase().replace(/ |'/g, '_'), false)
                  });

              var bg = curRow
                  .selectAll(".bg")
                  .data(d3.range(x * lines))
                  .enter()

              bg.append("rect")
                  .attr("x", function (f) {
                      return (f % x) * z + 21
                  })
                  .attr("y", function (f) {
                      return Math.floor(f / x) * z + 21
                  })
                  .attr("width", z - 2)
                  .attr("height", z - 2)
                  .attr("rx", 2)
                  .attr("ry", 2)
                  .attr("class", "bg")
                  .style("fill", "#E6E6E6")

             /* var margin = d.offset == 0 ? 'style="margin-top : 10px"' : 'style="margin-top : ' + (10 + 20 * d.offset) + 'px "',
                  subj = d.titre;*/

              curRow.append("text")
                  .attr("x", 20)
                  .attr("class", "row-txt")
                  .attr("y", 15)
                  .style("fill", "#333")
                  .attr("font-size", "0.85em")
                  .text(d.titre)
                  .on("click", function () {
                      selectRow(d.titre.toLowerCase().replace(/ |'/g, '_'), false)
                  });

              var popover = function (d) {
                  var date = d.date.split('-'),
                      div = d3.select(document.createElement("div")).style("width", "100%");
                  div.append("p").html("" + utils.groups[d.groupe].nom + "");
                  div.append("p").html("Sort : " + d.sort + "");
                  div.append("p").html("" + [date[2], date[1], date[0]].join("/") + "");
                  return {
                      title: "Amendement " + d.numero,
                      content: div,
                      placement: "mouse",
                      gravity: "right",
                      displacement: [10, -90],
                      mousemove: true
                  };
              }

              var amds = curRow
                  .selectAll(".amd")
                  .data(d.amendements)
                  .enter();
              amds.append("rect")
                  .attr("x", function (f, i) {
                      if(multi)  return Math.floor(i / lines) * z + 21;
                      else return (i % x) * z + 21
                  })
                  .attr("y", function (f, i) {
                      if(multi)  return Math.floor(Math.floor(i / lines) % 2 == 0 ? i % lines : lines - (i % lines) - 1) * z + 21;
                      return Math.floor(i / x) * z + 21
                  })
                  .attr("width", z - 2)
                  .attr("height", z - 2)
                  .attr("rx", 2)
                  .attr("ry", 2)
                  .attr("id", function (d) {
                      return "a_" + d.numero.replace(/[^a-z\d]/ig, '')
                  })
                  .attr("class", function(d) { return "amd " + utils.slugGroup(d.groupe) + " " + utils.slugGroup(d.sort); })
                  .style("fill", color_amd)
                  .popover(popover)
                  .on("click", select);

              var imgs = curRow
                  .selectAll("image")
                  .data(d.amendements)
                  .enter();
              imgs.append("svg:image")
                  .attr("x", function (f, i) {
                      if(multi)  return Math.floor(i / lines) * z + 25;
                      else return (i % x) * z + 25
                  })
                  .attr("y", function (f, i) {
                      if(multi)  return Math.floor(Math.floor(i / lines) % 2 == 0 ? i % lines : lines - (i % lines) - 1) * z + 25;
                      return Math.floor(i / x) * z + 25
                  })
                  .attr("width", z - 10)
                  .attr("height", z - 10)
                  .attr("xlink:href", get_status_img)
                  .popover(popover)
                  .on("click", select);

        }

		function select(d) {
            d3.event.stopPropagation()
            utils.resetHighlight('amds');

			d3.json(api_root+d.id_api+'/json',function(error,json){
				var currAmd = json.amendement,
                    source_am = '.fr — ',
                    statico = get_status_img(d),
                    col = color_amd(d);
                if (currAmd.url_nosdeputes) source_am = currAmd.url_nosdeputes+'">NosDéputés'+source_am+'Assemblée nationale';
                else if(currAmd.url_nossenateurs) source_am = currAmd.url_nossenateurs+'">NosSénateurs'+source_am+'Sénat';
                $(".text-container").html(
                    "
Date : " + d3.time.format("%d/%m/%Y")(d3.time.format("%Y-%m-%d").parse(d.date)) + "
" +
                    "
Objet : " + currAmd.sujet+"
" +
                    "
Signataires : " + currAmd.signataires+"
" +
                    "
Statut : " + currAmd.sort + "
" +
                    "
Exposé des motifs : " + currAmd.expose+"
" +
                    "
Texte : " + currAmd.texte +
                    '
Sources : <a target="_blank" href="' + source_am + "
"
                );
			})
            d3.selectAll("#a_"+d.numero.replace(/[^a-z\d]/ig, ''))
                .classed("actv-amd", true)
                .style("stroke", "#D80053")
                .style("stroke-width", 2);
            $("#text-title").text("Amendement "+d.numero)
			$('.text-container').scrollTop(0);
			if(!$(".end-tip").is(":visible")) $(".end-tip").fadeIn(200);
		}

		function color_amd(d) {
			if(utils.groups[d.groupe]) {
				return utils.adjustColor(utils.groups[d.groupe].color).toString();
			} else return "#E6E6E6";
        }

        $(document).ready(function() {
            utils.drawGroupsLegend();
            if ($(".others div").length) $(".others").append('

');
            [
                {nom: 'Adopté', id: 'adopt', img: 'ok'},
                {nom: 'Rejeté', id: 'rejet', img: 'ko'},
                {nom: 'Non voté', id: 'nonvot', img: 'nd'}
            ].forEach(function(d) {
                $(".others").append('

'+d.nom+'

');
            });
            $(".leg-item").tooltip();
            $('.text-container').bind('scroll',chk_scroll);
            draw();
        });

    }; //end function vis

    return vis;
  };

})();
<|endoftext|>


<commit_before>var Metyl = {};

(function() {

var _factories = [];

Metyl.Event = function(props) {
  this.data = props.data || {};
  this.origin = props.origin || null;
};

Metyl.registerMiddleware = function(callback) {
  _factories.push(callback);
}

Metyl.Application = function() {
  EventEmitter.call(this);
};

Metyl.Application.prototype = Object.create(EventEmitter.prototype);

Metyl.Application.prototype._buildPipeline = function() {
  var index = -1;
  var app = this;
  var next = null;

  next = function(event) {
    index += 1;
    if(index >= _factories.length) {
      return;
    } else {
      _factories[index](event, app, next);
    }
  }

  return next;
};

Metyl.Application.prototype.process = function(event) {
  var callback = this._buildPipeline();
  var e = new Metyl.Event(event);
  callback(e);
};

Metyl.Application.prototype.renderEventItem = function(elementName, event) {
  this.emit('eventItem', elementName, event);
};

})();
<commit_msg>add daily and weekly stats reset<commit_after>

(function() {

var _factories = [];

Metyl.registerMiddleware = function(callback) {
  _factories.push(callback);
};

Metyl.Subscription = function() {
  EventEmitter.call(this);
  this.isReady = false;
  this.socket = null;
  this.room = null;
  this.pending = null;
  this.stats = null;
  this.inactiveInterval = null;
  this.statsInterval = null;
  this.resetDayTimeout = null;
  this.resetWeekTimeout = null;
};

Metyl.Subscription.prototype = Object.create(EventEmitter.prototype);

Metyl.Subscription.prototype.initialize = function(room, callback) {
  var self = this;

  this.isReady = false;
  if(this.socket) {
    // Leave the current room
    this.socket.emit('leave', this.room);
  } else {
    this.socket = io();
  }

  if(this.inactiveInterval) {
    // Clear the inactive tracking interval
    clearInterval(this.inactiveInterval);
    this.inactiveInterval = null;
  }

  if(this.statsInterval) {
    // Clear the statistics update interval
    clearInterval(this.statsInterval);
    this.statsInterval = null;
  }

  if(this.resetDayTimeout) {
    // Clear the daily reset timeout
    clearTimeout(this.resetDayTimeout);
    this.resetDayTimeout = null;
  }

  if(this.resetWeekTimeout) {
    // Clear the weekly reset timeout
    clearTimeout(this.resetWeekTimeout);
    this.resetWeekTimeout = null;
  }

  this.room = room;
  // We keep a list of events we receive while we're processing the backlog.
  // After we're done processing the backlog we can process the events that
  // came in.
  this.pending = [];

  // Statistics for today and this week.
  this.stats = {
    // The number of events today
    today: 0,
    // The number of events this week
    week: 0,
    // The amount of time, in seconds, that no event has been received. We set this
    // to null so that the UI shows "Never"
    inactiveTime: null,
    // Events per day this week
    ratePerDay: 0.0
  };

  this.socket.on('data', function(e) {
    // Receive an event from the server.
    if(self.isReady) {
      // Process the event
      self.process(e);
    } else {
      // We are currently processing the backlog. Add the event to the pending
      // list so it can be processed later.
      self.pending.push(e);
    }
  }).on('connect', function() {
    // Connect to the room we need.
    self.socket.emit('join', room);
    // load the backlog
    self.loadBacklog(callback);
  }).on('error', function(error) {
    self.emit('error', error);
  });
};

/**
 * Build the pipeline that processes an incomming event. The pipline will push
 * the event through all middleware.
 */
Metyl.Subscription.prototype._buildPipeline = function() {
  var index = -1;
  var app = this;
  var next = null;

  next = function(event) {
    index += 1;
    if(index >= _factories.length) {
      return;
    } else {
      _factories[index](event, app, next);
    }
  }

  return next;
};

/**
 * Process an incomming event.
 */
Metyl.Subscription.prototype.process = function(e) {
  var event = new Metyl.Event(e);
  var callback = this._buildPipeline();

  // Kick off the pipeline
  callback(event);

  if(this.isReady) {
    // We only update today's statistics when the event came from the server
    // and not the backlog.
    this.stats.today += 1;
    this.stats.week += 1;
    this.stats.inactiveTime = 0;
    this.pushInactivity(true);
    this.pushStats();
  }
};

/**
 * Load the backlog of events. This is called when we first join a room. We
 * immediately load the last X-number of events so we aren't looking at an
 * empty list.
 */
Metyl.Subscription.prototype.loadBacklog = function(callback) {
  var self = this;
  var url = "/subscribe/" + this.room + "/backlog.json";

  $.ajax(url, {
    success: function(data) {
      var last = null;
      // Process each event in the backlog.
      data.events.forEach(function(event) {
        self.process(event);
        last = event.moment;
      });

      // Process each event that we received while processing the backlog.
      self.pending.forEach(function(event) {
        self.process(event);
        last = event.moment;
      });

      // Update statistics
      self.stats.week = data.stats.week;
      self.stats.today = data.stats.today;
      if(last) {
        self.stats.inactiveTime = moment().diff(last).asSeconds();
      }

      // Clear the pending events because we've already processed them.
      self.pending = null;
      self.isReady = true;

      // Push statistics and inactivity to the UI.
      self.pushInactivity();
      self.pushStats();

      // Set up an interval that updates the inactive event time. The interval
      // is called every second.
      self.inactiveInterval = setInterval(function() {
        self.pushInactivity();
      }, 1000);

      // Set up an interval that updates today's statistics. The interval is
      // called every minute.
      self.statsInterval = setInterval(function() {
        self.pushStats();
      }, 60000);

      // We need to reset today's statistics at the end of the end. At this
      // point we don't know how long it is before tomorrow begins. So, we do
      // some math to get the number of milliseconds from now till tomorrow and
      // then reset today's stats at that point.
      var now = moment();
      var msTillTomorrow = now.clone().add(1, 'day').startOf('day').diff(now);
      this.resetDayTimeout = setTimeout(function() {
        self.resetDay();
      }, msTillTomorrow);

      // Likewise, we don't know how long it is before the next week begins.
      // So, we do some more math to set a timeout when next week begins.
      var msTillNextWeek = now.clone().add(1, 'week').startOf('week').diff(now);
      this.resetWeekTimeout = setTimeout(function() {
        self.resetWeek();
      }, msTillNextWeek);

      if(callback) {
        callback();
      }
    },
    error: function() {
      self.emit('error', 'backlog failed');
    }
  });
};

/**
 * Render an event item to the event list in the UI.
 */
Metyl.Subscription.prototype.renderEventItem = function(elementName, event) {
  this.emit('renderItem', elementName, event);
};

/**
 * Push today's statistics to the UI.
 */
Metyl.Subscription.prototype.pushStats = function() {
  var now = moment();
  var minutes = (now.hours() * 60) + (now.day() * 1440);
  this.stats.ratePerDay = this.stats.week / (minutes / 1440);
  this.emit('stats', this.stats);
};

/**
 * Push inactivity time to the UI.
 */
Metyl.Subscription.prototype.pushInactivity = function(justPush) {
  this.emit('inactive', this.stats.inactiveTime);

  if(!justPush) {
    this.stats.inactiveTime += 1;
  }
};

/**
 * Called at the beginning of tomorrow. Creates another timeout to call this
 * function again at the next day.
 */
Metyl.Subscription.prototype.resetDay = function() {
  var self = this;

  this.stats.today = 0;
  this.pushStats();
  setTimeout(function() {
    self.resetDay();
  }, 86400000);
};

/**
 * Called at the beginning of next week. Creates another timeout to call this
 * function again at the beginning of the following week.
 */
Metyl.Subscription.prototype.resetWeek = function() {
  var self = this;

  this.stats.week = 0;
  this.pushStats();
  setTimeout(function() {
    self.resetWeek();
  }, 604800000);
};

})();
<|endoftext|>


<commit_before>// Globals
var API_SERVER = 'http://api.successwhale.com/v3';
var NARROW_SCREEN_WIDTH = 600;

// Viewmodel for SW
function SWUserViewModel() {
  this.colsPerScreen = ko.observable();
  this.postEntryText = ko.observable();
  this.postToAccounts = ko.observableArray();
  this.columns = ko.observableArray();
}

// Activate knockout.js
viewModel = new SWUserViewModel();
ko.applyBindings(viewModel);

// Checks the user is logged in (via a cookie) - if not, punts them
// to the login page
function checkLoggedIn() {
  if (!readCookie('token')) {
    window.location = '/';
  }
}

// Shows an error box for a set time, with the supplied HTML and optionally also
// a SuccessWhale API error message extracted from the returnedData of an unsuccessful
//  request
function showError(html, returnedData) {
  if (typeof(returnedData) != "undefined") {
    html += "
The SuccessWhale API reported the following error:
" + JSON.parse(returnedData.responseText).error
  }
  $('#errorbox').html(html);
  $('#errorbox').show('slow', function hideLater() {
    setTimeout(function() {
      $('#errorbox').hide('slow');
    }, 5000);
  });
}

// Shows a success notification for a set time, with the supplied HTML content
function showSuccess(html) {
  $('#successbox').html(html);
  $('#successbox').show('slow', function hideLater() {
    setTimeout(function() {
      $('#successbox').hide('slow');
    }, 5000);
  });
}

// Turn an item's text into proper HTML
function makeItemTextHTML(content) {
  return linkify_entities(content);
}

// Turn an item's 'from' structures into proper text
function makeFromUserText(content, service) {
  var html='';
  if (content.fromusername) {
    html += content.fromusername;
    // Enable this to put "@username" after Twitter users. Disabled here to reduce
    // clutter
    /*if (content.fromuser) {
      html += ' (';
      if (service === 'twitter')
      {
        html += '@';
      }
      html += content.fromuser + ')';
    }*/
  }
  else if (content.fromuser) {
    html += content.fromuser;
  }
  return html;
}

// Turn an item's 'from' structures into the URL of the 'from' user's profile
function makeFromUserLink(content, service) {
  var url='';
  if (service === 'twitter') {
    url = 'http://twitter.com/'+content.fromuser;
  }
  else if (service === 'facebook') {
    url = 'http://facebook.com/'+content.fromuserid;
  }
  return url;
}

// Turn an item's metadata (replies, comments...) structures into proper text
function makeMetadataText(content) {
  var html='';
  if (content.numreplied > 0) {
    html +=  ' 💬 ' + content.numreplied;
  }
  if (content.numretweeted > 0) {
    html += ' ♺ ' + content.numretweeted;
  }
  if (content.numfavourited > 0) {
    html +=  ' ★ ' + content.numfavourited;
  }
  if (content.numcomments > 0) {
    html += ' 💬 ' + content.numcomments;
  }
  if (content.numlikes > 0) {
    html += ' 👍 ' + content.numlikes;
  }
  return html;
}

// Load feed for a single column
function loadFeedForColumn(j) {
  var jqxhr = $.get(API_SERVER+'/feed', {sources: viewModel.columns()[j].fullpath, token: readCookie('token')})
    .done(function(returnedData) {
      viewModel.columns()[j].items.removeAll();
      viewModel.columns()[j].items.push.apply(viewModel.columns()[j].items, returnedData.items);
    })
    .fail(function(returnedData) {
      showError('Failed to fetch column "' + viewModel.columns()[j].title + '"', returnedData);
    });
}

// Fetch and display column content
function refreshColumns() {
  var j = 0;
  for (; j < viewModel.columns().length; j++) {
    loadFeedForColumn(j);
  }
}

// Get the user's display settings
function getDisplaySettings() {
  var jqxhr = $.get(API_SERVER+'/displaysettings', {token: readCookie('token')})
    .done(function(returnedData) {
      // Store the columns-per-screen value for use in rendering, or force it to 1
      // column per screen and narrow things down if we have a narrow (mobile phone)
      // window.
      if ($(window).width() > NARROW_SCREEN_WIDTH) {
        viewModel.colsPerScreen(returnedData.colsperscreen);
      } else {
        viewModel.colsPerScreen(1);
      }
    })
    .fail(function(returnedData) {
      showError('Failed to fetch display settings', returnedData);
    });
}

// Fetch and display the list of accounts to post to
function displayPostToAccounts() {
  var jqxhr = $.get(API_SERVER+'/posttoaccounts', {token: readCookie('token')})
    .done(function(returnedData) {
      viewModel.postToAccounts.push.apply(viewModel.postToAccounts, returnedData.posttoaccounts);
    })
    .fail(function(returnedData) {
      showError('Failed to fetch account list', returnedData);
    });
}

// Fetch and display columns
function displayColumns() {
  var jqxhr = $.get(API_SERVER+'/columns', {token: readCookie('token')})
    .done(function(returnedData) {
      var cols = returnedData.columns;

      // Add a dummy observableArray to hold items later
      var i = 0;
      for (; i<cols.length; i++) {
        cols[i].items = ko.observableArray();
      }

      // Update the view model
      viewModel.columns.push.apply(viewModel.columns, cols);

      // Refresh all columns to pull in items
      refreshColumns();
    })
    .fail(function(returnedData) {
      showError('Failed to fetch column list', returnedData);
    });
}

// Post the text from the main entry box (plus an attachment if set) as a new item.
function postItem() {
  // Build a list of service/uid:service/uid... for every service we have selected
  var postToAccountString = '';
  for (i=0; i<viewModel.postToAccounts().length; i++) {
    if (viewModel.postToAccounts()[i].enabled) {
      postToAccountString += viewModel.postToAccounts()[i].service + "/" + viewModel.postToAccounts()[i].uid + ":";
    }
  }
  // send the request
  var jqxhr = $.post(API_SERVER+'/item', {token: readCookie('token'), text: viewModel.postEntryText(), accounts: postToAccountString})
  .done(function(returnedData) {
    showSuccess('Item posted');
    viewModel.postEntryText('');
    refreshColumns();
  })
  .fail(function(returnedData) {
    showError('Failed to post item', returnedData);
  });
  return false;
}

// Automatic stuff on page load
checkLoggedIn();
getDisplaySettings();
displayPostToAccounts();
displayColumns();

// Bind "post item" on button click or textarea Ctrl+Enter
$('#postentry').keydown(function (e) {
  if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey) { postItem(); }
});
$('#postbutton').click(function (e) {
  postItem();
});
// Bind gpopover items
$('#postbuttondropdown').gpopover();
$('#attachbutton').gpopover();
// Bind other menu buttons
$('#logoutbutton').click(function (e) {
  eraseCookie('token');
  window.location = '/';
});
// Focus and enable autosize on post entry box
$('#postentry').autosize();
$('#postentry').focus();
// Refresh every 5 minutes
setInterval( function() { refreshColumns(); }, 300000);
<commit_msg>Don't close gpopovers on click<commit_after>// Globals
var API_SERVER = 'http://api.successwhale.com/v3';
var NARROW_SCREEN_WIDTH = 600;

// Viewmodel for SW
function SWUserViewModel() {
  this.colsPerScreen = ko.observable();
  this.postEntryText = ko.observable();
  this.postToAccounts = ko.observableArray();
  this.columns = ko.observableArray();
}

// Activate knockout.js
viewModel = new SWUserViewModel();
ko.applyBindings(viewModel);

// Checks the user is logged in (via a cookie) - if not, punts them
// to the login page
function checkLoggedIn() {
  if (!readCookie('token')) {
    window.location = '/';
  }
}

// Shows an error box for a set time, with the supplied HTML and optionally also
// a SuccessWhale API error message extracted from the returnedData of an unsuccessful
//  request
function showError(html, returnedData) {
  if (typeof(returnedData) != "undefined") {
    html += "
The SuccessWhale API reported the following error:
" + JSON.parse(returnedData.responseText).error
  }
  $('#errorbox').html(html);
  $('#errorbox').show('slow', function hideLater() {
    setTimeout(function() {
      $('#errorbox').hide('slow');
    }, 5000);
  });
}

// Shows a success notification for a set time, with the supplied HTML content
function showSuccess(html) {
  $('#successbox').html(html);
  $('#successbox').show('slow', function hideLater() {
    setTimeout(function() {
      $('#successbox').hide('slow');
    }, 5000);
  });
}

// Turn an item's text into proper HTML
function makeItemTextHTML(content) {
  return linkify_entities(content);
}

// Turn an item's 'from' structures into proper text
function makeFromUserText(content, service) {
  var html='';
  if (content.fromusername) {
    html += content.fromusername;
    // Enable this to put "@username" after Twitter users. Disabled here to reduce
    // clutter
    /*if (content.fromuser) {
      html += ' (';
      if (service === 'twitter')
      {
        html += '@';
      }
      html += content.fromuser + ')';
    }*/
  }
  else if (content.fromuser) {
    html += content.fromuser;
  }
  return html;
}

// Turn an item's 'from' structures into the URL of the 'from' user's profile
function makeFromUserLink(content, service) {
  var url='';
  if (service === 'twitter') {
    url = 'http://twitter.com/'+content.fromuser;
  }
  else if (service === 'facebook') {
    url = 'http://facebook.com/'+content.fromuserid;
  }
  return url;
}

// Turn an item's metadata (replies, comments...) structures into proper text
function makeMetadataText(content) {
  var html='';
  if (content.numreplied > 0) {
    html +=  ' 💬 ' + content.numreplied;
  }
  if (content.numretweeted > 0) {
    html += ' ♺ ' + content.numretweeted;
  }
  if (content.numfavourited > 0) {
    html +=  ' ★ ' + content.numfavourited;
  }
  if (content.numcomments > 0) {
    html += ' 💬 ' + content.numcomments;
  }
  if (content.numlikes > 0) {
    html += ' 👍 ' + content.numlikes;
  }
  return html;
}

// Load feed for a single column
function loadFeedForColumn(j) {
  var jqxhr = $.get(API_SERVER+'/feed', {sources: viewModel.columns()[j].fullpath, token: readCookie('token')})
    .done(function(returnedData) {
      viewModel.columns()[j].items.removeAll();
      viewModel.columns()[j].items.push.apply(viewModel.columns()[j].items, returnedData.items);
    })
    .fail(function(returnedData) {
      showError('Failed to fetch column "' + viewModel.columns()[j].title + '"', returnedData);
    });
}

// Fetch and display column content
function refreshColumns() {
  var j = 0;
  for (; j < viewModel.columns().length; j++) {
    loadFeedForColumn(j);
  }
}

// Get the user's display settings
function getDisplaySettings() {
  var jqxhr = $.get(API_SERVER+'/displaysettings', {token: readCookie('token')})
    .done(function(returnedData) {
      // Store the columns-per-screen value for use in rendering, or force it to 1
      // column per screen and narrow things down if we have a narrow (mobile phone)
      // window.
      if ($(window).width() > NARROW_SCREEN_WIDTH) {
        viewModel.colsPerScreen(returnedData.colsperscreen);
      } else {
        viewModel.colsPerScreen(1);
      }
    })
    .fail(function(returnedData) {
      showError('Failed to fetch display settings', returnedData);
    });
}

// Fetch and display the list of accounts to post to
function displayPostToAccounts() {
  var jqxhr = $.get(API_SERVER+'/posttoaccounts', {token: readCookie('token')})
    .done(function(returnedData) {
      viewModel.postToAccounts.push.apply(viewModel.postToAccounts, returnedData.posttoaccounts);
    })
    .fail(function(returnedData) {
      showError('Failed to fetch account list', returnedData);
    });
}

// Fetch and display columns
function displayColumns() {
  var jqxhr = $.get(API_SERVER+'/columns', {token: readCookie('token')})
    .done(function(returnedData) {
      var cols = returnedData.columns;

      // Add a dummy observableArray to hold items later
      var i = 0;
      for (; i<cols.length; i++) {
        cols[i].items = ko.observableArray();
      }

      // Update the view model
      viewModel.columns.push.apply(viewModel.columns, cols);

      // Refresh all columns to pull in items
      refreshColumns();
    })
    .fail(function(returnedData) {
      showError('Failed to fetch column list', returnedData);
    });
}

// Post the text from the main entry box (plus an attachment if set) as a new item.
function postItem() {
  // Build a list of service/uid:service/uid... for every service we have selected
  var postToAccountString = '';
  for (i=0; i<viewModel.postToAccounts().length; i++) {
    if (viewModel.postToAccounts()[i].enabled) {
      postToAccountString += viewModel.postToAccounts()[i].service + "/" + viewModel.postToAccounts()[i].uid + ":";
    }
  }
  // send the request
  var jqxhr = $.post(API_SERVER+'/item', {token: readCookie('token'), text: viewModel.postEntryText(), accounts: postToAccountString})
  .done(function(returnedData) {
    showSuccess('Item posted');
    viewModel.postEntryText('');
    refreshColumns();
  })
  .fail(function(returnedData) {
    showError('Failed to post item', returnedData);
  });
  return false;
}

// Automatic stuff on page load
checkLoggedIn();
getDisplaySettings();
displayPostToAccounts();
displayColumns();

// Bind "post item" on button click or textarea Ctrl+Enter
$('#postentry').keydown(function (e) {
  if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey) { postItem(); }
});
$('#postbutton').click(function (e) {
  postItem();
});
// Bind gpopover items
$('#postbuttondropdown').gpopover({preventHide: true});
$('#attachbutton').gpopover({preventHide: true});
// Bind other menu buttons
$('#logoutbutton').click(function (e) {
  eraseCookie('token');
  window.location = '/';
});
// Focus and enable autosize on post entry box
$('#postentry').autosize();
$('#postentry').focus();
// Refresh every 5 minutes
setInterval( function() { refreshColumns(); }, 300000);
<|endoftext|>


<commit_before>document.addEventListener("DOMContentLoaded", function() {
  var mouse = {
    click: false,
    move: false,
    pos: {x:0, y:0},
    pos_prev: false
  };
  // get canvas element and create context
  var canvas  = document.getElementById('drawing');
  var context = canvas.getContext('2d');
  var width   = window.innerWidth;
  var height  = window.innerHeight - $('#title').height();
  var socket  = io();

  var clickX = [];
  var clickY = [];
  var clickDrag = [];
  var paint;

  context.strokeStyle = "#000";
  context.lineJoin = "round";
  context.lineWidth = 5;

  // set canvas to full browser width/height
  canvas.width = width;
  canvas.height = height;

  socket.on('draw_line', function(data) {
    console.log(data);
    clickX.push(data.clickX * width);
    clickY.push(data.clickY * height);
    clickDrag.push(data.clickDrag);
    if(data.redraw)
      redraw();
    else
      drawNew();
  });

  function drawNew() {
    var i = clickX.length - 1
    if (!clickDrag[i]) {
      if (clickX.length == 0) {
        context.beginPath();
        context.moveTo(clickX[i], clickY[i]);
        context.stroke();
      } else {
        context.closePath();

        context.beginPath();
        context.moveTo(clickX[i], clickY[i]);
        context.stroke();
      }
    } else {
      context.lineTo(clickX[i], clickY[i]);
      context.stroke();
    }
  }

  function redraw() {
    // Clears the canvas
    context.clearRect(0, 0, context.canvas.width, context.canvas.height);

    for (var i = 0; i < clickX.length; i += 1) {
      if (!clickDrag[i] && i == 0) {
        context.beginPath();
        context.moveTo(clickX[i], clickY[i]);
        context.stroke();
      } else if (!clickDrag[i] && i > 0) {
        context.closePath();

        context.beginPath();
        context.moveTo(clickX[i], clickY[i]);
        context.stroke();
      } else {
        context.lineTo(clickX[i], clickY[i]);
        context.stroke();
      }
    }
  }
});
<commit_msg>removed loggin from sketch host<commit_after>document.addEventListener("DOMContentLoaded", function() {
  var mouse = {
    click: false,
    move: false,
    pos: {x:0, y:0},
    pos_prev: false
  };
  // get canvas element and create context
  var canvas  = document.getElementById('drawing');
  var context = canvas.getContext('2d');
  var width   = window.innerWidth;
  var height  = window.innerHeight - $('#title').height();
  var socket  = io();

  var clickX = [];
  var clickY = [];
  var clickDrag = [];
  var paint;

  context.strokeStyle = "#000";
  context.lineJoin = "round";
  context.lineWidth = 5;

  // set canvas to full browser width/height
  canvas.width = width;
  canvas.height = height;

  socket.on('draw_line', function(data) {
    clickX.push(data.clickX * width);
    clickY.push(data.clickY * height);
    clickDrag.push(data.clickDrag);
    if(data.redraw)
      redraw();
    else
      drawNew();
  });

  function drawNew() {
    var i = clickX.length - 1
    if (!clickDrag[i]) {
      if (clickX.length == 0) {
        context.beginPath();
        context.moveTo(clickX[i], clickY[i]);
        context.stroke();
      } else {
        context.closePath();

        context.beginPath();
        context.moveTo(clickX[i], clickY[i]);
        context.stroke();
      }
    } else {
      context.lineTo(clickX[i], clickY[i]);
      context.stroke();
    }
  }

  function redraw() {
    // Clears the canvas
    context.clearRect(0, 0, context.canvas.width, context.canvas.height);

    for (var i = 0; i < clickX.length; i += 1) {
      if (!clickDrag[i] && i == 0) {
        context.beginPath();
        context.moveTo(clickX[i], clickY[i]);
        context.stroke();
      } else if (!clickDrag[i] && i > 0) {
        context.closePath();

        context.beginPath();
        context.moveTo(clickX[i], clickY[i]);
        context.stroke();
      } else {
        context.lineTo(clickX[i], clickY[i]);
        context.stroke();
      }
    }
  }
});
<|endoftext|>


<commit_before>Ext.ux.TokenField = Ext.extend(Ext.form.Text,  {

    // what this field returns.
    // 'array' returns an array
    // 'string' returns values as a string seperated by <tokenSeperator> so a csv string by default
    returnType: 'array',

    // set this to split tokens on something other than comma
    tokenSeperator: ',',

    tokenTpl: [
        '<tpl for="tokens">',
            '',
                '{.}',
                '',
            '',
        '</tpl>'
    ],

    KEY_BACK_SPACE: 8,
    KEY_ENTER: 13,

    // fetch the current value as an array
    getData: function(){
        return this.value || [];
    },

    // get each ux-token + the current value of ux-token-input
    // return either array or csv based on this.returnType
    getValue: function(){
        var data = [],
            active = this.getInputValue();
        data = data.concat(this.getData());
        if(active)
            data.push(active);
        return this.returnType=='array' ? data : data.join(this.tokenSeperator);
    },

    setValue: function(values){
        if(typeof values == 'string')
            this.value = values.split(this.tokenSeperator);
        else if(Ext.isArray(values))
            this.value = values;
        else
            console.warn("TokenField only supports string or array values was given: ",values);
        this.updateTokenTpl();
    },

    removeToken: function(idx){
        var data = this.getData();
        if(idx===-1)
            idx = data.length-1;
        data.splice(idx,1);
        this.updateTokenTpl();
    },

    getInputValue: function(){
        if(this.fieldEl)
            return this.fieldEl.dom.value.trim();
    },

    appendToken: function(){
        var token = this.getInputValue();
        if(token)
            this.getData().push(token.replace(/,$/,'').trim());
        this.updateTokenTpl();
    },

    clearInput: function(){
        this.fieldEl.dom.value = '';
    },

    updateTokenTpl: function(){
        if(this.fieldEl && this.el && this.el.dom)
            this.tokenTpl.overwrite(this.el.down('.ux-tokens'), {tokens:this.getData()});
    },

    // dynamically resize input width to fit word
    // well... try to anyway
    resizeInput: function(){
        if(!this.fieldEl)
            return;
        var w = this.fieldEl.dom.value.length*14;
        this.fieldEl.setStyle('width', (w<75 ? 75 : w)+'px');
    },

    onChangeInput: function(input, e){
        if(!this.fieldEl)
            return;
        // trigger event for some keys
        var c = this.fieldEl.dom.value[this.fieldEl.dom.value.length-1];
        if(e.browserEvent.keyCode == this.KEY_BACK_SPACE){
            if(!this.fieldEl.dom.value){
                if(this.preventRemoveToken){
                    this.preventRemoveToken = false;
                }else{
                    this.removeToken(-1);
                }
            }
            return;
        }else if(e.browserEvent.keyCode == this.KEY_ENTER || c == this.tokenSeperator){
            this.appendToken();
            this.clearInput();
        }
        this.resizeInput();
        this.preventRemoveToken = true;
    },

    onFieldTap: function(e){
        e.preventDefault();
        if(!this.fieldEl)
            return;
        var idx,
            node = Ext.get(e.target);
        if((parent = node.up('.ux-token'))){
            node = parent;
        }
        if(node.hasCls('ux-token')){
            idx = node.getAttribute('data-idx');
            this.removeToken(parseInt(idx-1,10));
        }
        this.fieldEl.dom.focus();
    },

    initRenderData: function() {
        this.renderData = Ext.ux.TokenField.superclass.initRenderData.apply(this, arguments);
        this.renderData.tokens = this.getData();
        return this.renderData;
    },

    initComponent: function(){
        Ext.ux.TokenField.superclass.initComponent.apply(this,arguments);
        // init template
        this.renderTpl = [
            '<tpl if="label">',
                '
{label}
',
            '</tpl>',
            '<tpl if="fieldEl">',
                '
',
                    ''];
        this.renderTpl = this.renderTpl.concat(this.tokenTpl);
        this.renderTpl = this.renderTpl.concat('',
                    '<input id="{inputId}" style="width:50px; display:inline-block;" type="text" name="{name}" class="{fieldCls} ux-token-input" autocorrect="false" autocapitalize="false", autocomplete="false">',
                    '<tpl if="useMask">

</tpl>', //remove?
                '
',
            '</tpl>');
        this.tokenTpl = new Ext.XTemplate(this.tokenTpl);
        // listen for keypresses
        this.on('keyup',this.onChangeInput);
        //
        this.on('click',this.onFieldTap, this, {element:"el"});
    }
});

Ext.reg('tokenfield', Ext.ux.TokenField);<commit_msg>example<commit_after>Ext.ux.TokenField = Ext.extend(Ext.form.Text,  {

    // what this field returns.
    // 'array' returns an array
    // 'string' returns values as a string seperated by <tokenSeperator> so a csv string by default
    returnType: 'array',

    // set this to split tokens on something other than comma
    tokenSeperator: ',',

    tokenTpl: [
        '<tpl for="tokens">',
            '',
                '{.}',
                '',
            '',
        '</tpl>'
    ],

    KEY_BACK_SPACE: 8,
    KEY_ENTER: 13,

    // fetch the current value as an array
    getData: function(){
        return this.value || [];
    },

    // get each ux-token + the current value of ux-token-input
    // return either array or csv based on this.returnType
    getValue: function(){
        var data = [],
            active = this.getInputValue();
        data = data.concat(this.getData());
        if(active)
            data.push(active);
        return this.returnType=='array' ? data : data.join(this.tokenSeperator);
    },

    setValue: function(values){
        if(typeof values == 'string')
            this.value = values.split(this.tokenSeperator);
        else if(Ext.isArray(values))
            this.value = values;
        else
            console.warn("TokenField only supports string or array values was given: ",values);
        this.updateTokenTpl();
    },

    removeToken: function(idx){
        var data = this.getData();
        if(idx===-1)
            idx = data.length-1;
        data.splice(idx,1);
        this.updateTokenTpl();
    },

    getInputValue: function(){
        if(this.fieldEl)
            return this.fieldEl.dom.value.trim();
    },

    appendToken: function(){
        var token = this.getInputValue();
        if(token)
            this.getData().push(token.replace(/,$/,'').trim());
        this.updateTokenTpl();
    },

    clearInput: function(){
        this.fieldEl.dom.value = '';
    },

    updateTokenTpl: function(){
        if(this.fieldEl && this.el && this.el.dom)
            this.tokenTpl.overwrite(this.el.down('.ux-tokens'), {tokens:this.getData()});
    },

    // dynamically resize input width to fit word
    // well... try to anyway
    resizeInput: function(){
        if(!this.fieldEl)
            return;
        var w = this.fieldEl.dom.value.length*14;
        this.fieldEl.setStyle('width', (w<75 ? 75 : w)+'px');
    },

    onChangeInput: function(input, e){
        if(!this.fieldEl)
            return;
        // trigger event for some keys
        var c = this.fieldEl.dom.value[this.fieldEl.dom.value.length-1];
        if(e.browserEvent.keyCode == this.KEY_BACK_SPACE){
            if(!this.fieldEl.dom.value){
                if(this.preventRemoveToken){
                    this.preventRemoveToken = false;
                }else{
                    this.removeToken(-1);
                }
            }
            return;
        }else if(e.browserEvent.keyCode == this.KEY_ENTER || c == this.tokenSeperator){
            this.appendToken();
            this.clearInput();
        }
        this.resizeInput();
        this.preventRemoveToken = true;
    },

    onFieldTap: function(e){
        e.preventDefault();
        if(!this.fieldEl)
            return;
        var idx,
            node = Ext.get(e.target);
        if((parent = node.up('.ux-token'))){
            node = parent;
        }
        if(node.hasCls('ux-token')){
            idx = node.getAttribute('data-idx');
            this.removeToken(parseInt(idx-1,10));
        }
        this.fieldEl.dom.focus();
    },

    initRenderData: function() {
        this.renderData = Ext.ux.TokenField.superclass.initRenderData.apply(this, arguments);
        this.renderData.tokens = this.getData();
        return this.renderData;
    },

    initComponent: function(){
        Ext.ux.TokenField.superclass.initComponent.apply(this,arguments);
        // init template
        this.renderTpl = [
            '<tpl if="label">',
                '
{label}
',
            '</tpl>',
            '<tpl if="fieldEl">',
                '
',
                    ''];
        this.renderTpl = this.renderTpl.concat(this.tokenTpl);
        this.renderTpl = this.renderTpl.concat('',
                    '<input id="{inputId}" style="width:50px; display:inline-block;" type="text" name="{name}" class="{fieldCls} ux-token-input" autocorrect="false" autocapitalize="false", autocomplete="false">',
                    '<tpl if="useMask">

</tpl>', //remove?
                '
',
            '</tpl>');
        this.tokenTpl = new Ext.XTemplate(this.tokenTpl);
        // listen for keypresses
        this.on('keyup',this.onChangeInput);
        //
        this.on('singletap',this.onFieldTap, this, {element:"el"});
    }
});

Ext.reg('tokenfield', Ext.ux.TokenField);<|endoftext|>


<commit_before>//Import events
var events = require('events');

//Create EventHandler function
var EventHandler = function() {
  //Call the EventEmitter constructor on this object
  events.EventEmitter.call(this);
  //Create the registered event
  this.registered = function() {
    this.emit('registered');
  }
  //Request event
  //callback is the callback used once work is received
  this.request = function(callback) {
    this.emit('request', callback);
  }

  //Submit event
  //callback is the callback once submitted work is confirmed
  this.submit = function(callback) {
    this.emit('submit', callback);
  }
}

EventHandler.prototype.__proto__ = events.EventEmitter.prototype;

module.exports = EventHandler;
<commit_msg>Made sure that all data is passed to listener<commit_after>//Import events
var events = require('events');

//Create EventHandler function
var EventHandler = function() {
  //Call the EventEmitter constructor on this object
  events.EventEmitter.call(this);
  //Create the registered event
  this.registered = function() {
    this.emit('registered');
  }
  //Request event
  //callback is the callback used once work is received
  this.request = function(callback) {
    this.emit('request', callback);
  }

  //Submit event
  //callback is the callback once submitted work is confirmed
  this.submit = function(data, callback) {
    this.emit('submit', data, callback);
  }
}

EventHandler.prototype.__proto__ = events.EventEmitter.prototype;

module.exports = EventHandler;
<|endoftext|>


<commit_before>module.exports = function(RED)
{
	"use strict";

	function HueLight(config)
	{
		RED.nodes.createNode(this, config);
		var bridge = RED.nodes.getNode(config.bridge);
		let huejay = require('huejay');
		var moment = require('moment');
		let rgb = require('../utils/rgb');
		let rgbHex = require('rgb-hex');
		let hexRGB = require('hex-rgb');
		var context = this.context();
		var scope = this;

		//
		// CHECK CONFIG
		if(bridge == null)
		{
			this.status({fill: "red", shape: "ring", text: "not configured"});
			return false;
		}

		//
		// INITIALIZE CLIENT
		var lightID = (config.lightid) ? parseInt(config.lightid) : false;
		let client = new huejay.Client({
			host: (bridge.config.bridge).toString(),
			port: 80,
			username: bridge.config.key
		});

		//
		// UPDATE STATE
		scope.status({fill: "grey", shape: "dot", text: "initializing…"});

		if(lightID != false)
		{
			this.recheck = setInterval(function()
			{
				client.lights.getById(lightID)
				.then(light => {
					if(light.reachable == false)
					{
						scope.status({fill: "red", shape: "ring", text: "not reachable"});
					}
					else
					{
						var state = context.get('status') || false;
						var uniqueStatus = ((light.on) ? "1" : "0") + light.brightness + light.hue + light.saturation + light.colorTemp;
						var brightnessPercent = 0;

						if(state != uniqueStatus)
						{
							context.set('status', uniqueStatus);

							if(light.on)
							{
								brightnessPercent = Math.round((100/254)*light.brightness);
								scope.status({fill: "yellow", shape: "dot", text: "turned on ("+ brightnessPercent +"%)"});
							}
							else
							{
								scope.status({fill: "grey", shape: "dot", text: "turned off"});
							}

							// DETERMINE TYPE AND SEND STATUS
							var message = {};
							message.payload = {};
							message.payload.on = light.on;
							message.payload.brightness = brightnessPercent;

							message.info = {};
							message.info.id = light.id;
							message.info.uniqueId = light.uniqueId;
							message.info.name = light.name;
							message.info.type = light.type;
							message.info.softwareVersion = light.softwareVersion;

							message.info.model = {};;
							message.info.model.id = light.model.id;
							message.info.model.manufacturer = light.model.manufacturer;
							message.info.model.name = light.model.name;
							message.info.model.type = light.model.type;
							message.info.model.colorGamut = light.model.colorGamut;
							message.info.model.friendsOfHue = light.model.friendsOfHue;

							if(light.xy)
							{
								var rgbColor = rgb.convertXYtoRGB(light.xy[0], light.xy[1], light.brightness);

								message.payload.rgb = rgbColor;
								message.payload.hex = rgbHex(rgbColor[0], rgbColor[1], rgbColor[2]);
							}

							message.payload.updated = moment().format();

							scope.send(message);
						}
					}
				})
				.catch(error => {
					scope.status({fill: "red", shape: "ring", text: "connection error"});
					clearInterval(scope.recheck);
				});
			}, parseInt(bridge.config.interval));
		}
		else
		{
			scope.status({fill: "grey", shape: "dot", text: "universal mode"});
		}

		//
		// TURN ON / OFF LIGHT
		this.on('input', function(msg)
		{
			var context = this.context();

			// CHECK IF LIGHT ID IS SET
			if(lightID == false)
			{
				if(typeof msg.topic != 'undefined')
				{
					lightID = parseInt(msg.topic);
				}
				else
				{
					scope.error("No light Id defined. Please check the docs.");
					return false;
				}
			}

			// SIMPLE TURN ON / OFF LIGHT
			if(msg.payload == true || msg.payload == false)
			{
				if(lightID != false)
				{
					client.lights.getById(lightID)
					.then(light => {
						if(light.reachable)
						{
							light.on = msg.payload;
							return client.lights.save(light);
						}
						else
						{
							scope.status({fill: "red", shape: "ring", text: "not reachable"});
							return false;
						}
					})
					.then(light => {
						if(light != false)
						{
							scope.sendLightStatus(light);
						}
					})
					.catch(error => {
						scope.error(error);
						scope.status({fill: "red", shape: "ring", text: "input error"});
						clearInterval(scope.recheck);
					});
				}
			}
			// EXTENDED TURN ON / OFF LIGHT
			else if(typeof msg.payload.on != 'undefined')
			{
				client.lights.getById(lightID)
				.then(light => {
					if(light.reachable)
					{
						light.on = msg.payload.on;

						// SET BRIGHTNESS
						if(msg.payload.brightness)
						{
							light.brightness = Math.round((254/100)*parseInt(msg.payload.brightness));
						}

						// SET RGB COLOR
						if(msg.payload.rgb && light.xy)
						{
							light.xy = rgb.convertRGBtoXY(msg.payload.rgb, light.modelid);
						}

						// SET HEX COLOR
						if(msg.payload.hex && light.xy)
						{
							light.xy = rgb.convertRGBtoXY(hexRGB((msg.payload.hex).toString()), light.modelid);
						}

						// SET TRANSITION TIME
						if(msg.payload.transitionTime)
						{
							light.transitionTime = parseInt(msg.payload.transitionTime);
						}

						// SET COLORLOOP EFFECT
						if(msg.payload.colorloop && msg.payload.colorloop > 0 && light.xy)
						{
							light.effect = 'colorloop';

							// DISABLE AFTER
							setTimeout(function() {
								light.effect = 'none';
								client.lights.save(light);
							}, parseInt(msg.payload.colorloop)*1000);
						}

						return client.lights.save(light);
					}
					else
					{
						scope.status({fill: "red", shape: "ring", text: "not reachable"});
						return false;
					}
				})
				.then(light => {
					if(light != false)
					{
						scope.sendLightStatus(light);
					}
				})
				.catch(error => {
					scope.error(error);
					scope.status({fill: "red", shape: "ring", text: "input error"});
					clearInterval(scope.recheck);
				});
			}
			// ALERT EFFECT
			else if(typeof msg.payload.alert != 'undefined' && msg.payload.alert > 0)
			{
				client.lights.getById(lightID)
				.then(light => {
					if(light.reachable)
					{
						context.set('lightPreviousState', [light.on ? true : false, light.brightness, light.xy ? light.xy : false]);

						// SET ALERT COLOR
						if(light.xy)
						{
							if(typeof msg.payload.rgb != 'undefined')
							{
								light.xy = rgb.convertRGBtoXY(msg.payload.rgb, light.modelid);
							}
							else if(typeof msg.payload.hex != 'undefined')
							{
								light.xy = rgb.convertRGBtoXY(hexRGB((msg.payload.hex).toString()), light.modelid);
							}
							else
							{
								light.xy = rgb.convertRGBtoXY([255,0,0], light.modelid);
							}
						}

						// ACTIVATE
						light.on = true;
						light.brightness = 254;
						light.transitionTime = 0;
						return client.lights.save(light);
					}
					else
					{
						scope.status({fill: "red", shape: "ring", text: "not reachable"});
						return false;
					}
				})
				.then(light => {
					// ACTIVATE ALERT
					if(light != false)
					{
						light.alert = 'lselect';
						return client.lights.save(light);
					}
					else
					{
						return false;
					}
				})
				.then(light => {
					// TURN OFF ALERT
					if(light != false)
					{
						var lightPreviousState = context.get('lightPreviousState');
						var alertSeconds = parseInt(msg.payload.alert);

						setTimeout(function() {
							light.on = lightPreviousState[0];
							light.alert = 'none';
							light.brightness = lightPreviousState[1];
							light.transitionTime = 2;

							if(lightPreviousState[2] != false)
							{
								light.xy = lightPreviousState[2];
							}

							client.lights.save(light);
						}, alertSeconds * 1000);
					}
				})
				.catch(error => {
					scope.error(error);
					scope.status({fill: "red", shape: "ring", text: "input error"});
					clearInterval(scope.recheck);
				});
			}
		});

		//
		// SEND LIGHT STATUS
		this.sendLightStatus = function(light)
		{
			var scope = this;

			var state = scope.context().get('status') || false;
			var uniqueStatus = ((light.on) ? "1" : "0") + light.brightness + light.hue + light.saturation + light.colorTemp;
			var brightnessPercent = 0;

			if(light.on)
			{
				brightnessPercent = Math.round((100/254)*light.brightness);
				scope.status({fill: "yellow", shape: "dot", text: "turned on ("+ brightnessPercent +"%)"});
			}
			else
			{
				scope.status({fill: "grey", shape: "dot", text: "turned off"});
			}

			// DETERMINE TYPE AND SEND STATUS
			var message = {};
			message.payload = {};
			message.payload.on = light.on;
			message.payload.brightness = brightnessPercent;

			message.info = {};
			message.info.id = light.id;
			message.info.uniqueId = light.uniqueId;
			message.info.name = light.name;
			message.info.type = light.type;
			message.info.softwareVersion = light.softwareVersion;

			message.info.model = {};;
			message.info.model.id = light.model.id;
			message.info.model.manufacturer = light.model.manufacturer;
			message.info.model.name = light.model.name;
			message.info.model.type = light.model.type;
			message.info.model.colorGamut = light.model.colorGamut;
			message.info.model.friendsOfHue = light.model.friendsOfHue;

			if(light.xy)
			{
				var rgbColor = rgb.convertXYtoRGB(light.xy[0], light.xy[1], light.brightness);

				message.payload.rgb = rgbColor;
				message.payload.hex = rgbHex(rgbColor[0], rgbColor[1], rgbColor[2]);
			}

			message.payload.updated = moment().format();

			scope.send(message);
			scope.context().set('status', uniqueStatus);
		}

		//
		// CLOSE NDOE / REMOVE RECHECK INTERVAL
		this.on('close', function()
		{
			clearInterval(scope.recheck);
		});
	}

	RED.nodes.registerType("hue-light", HueLight);
}<commit_msg>Added basic color temperature support<commit_after>module.exports = function(RED)
{
	"use strict";

	function HueLight(config)
	{
		RED.nodes.createNode(this, config);
		var bridge = RED.nodes.getNode(config.bridge);
		let huejay = require('huejay');
		var moment = require('moment');
		let rgb = require('../utils/rgb');
		let rgbHex = require('rgb-hex');
		let hexRGB = require('hex-rgb');
		var context = this.context();
		var scope = this;

		//
		// CHECK CONFIG
		if(bridge == null)
		{
			this.status({fill: "red", shape: "ring", text: "not configured"});
			return false;
		}

		//
		// INITIALIZE CLIENT
		var lightID = (config.lightid) ? parseInt(config.lightid) : false;
		let client = new huejay.Client({
			host: (bridge.config.bridge).toString(),
			port: 80,
			username: bridge.config.key
		});

		//
		// UPDATE STATE
		scope.status({fill: "grey", shape: "dot", text: "initializing…"});

		if(lightID != false)
		{
			this.recheck = setInterval(function()
			{
				client.lights.getById(lightID)
				.then(light => {
					if(light.reachable == false)
					{
						scope.status({fill: "red", shape: "ring", text: "not reachable"});
					}
					else
					{
						var state = context.get('status') || false;
						var uniqueStatus = ((light.on) ? "1" : "0") + light.brightness + light.hue + light.saturation + light.colorTemp;
						var brightnessPercent = 0;

						if(state != uniqueStatus)
						{
							context.set('status', uniqueStatus);

							if(light.on)
							{
								brightnessPercent = Math.round((100/254)*light.brightness);
								scope.status({fill: "yellow", shape: "dot", text: "turned on ("+ brightnessPercent +"%)"});
							}
							else
							{
								scope.status({fill: "grey", shape: "dot", text: "turned off"});
							}

							// DETERMINE TYPE AND SEND STATUS
							var message = {};
							message.payload = {};
							message.payload.on = light.on;
							message.payload.brightness = brightnessPercent;

							message.info = {};
							message.info.id = light.id;
							message.info.uniqueId = light.uniqueId;
							message.info.name = light.name;
							message.info.type = light.type;
							message.info.softwareVersion = light.softwareVersion;

							message.info.model = {};;
							message.info.model.id = light.model.id;
							message.info.model.manufacturer = light.model.manufacturer;
							message.info.model.name = light.model.name;
							message.info.model.type = light.model.type;
							message.info.model.colorGamut = light.model.colorGamut;
							message.info.model.friendsOfHue = light.model.friendsOfHue;

							if(light.xy)
							{
								var rgbColor = rgb.convertXYtoRGB(light.xy[0], light.xy[1], light.brightness);

								message.payload.rgb = rgbColor;
								message.payload.hex = rgbHex(rgbColor[0], rgbColor[1], rgbColor[2]);
							}

							message.payload.updated = moment().format();

							scope.send(message);
						}
					}
				})
				.catch(error => {
					scope.status({fill: "red", shape: "ring", text: "connection error"});
					clearInterval(scope.recheck);
				});
			}, parseInt(bridge.config.interval));
		}
		else
		{
			scope.status({fill: "grey", shape: "dot", text: "universal mode"});
		}

		//
		// TURN ON / OFF LIGHT
		this.on('input', function(msg)
		{
			var context = this.context();

			// CHECK IF LIGHT ID IS SET
			if(lightID == false)
			{
				if(typeof msg.topic != 'undefined')
				{
					lightID = parseInt(msg.topic);
				}
				else
				{
					scope.error("No light Id defined. Please check the docs.");
					return false;
				}
			}

			// SIMPLE TURN ON / OFF LIGHT
			if(msg.payload == true || msg.payload == false)
			{
				if(lightID != false)
				{
					client.lights.getById(lightID)
					.then(light => {
						if(light.reachable)
						{
							light.on = msg.payload;
							return client.lights.save(light);
						}
						else
						{
							scope.status({fill: "red", shape: "ring", text: "not reachable"});
							return false;
						}
					})
					.then(light => {
						if(light != false)
						{
							scope.sendLightStatus(light);
						}
					})
					.catch(error => {
						scope.error(error);
						scope.status({fill: "red", shape: "ring", text: "input error"});
						clearInterval(scope.recheck);
					});
				}
			}
			// EXTENDED TURN ON / OFF LIGHT
			else if(typeof msg.payload.on != 'undefined')
			{
				client.lights.getById(lightID)
				.then(light => {
					if(light.reachable)
					{
						light.on = msg.payload.on;

						// SET BRIGHTNESS
						if(msg.payload.brightness)
						{
							light.brightness = Math.round((254/100)*parseInt(msg.payload.brightness));
						}

						// SET RGB COLOR
						if(msg.payload.rgb && light.xy)
						{
							light.xy = rgb.convertRGBtoXY(msg.payload.rgb, light.modelid);
						}

						// SET HEX COLOR
						if(msg.payload.hex && light.xy)
						{
							light.xy = rgb.convertRGBtoXY(hexRGB((msg.payload.hex).toString()), light.modelid);
						}
						//SET COLOR TEMPERATURE
                                                if(msg.payload.colorTemp && light.colorTemp)
                                                {
                                                        let colorTemp = parseInt(msg.payload.colorTemp);
                                                        if(colorTemp >= 153 && colorTemp <= 500)
                                                        {
                                                                light.colorTemp = parseInt(msg.payload.colorTemp);
                                                        } else {
                                                                scope.error("Invalid color temprature. Only 153 - 500 allowed");
                                                                return false;
                                                        }
                                                }
						// SET TRANSITION TIME
						if(msg.payload.transitionTime)
						{
							light.transitionTime = parseInt(msg.payload.transitionTime);
						}

						// SET COLORLOOP EFFECT
						if(msg.payload.colorloop && msg.payload.colorloop > 0 && light.xy)
						{
							light.effect = 'colorloop';

							// DISABLE AFTER
							setTimeout(function() {
								light.effect = 'none';
								client.lights.save(light);
							}, parseInt(msg.payload.colorloop)*1000);
						}

						return client.lights.save(light);
					}
					else
					{
						scope.status({fill: "red", shape: "ring", text: "not reachable"});
						return false;
					}
				})
				.then(light => {
					if(light != false)
					{
						scope.sendLightStatus(light);
					}
				})
				.catch(error => {
					scope.error(error);
					scope.status({fill: "red", shape: "ring", text: "input error"});
					clearInterval(scope.recheck);
				});
			}
			// ALERT EFFECT
			else if(typeof msg.payload.alert != 'undefined' && msg.payload.alert > 0)
			{
				client.lights.getById(lightID)
				.then(light => {
					if(light.reachable)
					{
						context.set('lightPreviousState', [light.on ? true : false, light.brightness, light.xy ? light.xy : false]);

						// SET ALERT COLOR
						if(light.xy)
						{
							if(typeof msg.payload.rgb != 'undefined')
							{
								light.xy = rgb.convertRGBtoXY(msg.payload.rgb, light.modelid);
							}
							else if(typeof msg.payload.hex != 'undefined')
							{
								light.xy = rgb.convertRGBtoXY(hexRGB((msg.payload.hex).toString()), light.modelid);
							}
							else
							{
								light.xy = rgb.convertRGBtoXY([255,0,0], light.modelid);
							}
						}

						// ACTIVATE
						light.on = true;
						light.brightness = 254;
						light.transitionTime = 0;
						return client.lights.save(light);
					}
					else
					{
						scope.status({fill: "red", shape: "ring", text: "not reachable"});
						return false;
					}
				})
				.then(light => {
					// ACTIVATE ALERT
					if(light != false)
					{
						light.alert = 'lselect';
						return client.lights.save(light);
					}
					else
					{
						return false;
					}
				})
				.then(light => {
					// TURN OFF ALERT
					if(light != false)
					{
						var lightPreviousState = context.get('lightPreviousState');
						var alertSeconds = parseInt(msg.payload.alert);

						setTimeout(function() {
							light.on = lightPreviousState[0];
							light.alert = 'none';
							light.brightness = lightPreviousState[1];
							light.transitionTime = 2;

							if(lightPreviousState[2] != false)
							{
								light.xy = lightPreviousState[2];
							}

							client.lights.save(light);
						}, alertSeconds * 1000);
					}
				})
				.catch(error => {
					scope.error(error);
					scope.status({fill: "red", shape: "ring", text: "input error"});
					clearInterval(scope.recheck);
				});
			}
		});

		//
		// SEND LIGHT STATUS
		this.sendLightStatus = function(light)
		{
			var scope = this;

			var state = scope.context().get('status') || false;
			var uniqueStatus = ((light.on) ? "1" : "0") + light.brightness + light.hue + light.saturation + light.colorTemp;
			var brightnessPercent = 0;

			if(light.on)
			{
				brightnessPercent = Math.round((100/254)*light.brightness);
				scope.status({fill: "yellow", shape: "dot", text: "turned on ("+ brightnessPercent +"%)"});
			}
			else
			{
				scope.status({fill: "grey", shape: "dot", text: "turned off"});
			}

			// DETERMINE TYPE AND SEND STATUS
			var message = {};
			message.payload = {};
			message.payload.on = light.on;
			message.payload.brightness = brightnessPercent;

			message.info = {};
			message.info.id = light.id;
			message.info.uniqueId = light.uniqueId;
			message.info.name = light.name;
			message.info.type = light.type;
			message.info.softwareVersion = light.softwareVersion;

			message.info.model = {};;
			message.info.model.id = light.model.id;
			message.info.model.manufacturer = light.model.manufacturer;
			message.info.model.name = light.model.name;
			message.info.model.type = light.model.type;
			message.info.model.colorGamut = light.model.colorGamut;
			message.info.model.friendsOfHue = light.model.friendsOfHue;

			if(light.xy)
			{
				var rgbColor = rgb.convertXYtoRGB(light.xy[0], light.xy[1], light.brightness);

				message.payload.rgb = rgbColor;
				message.payload.hex = rgbHex(rgbColor[0], rgbColor[1], rgbColor[2]);
			}

			message.payload.updated = moment().format();

			scope.send(message);
			scope.context().set('status', uniqueStatus);
		}

		//
		// CLOSE NDOE / REMOVE RECHECK INTERVAL
		this.on('close', function()
		{
			clearInterval(scope.recheck);
		});
	}

	RED.nodes.registerType("hue-light", HueLight);
}
<|endoftext|>


<commit_before>
/*
print(true.toString());
print((3.14).toString());

bar = {};
bar.x = 2;
*/

/*
print(Math.abs("-3.14"));
print(Math.abs());
print(Math.max(1, 10, 20, 5));
print(Math.min(5, 6, 3, 10));
print(Math.PI.toString());
*/

for(var i = 0; i < 10; ++i)
    test[i] = "#" + i.toString();

print(test.length);

/*
try {
    throw bar;
} catch (x) {
    print(x.x);
}

prettyPrint = function (str) {
    println("~*~ " + str + " ~*~");
};

address = function (street, zipcode, city) {
    this.street = street;
    this.zipcode = zipcode;
    this.city = city;
    this.print = function () {
        println("");
        prettyPrint(street);
        println(zipcode + " " + city);
    };
};

user = function (name, age) {
    this.name = name;
    this.age = age + 1;
    this.address = new address("Backa Ringgata 26", 51163, "Skene");

    test = this;

    this.print = function (testline) {
        println("line: " + testline);
        println("Name: " + this.name);
        println("Age: " + age + ", real: " + this.age);
    };
};

user.prototype.lol = "tjo";
fredrik = new user("Fredrik", 24);
anders = new user("Anders", 22);

with (fredrik) {
    print("1");
    anders.print("2");
    address.print("3");
    prettyPrint(" hello world ");
}

println("foo".lol); // <- undefined

foo = { "a": 1, "b": 2, "c": 3 };

delete foo.a;
delete foo["c"];

for (bar.key in foo) {
    println(bar.key + ": " + foo[bar.key]);
};
*/<commit_msg>uncommented some code<commit_after>
/*
print(true.toString());
print((3.14).toString());

bar = {};
bar.x = 2;
*/

/*
print(Math.abs("-3.14"));
print(Math.abs());
print(Math.max(1, 10, 20, 5));
print(Math.min(5, 6, 3, 10));
print(Math.PI.toString());
*/

for(var i = 0; i < 10; ++i)
    test[i] = "#" + i.toString();

print(test.length);

prettyPrint = function (str) {
    println("~*~ " + str + " ~*~");
};

address = function (street, zipcode, city) {
    this.street = street;
    this.zipcode = zipcode;
    this.city = city;
    this.print = function () {
        println("");
        prettyPrint(street);
        println(zipcode + " " + city);
    };
};

user = function (name, age) {
    this.name = name;
    this.age = age + 1;
    this.address = new address("Backa Ringgata 26", 51163, "Skene");

    test = this;

    this.print = function (testline) {
        println("line: " + testline);
        println("Name: " + this.name);
        println("Age: " + age + ", real: " + this.age);
    };
};

user.prototype.lol = "tjo";
fredrik = new user("Fredrik", 24);
anders = new user("Anders", 22);

with (fredrik) {
    print("1");
    anders.print("2");
    address.print("3");
    prettyPrint(" hello world ");
}

println("foo".lol); // <- undefined

foo = { "a": 1, "b": 2, "c": 3 };

delete foo.a;
delete foo["c"];

for (bar.key in foo) {
    println(bar.key + ": " + foo[bar.key]);
};
<|endoftext|>


<commit_before>function player(name) {
    this.money = 15,
    this.properties = 1,
    this.workers = 0,
    this.name = name;
    //this.shares = 5
}

var me = new player("You");
var other = new player("Person 2");
var person = new player("Person 3");
var ai1 = new player("AI1");
var ai2 = new player("AI2");
var ai3 = new player("AI3");

var winner;<commit_msg>Adding a new person<commit_after>function player(name) {
    this.money = 15,
    this.properties = 1,
    this.workers = 0,
    this.name = name;
    //this.shares = 5
}

var me = new player("You");
var other = new player("Person 2");
var person = new player("Person 3");
var newPerson = new player("Person 4");
var ai1 = new player("AI1");
var ai2 = new player("AI2");
var ai3 = new player("AI3");

var winner;
<|endoftext|>


<commit_before>/**
 * Created by andre on 20/01/2017.
 */
$( document ).ready(function() {
    $('.button-collapse').sideNav();
    $('.parallax').parallax();
    $('.modal').modal();
    $('select').material_select();

    $("#flag").click(function() {
        var lang = Cookies.get('lang');
        if(lang === 'es-es') {
            Cookies.set('lang', 'en-us');
        } else {
            Cookies.set('lang', 'es-es');
        }
        location.reload(true);
    });
});<commit_msg>doble comillas en init<commit_after>/**
 * Created by andre on 20/01/2017.
 */
$( document ).ready(function() {
    $(".button-collapse").sideNav();
    $(".parallax").parallax();
    $(".modal").modal();
    $("select").material_select();

    $("#flag").click(function() {
        var lang = Cookies.get("lang");
        if(lang === "es-es") {
            Cookies.set("lang", "en-us");
        } else {
            Cookies.set("lang", "es-es");
        }
        location.reload(true);
    });
});<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule Modal
 * @flow
 */
'use strict';

var PropTypes = require('ReactPropTypes');
var React = require('React');
var StyleSheet = require('StyleSheet');
var View = require('View');

var requireNativeComponent = require('requireNativeComponent');
var RCTModalHostView = requireNativeComponent('RCTModalHostView', null);

/**
 * A Modal component covers the native view (e.g. UIViewController, Activity)
 * that contains the React Native root.
 *
 * Use Modal in hybrid apps that embed React Native; Modal allows the portion of
 * your app written in React Native to present content above the enclosing
 * native view hierarchy.
 *
 * In apps written with React Native from the root view down, you should use
 * Navigator instead of Modal. With a top-level Navigator, you have more control
 * over how to present the modal scene over the rest of your app.
 */
class Modal extends React.Component {
  render(): ?ReactElement {
    if (this.props.visible === false) {
      return null;
    }

    if (this.props.transparent) {
      var containerBackgroundColor = {backgroundColor: 'transparent'};
    }

    return (
      <RCTModalHostView
        animated={this.props.animated}
        transparent={this.props.transparent}
        onDismiss={this.props.onDismiss}
        style={styles.modal}>
        <View style={[styles.container, containerBackgroundColor]}>
          {this.props.children}
        </View>
      </RCTModalHostView>
    );
  }
}

Modal.propTypes = {
  animated: PropTypes.bool,
  transparent: PropTypes.bool,
  onDismiss: PropTypes.func,
};

var styles = StyleSheet.create({
  modal: {
    position: 'absolute',
  },
  container: {
    left: 0,
    position: 'absolute',
    top: 0,
  }
});

module.exports = Modal;
<commit_msg>Documentation Update<commit_after>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule Modal
 * @flow
 */
'use strict';

var PropTypes = require('ReactPropTypes');
var React = require('React');
var StyleSheet = require('StyleSheet');
var View = require('View');

var requireNativeComponent = require('requireNativeComponent');
var RCTModalHostView = requireNativeComponent('RCTModalHostView', null);

/**
 * A Modal component covers the native view (e.g. UIViewController, Activity)
 * that contains the React Native root.
 *
 * Use Modal in hybrid apps that embed React Native; Modal allows the portion of
 * your app written in React Native to present content above the enclosing
 * native view hierarchy.
 *
 * In apps written with React Native from the root view down, you should use
 * Navigator instead of Modal. With a top-level Navigator, you have more control
 * over how to present the modal scene over the rest of your app by using the
 * configureScene property.
 */
class Modal extends React.Component {
  render(): ?ReactElement {
    if (this.props.visible === false) {
      return null;
    }

    if (this.props.transparent) {
      var containerBackgroundColor = {backgroundColor: 'transparent'};
    }

    return (
      <RCTModalHostView
        animated={this.props.animated}
        transparent={this.props.transparent}
        onDismiss={this.props.onDismiss}
        style={styles.modal}>
        <View style={[styles.container, containerBackgroundColor]}>
          {this.props.children}
        </View>
      </RCTModalHostView>
    );
  }
}

Modal.propTypes = {
  animated: PropTypes.bool,
  transparent: PropTypes.bool,
  onDismiss: PropTypes.func,
};

var styles = StyleSheet.create({
  modal: {
    position: 'absolute',
  },
  container: {
    left: 0,
    position: 'absolute',
    top: 0,
  }
});

module.exports = Modal;
<|endoftext|>