browser/devtools/shared/widgets/Spectrum.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 "use strict";
     7 const EventEmitter = require("devtools/toolkit/event-emitter");
     9 /**
    10  * Spectrum creates a color picker widget in any container you give it.
    11  *
    12  * Simple usage example:
    13  *
    14  * const {Spectrum} = require("devtools/shared/widgets/Spectrum");
    15  * let s = new Spectrum(containerElement, [255, 126, 255, 1]);
    16  * s.on("changed", (event, rgba, color) => {
    17  *   console.log("rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ", " + rgba[3] + ")");
    18  * });
    19  * s.show();
    20  * s.destroy();
    21  *
    22  * Note that the color picker is hidden by default and you need to call show to
    23  * make it appear. This 2 stages initialization helps in cases you are creating
    24  * the color picker in a parent element that hasn't been appended anywhere yet
    25  * or that is hidden. Calling show() when the parent element is appended and
    26  * visible will allow spectrum to correctly initialize its various parts.
    27  *
    28  * Fires the following events:
    29  * - changed : When the user changes the current color
    30  */
    31 function Spectrum(parentEl, rgb) {
    32   EventEmitter.decorate(this);
    34   this.element = parentEl.ownerDocument.createElement('div');
    35   this.parentEl = parentEl;
    37   this.element.className = "spectrum-container";
    38   this.element.innerHTML = [
    39     "<div class='spectrum-top'>",
    40       "<div class='spectrum-fill'></div>",
    41       "<div class='spectrum-top-inner'>",
    42         "<div class='spectrum-color spectrum-box'>",
    43           "<div class='spectrum-sat'>",
    44             "<div class='spectrum-val'>",
    45               "<div class='spectrum-dragger'></div>",
    46             "</div>",
    47           "</div>",
    48         "</div>",
    49         "<div class='spectrum-hue spectrum-box'>",
    50           "<div class='spectrum-slider spectrum-slider-control'></div>",
    51         "</div>",
    52       "</div>",
    53     "</div>",
    54     "<div class='spectrum-alpha spectrum-checker spectrum-box'>",
    55       "<div class='spectrum-alpha-inner'>",
    56         "<div class='spectrum-alpha-handle spectrum-slider-control'></div>",
    57       "</div>",
    58     "</div>",
    59   ].join("");
    61   this.onElementClick = this.onElementClick.bind(this);
    62   this.element.addEventListener("click", this.onElementClick, false);
    64   this.parentEl.appendChild(this.element);
    66   this.slider = this.element.querySelector(".spectrum-hue");
    67   this.slideHelper = this.element.querySelector(".spectrum-slider");
    68   Spectrum.draggable(this.slider, this.onSliderMove.bind(this));
    70   this.dragger = this.element.querySelector(".spectrum-color");
    71   this.dragHelper = this.element.querySelector(".spectrum-dragger");
    72   Spectrum.draggable(this.dragger, this.onDraggerMove.bind(this));
    74   this.alphaSlider = this.element.querySelector(".spectrum-alpha");
    75   this.alphaSliderInner = this.element.querySelector(".spectrum-alpha-inner");
    76   this.alphaSliderHelper = this.element.querySelector(".spectrum-alpha-handle");
    77   Spectrum.draggable(this.alphaSliderInner, this.onAlphaSliderMove.bind(this));
    79   if (rgb) {
    80     this.rgb = rgb;
    81     this.updateUI();
    82   }
    83 }
    85 module.exports.Spectrum = Spectrum;
    87 Spectrum.hsvToRgb = function(h, s, v, a) {
    88   let r, g, b;
    90   let i = Math.floor(h * 6);
    91   let f = h * 6 - i;
    92   let p = v * (1 - s);
    93   let q = v * (1 - f * s);
    94   let t = v * (1 - (1 - f) * s);
    96   switch(i % 6) {
    97     case 0: r = v, g = t, b = p; break;
    98     case 1: r = q, g = v, b = p; break;
    99     case 2: r = p, g = v, b = t; break;
   100     case 3: r = p, g = q, b = v; break;
   101     case 4: r = t, g = p, b = v; break;
   102     case 5: r = v, g = p, b = q; break;
   103   }
   105   return [r * 255, g * 255, b * 255, a];
   106 };
   108 Spectrum.rgbToHsv = function(r, g, b, a) {
   109   r = r / 255;
   110   g = g / 255;
   111   b = b / 255;
   113   let max = Math.max(r, g, b), min = Math.min(r, g, b);
   114   let h, s, v = max;
   116   let d = max - min;
   117   s = max == 0 ? 0 : d / max;
   119   if(max == min) {
   120     h = 0; // achromatic
   121   }
   122   else {
   123     switch(max) {
   124       case r: h = (g - b) / d + (g < b ? 6 : 0); break;
   125       case g: h = (b - r) / d + 2; break;
   126       case b: h = (r - g) / d + 4; break;
   127     }
   128     h /= 6;
   129   }
   130   return [h, s, v, a];
   131 };
   133 Spectrum.getOffset = function(el) {
   134   let curleft = 0, curtop = 0;
   135   if (el.offsetParent) {
   136     while (el) {
   137       curleft += el.offsetLeft;
   138       curtop += el.offsetTop;
   139       el = el.offsetParent;
   140     }
   141   }
   142   return {
   143     left: curleft,
   144     top: curtop
   145   };
   146 };
   148 Spectrum.draggable = function(element, onmove, onstart, onstop) {
   149   onmove = onmove || function() {};
   150   onstart = onstart || function() {};
   151   onstop = onstop || function() {};
   153   let doc = element.ownerDocument;
   154   let dragging = false;
   155   let offset = {};
   156   let maxHeight = 0;
   157   let maxWidth = 0;
   159   function prevent(e) {
   160     e.stopPropagation();
   161     e.preventDefault();
   162   }
   164   function move(e) {
   165     if (dragging) {
   166       let pageX = e.pageX;
   167       let pageY = e.pageY;
   169       let dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));
   170       let dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));
   172       onmove.apply(element, [dragX, dragY]);
   173     }
   174   }
   176   function start(e) {
   177     let rightclick = e.which === 3;
   179     if (!rightclick && !dragging) {
   180       if (onstart.apply(element, arguments) !== false) {
   181         dragging = true;
   182         maxHeight = element.offsetHeight;
   183         maxWidth = element.offsetWidth;
   185         offset = Spectrum.getOffset(element);
   187         move(e);
   189         doc.addEventListener("selectstart", prevent, false);
   190         doc.addEventListener("dragstart", prevent, false);
   191         doc.addEventListener("mousemove", move, false);
   192         doc.addEventListener("mouseup", stop, false);
   194         prevent(e);
   195       }
   196     }
   197   }
   199   function stop() {
   200     if (dragging) {
   201       doc.removeEventListener("selectstart", prevent, false);
   202       doc.removeEventListener("dragstart", prevent, false);
   203       doc.removeEventListener("mousemove", move, false);
   204       doc.removeEventListener("mouseup", stop, false);
   205       onstop.apply(element, arguments);
   206     }
   207     dragging = false;
   208   }
   210   element.addEventListener("mousedown", start, false);
   211 };
   213 Spectrum.prototype = {
   214   set rgb(color) {
   215     this.hsv = Spectrum.rgbToHsv(color[0], color[1], color[2], color[3]);
   216   },
   218   get rgb() {
   219     let rgb = Spectrum.hsvToRgb(this.hsv[0], this.hsv[1], this.hsv[2], this.hsv[3]);
   220     return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2]), Math.round(rgb[3]*100)/100];
   221   },
   223   get rgbNoSatVal() {
   224     let rgb = Spectrum.hsvToRgb(this.hsv[0], 1, 1);
   225     return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2]), rgb[3]];
   226   },
   228   get rgbCssString() {
   229     let rgb = this.rgb;
   230     return "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " + rgb[3] + ")";
   231   },
   233   show: function() {
   234     this.element.classList.add('spectrum-show');
   236     this.slideHeight = this.slider.offsetHeight;
   237     this.dragWidth = this.dragger.offsetWidth;
   238     this.dragHeight = this.dragger.offsetHeight;
   239     this.dragHelperHeight = this.dragHelper.offsetHeight;
   240     this.slideHelperHeight = this.slideHelper.offsetHeight;
   241     this.alphaSliderWidth = this.alphaSliderInner.offsetWidth;
   242     this.alphaSliderHelperWidth = this.alphaSliderHelper.offsetWidth;
   244     this.updateUI();
   245   },
   247   onElementClick: function(e) {
   248     e.stopPropagation();
   249   },
   251   onSliderMove: function(dragX, dragY) {
   252     this.hsv[0] = (dragY / this.slideHeight);
   253     this.updateUI();
   254     this.onChange();
   255   },
   257   onDraggerMove: function(dragX, dragY) {
   258     this.hsv[1] = dragX / this.dragWidth;
   259     this.hsv[2] = (this.dragHeight - dragY) / this.dragHeight;
   260     this.updateUI();
   261     this.onChange();
   262   },
   264   onAlphaSliderMove: function(dragX, dragY) {
   265     this.hsv[3] = dragX / this.alphaSliderWidth;
   266     this.updateUI();
   267     this.onChange();
   268   },
   270   onChange: function() {
   271     this.emit("changed", this.rgb, this.rgbCssString);
   272   },
   274   updateHelperLocations: function() {
   275     // If the UI hasn't been shown yet then none of the dimensions will be correct
   276     if (!this.element.classList.contains('spectrum-show'))
   277       return;
   279     let h = this.hsv[0];
   280     let s = this.hsv[1];
   281     let v = this.hsv[2];
   283     // Placing the color dragger
   284     let dragX = s * this.dragWidth;
   285     let dragY = this.dragHeight - (v * this.dragHeight);
   286     let helperDim = this.dragHelperHeight/2;
   288     dragX = Math.max(
   289       -helperDim,
   290       Math.min(this.dragWidth - helperDim, dragX - helperDim)
   291     );
   292     dragY = Math.max(
   293       -helperDim,
   294       Math.min(this.dragHeight - helperDim, dragY - helperDim)
   295     );
   297     this.dragHelper.style.top = dragY + "px";
   298     this.dragHelper.style.left = dragX + "px";
   300     // Placing the hue slider
   301     let slideY = (h * this.slideHeight) - this.slideHelperHeight/2;
   302     this.slideHelper.style.top = slideY + "px";
   304     // Placing the alpha slider
   305     let alphaSliderX = (this.hsv[3] * this.alphaSliderWidth) - (this.alphaSliderHelperWidth / 2);
   306     this.alphaSliderHelper.style.left = alphaSliderX + "px";
   307   },
   309   updateUI: function() {
   310     this.updateHelperLocations();
   312     let rgb = this.rgb;
   313     let rgbNoSatVal = this.rgbNoSatVal;
   315     let flatColor = "rgb(" + rgbNoSatVal[0] + ", " + rgbNoSatVal[1] + ", " + rgbNoSatVal[2] + ")";
   316     let fullColor = "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " + rgb[3] + ")";
   318     this.dragger.style.backgroundColor = flatColor;
   320     var rgbNoAlpha = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
   321     var rgbAlpha0 = "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ", 0)";
   322     var alphaGradient = "linear-gradient(to right, " + rgbAlpha0 + ", " + rgbNoAlpha + ")";
   323     this.alphaSliderInner.style.background = alphaGradient;
   324   },
   326   destroy: function() {
   327     this.element.removeEventListener("click", this.onElementClick, false);
   329     this.parentEl.removeChild(this.element);
   331     this.slider = null;
   332     this.dragger = null;
   333     this.alphaSlider = this.alphaSliderInner = this.alphaSliderHelper = null;
   334     this.parentEl = null;
   335     this.element = null;
   336   }
   337 };

mercurial