Home | History | Annotate | Download | only in front_end
      1 /*
      2  * Copyright (C) 2011 Brian Grinstead All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 /**
     30  * @constructor
     31  * @extends {WebInspector.View}
     32  */
     33 WebInspector.Spectrum = function()
     34 {
     35     WebInspector.View.call(this);
     36     this.registerRequiredCSS("spectrum.css");
     37 
     38     this.element.className = "spectrum-container";
     39     this.element.tabIndex = 0;
     40 
     41     var topElement = this.element.createChild("div", "spectrum-top");
     42     topElement.createChild("div", "spectrum-fill");
     43 
     44     var topInnerElement = topElement.createChild("div", "spectrum-top-inner fill");
     45     this._draggerElement = topInnerElement.createChild("div", "spectrum-color");
     46     this._dragHelperElement = this._draggerElement.createChild("div", "spectrum-sat fill").createChild("div", "spectrum-val fill").createChild("div", "spectrum-dragger");
     47 
     48     this._sliderElement = topInnerElement.createChild("div", "spectrum-hue");
     49     this.slideHelper = this._sliderElement.createChild("div", "spectrum-slider");
     50 
     51     var rangeContainer = this.element.createChild("div", "spectrum-range-container");
     52     var alphaLabel = rangeContainer.createChild("label");
     53     alphaLabel.textContent = WebInspector.UIString("\u03B1:");
     54 
     55     this._alphaElement = rangeContainer.createChild("input", "spectrum-range");
     56     this._alphaElement.setAttribute("type", "range");
     57     this._alphaElement.setAttribute("min", "0");
     58     this._alphaElement.setAttribute("max", "100");
     59     this._alphaElement.addEventListener("change", alphaDrag.bind(this), false);
     60 
     61     var swatchElement = document.createElement("span");
     62     swatchElement.className = "swatch";
     63     this._swatchInnerElement = swatchElement.createChild("span", "swatch-inner");
     64 
     65     var displayContainer = this.element.createChild("div");
     66     displayContainer.appendChild(swatchElement);
     67     this._displayElement = displayContainer.createChild("span", "source-code spectrum-display-value");
     68 
     69     WebInspector.Spectrum.draggable(this._sliderElement, hueDrag.bind(this));
     70     WebInspector.Spectrum.draggable(this._draggerElement, colorDrag.bind(this), colorDragStart.bind(this));
     71 
     72     function hueDrag(element, dragX, dragY)
     73     {
     74         this._hsv[0] = (this.slideHeight - dragY) / this.slideHeight;
     75 
     76         this._onchange();
     77     }
     78 
     79     var initialHelperOffset;
     80 
     81     function colorDragStart(element, dragX, dragY)
     82     {
     83         initialHelperOffset = { x: this._dragHelperElement.offsetLeft, y: this._dragHelperElement.offsetTop };
     84     }
     85 
     86     function colorDrag(element, dragX, dragY, event)
     87     {
     88         if (event.shiftKey) {
     89             if (Math.abs(dragX - initialHelperOffset.x) >= Math.abs(dragY - initialHelperOffset.y))
     90                 dragY = initialHelperOffset.y;
     91             else
     92                 dragX = initialHelperOffset.x;
     93         }
     94 
     95         this._hsv[1] = dragX / this.dragWidth;
     96         this._hsv[2] = (this.dragHeight - dragY) / this.dragHeight;
     97 
     98         this._onchange();
     99     }
    100 
    101     function alphaDrag()
    102     {
    103         this._hsv[3] = this._alphaElement.value / 100;
    104 
    105         this._onchange();
    106     }
    107 };
    108 
    109 WebInspector.Spectrum.Events = {
    110     ColorChanged: "ColorChanged"
    111 };
    112 
    113 /**
    114  * @param {Function=} onmove
    115  * @param {Function=} onstart
    116  * @param {Function=} onstop
    117  */
    118 WebInspector.Spectrum.draggable = function(element, onmove, onstart, onstop) {
    119 
    120     var doc = document;
    121     var dragging;
    122     var offset;
    123     var scrollOffset;
    124     var maxHeight;
    125     var maxWidth;
    126 
    127     function consume(e)
    128     {
    129         e.consume(true);
    130     }
    131 
    132     function move(e)
    133     {
    134         if (dragging) {
    135             var dragX = Math.max(0, Math.min(e.pageX - offset.left + scrollOffset.left, maxWidth));
    136             var dragY = Math.max(0, Math.min(e.pageY - offset.top + scrollOffset.top, maxHeight));
    137 
    138             if (onmove)
    139                 onmove(element, dragX, dragY, e);
    140         }
    141     }
    142 
    143     function start(e)
    144     {
    145         var rightClick = e.which ? (e.which === 3) : (e.button === 2);
    146 
    147         if (!rightClick && !dragging) {
    148 
    149             if (onstart)
    150                 onstart(element, e)
    151 
    152             dragging = true;
    153             maxHeight = element.clientHeight;
    154             maxWidth = element.clientWidth;
    155 
    156             scrollOffset = element.scrollOffset();
    157             offset = element.totalOffset();
    158 
    159             doc.addEventListener("selectstart", consume, false);
    160             doc.addEventListener("dragstart", consume, false);
    161             doc.addEventListener("mousemove", move, false);
    162             doc.addEventListener("mouseup", stop, false);
    163 
    164             move(e);
    165             consume(e);
    166         }
    167     }
    168 
    169     function stop(e)
    170     {
    171         if (dragging) {
    172             doc.removeEventListener("selectstart", consume, false);
    173             doc.removeEventListener("dragstart", consume, false);
    174             doc.removeEventListener("mousemove", move, false);
    175             doc.removeEventListener("mouseup", stop, false);
    176 
    177             if (onstop)
    178                 onstop(element, e);
    179         }
    180 
    181         dragging = false;
    182     }
    183 
    184     element.addEventListener("mousedown", start, false);
    185 };
    186 
    187 WebInspector.Spectrum.prototype = {
    188     /**
    189      * @param {WebInspector.Color} color
    190      */
    191     setColor: function(color)
    192     {
    193         this._hsv = color.hsva();
    194     },
    195 
    196     /**
    197      * @return {WebInspector.Color}
    198      */
    199     color: function()
    200     {
    201         return WebInspector.Color.fromHSVA(this._hsv);
    202     },
    203 
    204     _colorString: function()
    205     {
    206         var cf = WebInspector.Color.Format;
    207         var format = this._originalFormat;
    208         var color = this.color();
    209         var originalFormatString = color.toString(this._originalFormat);
    210         if (originalFormatString)
    211             return originalFormatString;
    212 
    213         if (color.hasAlpha()) {
    214             // Everything except HSL(A) should be returned as RGBA if transparency is involved.
    215             if (format === cf.HSLA || format === cf.HSL)
    216                 return color.toString(cf.HSLA);
    217             else
    218                 return color.toString(cf.RGBA);
    219         }
    220 
    221         if (format === cf.ShortHEX)
    222             return color.toString(cf.HEX);
    223         console.assert(format === cf.Nickname);
    224         return color.toString(cf.RGB);
    225     },
    226 
    227 
    228     set displayText(text)
    229     {
    230         this._displayElement.textContent = text;
    231     },
    232 
    233     _onchange: function()
    234     {
    235         this._updateUI();
    236         this.dispatchEventToListeners(WebInspector.Spectrum.Events.ColorChanged, this._colorString());
    237     },
    238 
    239     _updateHelperLocations: function()
    240     {
    241         var h = this._hsv[0];
    242         var s = this._hsv[1];
    243         var v = this._hsv[2];
    244 
    245         // Where to show the little circle that displays your current selected color.
    246         var dragX = s * this.dragWidth;
    247         var dragY = this.dragHeight - (v * this.dragHeight);
    248 
    249         dragX = Math.max(-this._dragHelperElementHeight,
    250                         Math.min(this.dragWidth - this._dragHelperElementHeight, dragX - this._dragHelperElementHeight));
    251         dragY = Math.max(-this._dragHelperElementHeight,
    252                         Math.min(this.dragHeight - this._dragHelperElementHeight, dragY - this._dragHelperElementHeight));
    253 
    254         this._dragHelperElement.positionAt(dragX, dragY);
    255 
    256         // Where to show the bar that displays your current selected hue.
    257         var slideY = this.slideHeight - ((h * this.slideHeight) + this.slideHelperHeight);
    258         this.slideHelper.style.top = slideY + "px";
    259 
    260         this._alphaElement.value = this._hsv[3] * 100;
    261     },
    262 
    263     _updateUI: function()
    264     {
    265         this._updateHelperLocations();
    266 
    267         this._draggerElement.style.backgroundColor = WebInspector.Color.fromHSVA([this._hsv[0], 1, 1, 1]).toString(WebInspector.Color.Format.RGB);
    268         this._swatchInnerElement.style.backgroundColor = this.color().toString(WebInspector.Color.Format.RGBA);
    269 
    270         this._alphaElement.value = this._hsv[3] * 100;
    271     },
    272 
    273     wasShown: function()
    274     {
    275         this.slideHeight = this._sliderElement.offsetHeight;
    276         this.dragWidth = this._draggerElement.offsetWidth;
    277         this.dragHeight = this._draggerElement.offsetHeight;
    278         this._dragHelperElementHeight = this._dragHelperElement.offsetHeight / 2;
    279         this.slideHelperHeight = this.slideHelper.offsetHeight / 2;
    280         this._updateUI();
    281     },
    282 
    283     __proto__: WebInspector.View.prototype
    284 }
    285 
    286 /**
    287  * @constructor
    288  * @extends {WebInspector.Object}
    289  */
    290 WebInspector.SpectrumPopupHelper = function()
    291 {
    292     this._spectrum = new WebInspector.Spectrum();
    293     this._spectrum.element.addEventListener("keydown", this._onKeyDown.bind(this), false);
    294 
    295     this._popover = new WebInspector.Popover();
    296     this._popover.setCanShrink(false);
    297     this._popover.element.addEventListener("mousedown", consumeEvent, false);
    298 
    299     this._hideProxy = this.hide.bind(this, true);
    300 }
    301 
    302 WebInspector.SpectrumPopupHelper.Events = {
    303     Hidden: "Hidden"
    304 };
    305 
    306 WebInspector.SpectrumPopupHelper.prototype = {
    307     /**
    308      * @return {WebInspector.Spectrum}
    309      */
    310     spectrum: function()
    311     {
    312         return this._spectrum;
    313     },
    314 
    315     toggle: function(element, color, format)
    316     {
    317         if (this._popover.isShowing())
    318             this.hide(true);
    319         else
    320             this.show(element, color, format);
    321 
    322         return this._popover.isShowing();
    323     },
    324 
    325     show: function(element, color, format)
    326     {
    327         if (this._popover.isShowing()) {
    328             if (this._anchorElement === element)
    329                 return false;
    330 
    331             // Reopen the picker for another anchor element.
    332             this.hide(true);
    333         }
    334 
    335         this._anchorElement = element;
    336 
    337         this._spectrum.setColor(color);
    338         this._spectrum._originalFormat = format !== WebInspector.Color.Format.Original ? format : color.format();
    339         this.reposition(element);
    340 
    341         document.addEventListener("mousedown", this._hideProxy, false);
    342         window.addEventListener("blur", this._hideProxy, false);
    343         return true;
    344     },
    345 
    346     reposition: function(element)
    347     {
    348         if (!this._previousFocusElement)
    349             this._previousFocusElement = WebInspector.currentFocusElement();
    350         this._popover.showView(this._spectrum, element);
    351         WebInspector.setCurrentFocusElement(this._spectrum.element);
    352     },
    353 
    354     /**
    355      * @param {boolean=} commitEdit
    356      */
    357     hide: function(commitEdit)
    358     {
    359         if (!this._popover.isShowing())
    360             return;
    361         this._popover.hide();
    362 
    363         document.removeEventListener("mousedown", this._hideProxy, false);
    364         window.removeEventListener("blur", this._hideProxy, false);
    365 
    366         this.dispatchEventToListeners(WebInspector.SpectrumPopupHelper.Events.Hidden, !!commitEdit);
    367 
    368         WebInspector.setCurrentFocusElement(this._previousFocusElement);
    369         delete this._previousFocusElement;
    370 
    371         delete this._anchorElement;
    372     },
    373 
    374     _onKeyDown: function(event)
    375     {
    376         if (event.keyIdentifier === "Enter") {
    377             this.hide(true);
    378             event.consume(true);
    379             return;
    380         }
    381         if (event.keyIdentifier === "U+001B") { // Escape key
    382             this.hide(false);
    383             event.consume(true);
    384         }
    385     },
    386 
    387     __proto__: WebInspector.Object.prototype
    388 }
    389 
    390 /**
    391  * @constructor
    392  */
    393 WebInspector.ColorSwatch = function()
    394 {
    395     this.element = document.createElement("span");
    396     this._swatchInnerElement = this.element.createChild("span", "swatch-inner");
    397     this.element.title = WebInspector.UIString("Click to open a colorpicker. Shift-click to change color format");
    398     this.element.className = "swatch";
    399     this.element.addEventListener("mousedown", consumeEvent, false);
    400     this.element.addEventListener("dblclick", consumeEvent, false);
    401 }
    402 
    403 WebInspector.ColorSwatch.prototype = {
    404     /**
    405      * @param {string} colorString
    406      */
    407     setColorString: function(colorString)
    408     {
    409         this._swatchInnerElement.style.backgroundColor = colorString;
    410     }
    411 }
    412