/*jshint browser:true, strict:false, curly:false, indent:3*/

/*
 * hist.js
 *
 * Copyright (C) 2009-18 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

var hist = function (ele, breaks, counts, update) {

   // find the largest count for normalization
   var max = counts[0];
   for(var i = 1; i < counts.length; i++)
   {
      if (counts[i] > max)
         max = counts[i];
   }

   // arrays of histogram bars and background regions
   var bars = [];
   var backs = [];

   // selection model; initially everything is selected
   var start = 0;
   var end = counts.length - 1;

   // create elements for each bin
   var n = counts.length;
   for(var j = 0; j < n; j++)
   {
      // the histogram bar
      var bar = document.createElement("div");
      bar.className = "histBar";
      var style = bar.style;
      var height = (counts[j] / max) * 100;
      style.height = height + "%";
      style.width = "100%";
      style.position = "absolute";
      style.left = "0";
      style.top = (100 - height) + "%";

      // the background of the histogram bar
      var back = document.createElement("div");
      back.className = "histBack";
      back.style.height = "100%";
      back.style.position = "absolute";
      back.style.width = (100 / n) + "%";
      back.style.left = ((j / n) * 100) + "%";
      back.setAttribute("data-bin", j);
      back.appendChild(bar);
      backs.push(back);

      ele.appendChild(back);
   }

   // update selection based on start/end points
   var updateSelection = function() {
      for (var i = 0; i < backs.length; i++) {
         backs[i].className = (i >= start && i <= end) ?
            "histBack selected" : "histBack";
      }
      update(breaks[start], breaks[end + 1]);
   };

   // given an element, find the bin to which it corresponds, or null if the the bin could not be
   // determined
   var findBin = function(ele) {
      var bin = ele.getAttribute("data-bin");
      if (bin === null) {
         bin = ele.parentElement.getAttribute("data-bin");
      }
      if (bin === null) {
         return null;
      }
      return parseInt(bin);
   };

   // given a mouse event, update the selected bins
   var brush = function(evt) {
      // determine whether a bin was brushed
      var bin = findBin(evt.target);
      if (bin === null) {
         return;
      }

      // did the brush extend either side of the selection?
      if (bin >= start && bin <= end) {
         return;
      }

      // grow selection appropriately
      if (bin < start)
         start = bin;
      else if (bin > end)
         end = bin;

      // redraw
      updateSelection();
   };

   // perform initial redraw
   updateSelection();

   // set up event listeners
   ele.addEventListener("mousedown", function(evt) {
      var bin = findBin(evt.target);
      if (bin === null) {
         return;
      }
      start = bin;
      end = bin;
      updateSelection();
      ele.addEventListener("mousemove", brush);
   });

   ele.addEventListener("mouseup", function(evt) {
      ele.removeEventListener("mousemove", brush);
   });

   ele.addEventListener("mouseout", function(evt) {
      if (evt.target === ele)
         ele.removeEventListener("mousemove", brush);
   });
};

@@ -2823,162 +2823,282 @@
  // 
-did the brush extend either side of the selection?%0A      if (bin %3E= start && bin %3C= end) %7B%0A         return;%0A      %7D%0A%0A      // grow selection appropriately
+no work to do if we're already at an endpoint%0A      if (bin === end %7C%7C bin === start) %7B%0A         return;%0A      %7D%0A%0A      // move end marker to brushed bin%0A      var last = end;%0A      end = bin;%0A%0A      // if we went backwards, swap start and end so that they're still in order
 %0A   
@@ -3104,19 +3104,19 @@
     if (
-bin
+end
  %3C start
@@ -3116,16 +3116,18 @@
 %3C start)
+ %7B
 %0A       
@@ -3136,27 +3136,27 @@
 start = 
-bin
+end
 ;%0A      
 else if 
@@ -3151,47 +3151,30 @@
     
-else if (bin %3E end)%0A         end = bin;
+   end = last;%0A      %7D
 %0A%0A  

allow shrinking selection as well as growing it